From f9baca549e44791be0dd98de15add3d8452a8af0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 24 Aug 2021 17:59:52 +0100 Subject: [PATCH 001/493] Update version for v6.1.0 release Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 241f2cb536..dfda3e0b4f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.94 +6.1.0 From d42685765653ec155fdf60910662f8830bdb2cef Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 25 Aug 2021 10:25:12 +0100 Subject: [PATCH 002/493] Open 6.2 development tree Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index dfda3e0b4f..0ad6cf7fe6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.1.0 +6.1.50 From 3ce105c5bf6598676b0dcb2688d9a77097fc8c15 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Mon, 23 Aug 2021 11:00:03 +0800 Subject: [PATCH 003/493] docs/about: Remove the duplicated doc There are two places describing the same thing about deprecation of invalid topologies of -smp CLI, so remove the duplicated one. Signed-off-by: Yanan Wang Reviewed-by: Cornelia Huck Reviewed-by: Andrew Jones Reviewed-by: Thomas Huth Message-Id: <20210823030005.165668-2-wangyanan55@huawei.com> Signed-off-by: Thomas Huth --- docs/about/removed-features.rst | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index cbfa1a8e31..6a9c5bb484 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -647,19 +647,6 @@ as ignored. Currently, users are responsible for making sure the backing storage specified with ``-mem-path`` can actually provide the guest RAM configured with ``-m`` and QEMU fails to start up if RAM allocation is unsuccessful. -``-smp`` (invalid topologies) (removed 5.2) -''''''''''''''''''''''''''''''''''''''''''' - -CPU topology properties should describe whole machine topology including -possible CPUs. - -However, historically it was possible to start QEMU with an incorrect topology -where *n* <= *sockets* * *cores* * *threads* < *maxcpus*, -which could lead to an incorrect topology enumeration by the guest. -Support for invalid topologies is removed, the user must ensure -topologies described with -smp include all possible cpus, i.e. -*sockets* * *cores* * *threads* = *maxcpus*. - ``-machine enforce-config-section=on|off`` (removed 5.2) '''''''''''''''''''''''''''''''''''''''''''''''''''''''' From e2cc363bbbbc5d1b798f9a81d4fd31f3bfb8e9ab Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Mon, 23 Aug 2021 11:00:04 +0800 Subject: [PATCH 004/493] docs/about: Unify the subject format There is a mixture of "since/removed in X.Y" vs "since/removed in X.Y.Z" in the subjects in deprecated.rst/removed-features.rst. It will be better to use an unified format. It seems unlikely that we will ever deprecate something in a stable release, and even more unlikely that we'll remove something in one, so the short versions look like the thing we want to standardize on. So here we unify the subject format in deprecated.rst to "since X.Y", and unify the subject format in removed-features.rst to "removed in X.Y". Signed-off-by: Yanan Wang Reviewed-by: Cornelia Huck Reviewed-by: Andrew Jones Reviewed-by: Thomas Huth Message-Id: <20210823030005.165668-3-wangyanan55@huawei.com> Signed-off-by: Thomas Huth --- docs/about/deprecated.rst | 56 ++++++++++++++++----------------- docs/about/removed-features.rst | 28 ++++++++--------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 6d438f1c8d..8d4fd384a5 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -107,8 +107,8 @@ the process listing. This is replaced by the new ``password-secret`` option which lets the password be securely provided on the command line using a ``secret`` object instance. -``opened`` property of ``rng-*`` objects (since 6.0.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' +``opened`` property of ``rng-*`` objects (since 6.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''' The only effect of specifying ``opened=on`` in the command line or QMP ``object-add`` is that the device is opened immediately, possibly before all @@ -116,8 +116,8 @@ other options have been processed. This will either have no effect (if ``opened`` was the last option) or cause errors. The property is therefore useless and should not be specified. -``loaded`` property of ``secret`` and ``secret_keyring`` objects (since 6.0.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``loaded`` property of ``secret`` and ``secret_keyring`` objects (since 6.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' The only effect of specifying ``loaded=on`` in the command line or QMP ``object-add`` is that the secret is loaded immediately, possibly before all @@ -142,33 +142,33 @@ should be used instead. QEMU Machine Protocol (QMP) commands ------------------------------------ -``blockdev-open-tray``, ``blockdev-close-tray`` argument ``device`` (since 2.8.0) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``blockdev-open-tray``, ``blockdev-close-tray`` argument ``device`` (since 2.8) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Use argument ``id`` instead. -``eject`` argument ``device`` (since 2.8.0) -''''''''''''''''''''''''''''''''''''''''''' +``eject`` argument ``device`` (since 2.8) +''''''''''''''''''''''''''''''''''''''''' Use argument ``id`` instead. -``blockdev-change-medium`` argument ``device`` (since 2.8.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``blockdev-change-medium`` argument ``device`` (since 2.8) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Use argument ``id`` instead. -``block_set_io_throttle`` argument ``device`` (since 2.8.0) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``block_set_io_throttle`` argument ``device`` (since 2.8) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Use argument ``id`` instead. -``blockdev-add`` empty string argument ``backing`` (since 2.10.0) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``blockdev-add`` empty string argument ``backing`` (since 2.10) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Use argument value ``null`` instead. -``block-commit`` arguments ``base`` and ``top`` (since 3.1.0) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``block-commit`` arguments ``base`` and ``top`` (since 3.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Use arguments ``base-node`` and ``top-node`` instead. @@ -191,8 +191,8 @@ from Linux upstream kernel, declare it deprecated. System emulator CPUS -------------------- -``Icelake-Client`` CPU Model (since 5.2.0) -'''''''''''''''''''''''''''''''''''''''''' +``Icelake-Client`` CPU Model (since 5.2) +'''''''''''''''''''''''''''''''''''''''' ``Icelake-Client`` CPU Models are deprecated. Use ``Icelake-Server`` CPU Models instead. @@ -245,8 +245,8 @@ Device options Emulated device options ''''''''''''''''''''''' -``-device virtio-blk,scsi=on|off`` (since 5.0.0) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``-device virtio-blk,scsi=on|off`` (since 5.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The virtio-blk SCSI passthrough feature is a legacy VIRTIO feature. VIRTIO 1.0 and later do not support it because the virtio-scsi device was introduced for @@ -258,14 +258,14 @@ alias. Block device options '''''''''''''''''''' -``"backing": ""`` (since 2.12.0) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``"backing": ""`` (since 2.12) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In order to prevent QEMU from automatically opening an image's backing chain, use ``"backing": null`` instead. -``rbd`` keyvalue pair encoded filenames: ``""`` (since 3.1.0) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``rbd`` keyvalue pair encoded filenames: ``""`` (since 3.1) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Options for ``rbd`` should be specified according to its runtime options, like other block drivers. Legacy parsing of keyvalue pair encoded @@ -283,8 +283,8 @@ The above, converted to the current supported format:: linux-user mode CPUs -------------------- -``ppc64abi32`` CPUs (since 5.2.0) -''''''''''''''''''''''''''''''''' +``ppc64abi32`` CPUs (since 5.2) +''''''''''''''''''''''''''''''' The ``ppc64abi32`` architecture has a number of issues which regularly trip up our CI testing and is suspected to be quite broken. For that @@ -303,8 +303,8 @@ Related binaries Backwards compatibility ----------------------- -Runnability guarantee of CPU models (since 4.1.0) -''''''''''''''''''''''''''''''''''''''''''''''''' +Runnability guarantee of CPU models (since 4.1) +''''''''''''''''''''''''''''''''''''''''''''''' Previous versions of QEMU never changed existing CPU models in ways that introduced additional host software or hardware diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 6a9c5bb484..1c926a8bc1 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -194,8 +194,8 @@ by the ``tls-authz`` and ``sasl-authz`` options. The ``pretty=on|off`` switch has no effect for HMP monitors and its use is rejected. -``-drive file=json:{...{'driver':'file'}}`` (removed 6.0) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``-drive file=json:{...{'driver':'file'}}`` (removed in 6.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' The 'file' driver for drives is no longer appropriate for character or host devices and will only accept regular files (S_IFREG). The correct driver @@ -272,8 +272,8 @@ for the RISC-V ``virt`` machine and ``sifive_u`` machine. QEMU Machine Protocol (QMP) commands ------------------------------------ -``block-dirty-bitmap-add`` "autoload" parameter (removed in 4.2.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``block-dirty-bitmap-add`` "autoload" parameter (removed in 4.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' The "autoload" parameter has been ignored since 2.12.0. All bitmaps are automatically loaded from qcow2 images. @@ -456,15 +456,15 @@ Nobody was using this CPU emulation in QEMU, and there were no test images available to make sure that the code is still working, so it has been removed without replacement. -``lm32`` CPUs (removed in 6.1.0) -'''''''''''''''''''''''''''''''' +``lm32`` CPUs (removed in 6.1) +'''''''''''''''''''''''''''''' The only public user of this architecture was the milkymist project, which has been dead for years; there was never an upstream Linux port. Removed without replacement. -``unicore32`` CPUs (since 6.1.0) -'''''''''''''''''''''''''''''''' +``unicore32`` CPUs (removed in 6.1) +''''''''''''''''''''''''''''''''''' Support for this CPU was removed from the upstream Linux kernel, and there is no available upstream toolchain to build binaries for it. @@ -593,8 +593,8 @@ error when ``-u`` is not used. Command line options -------------------- -``-smp`` (invalid topologies) (removed 5.2) -''''''''''''''''''''''''''''''''''''''''''' +``-smp`` (invalid topologies) (removed in 5.2) +'''''''''''''''''''''''''''''''''''''''''''''' CPU topology properties should describe whole machine topology including possible CPUs. @@ -606,8 +606,8 @@ Support for invalid topologies is removed, the user must ensure topologies described with -smp include all possible cpus, i.e. *sockets* * *cores* * *threads* = *maxcpus*. -``-numa`` node (without memory specified) (removed 5.2) -''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``-numa`` node (without memory specified) (removed in 5.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Splitting RAM by default between NUMA nodes had the same issues as ``mem`` parameter with the difference that the role of the user plays QEMU using @@ -647,8 +647,8 @@ as ignored. Currently, users are responsible for making sure the backing storage specified with ``-mem-path`` can actually provide the guest RAM configured with ``-m`` and QEMU fails to start up if RAM allocation is unsuccessful. -``-machine enforce-config-section=on|off`` (removed 5.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``-machine enforce-config-section=on|off`` (removed in 5.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' The ``enforce-config-section`` property was replaced by the ``-global migration.send-configuration={on|off}`` option. From 7f697d7b6829189fbecea61c80838008146a7001 Mon Sep 17 00:00:00 2001 From: Yanan Wang Date: Mon, 23 Aug 2021 11:00:05 +0800 Subject: [PATCH 005/493] docs/about: Add the missing release record in the subject Commit 29e0447551 (docs/about/removed-features: Document removed CLI options from QEMU v3.1) has recorded some CLI options as replaced/removed from QEMU v3.1, but one of the subjects has missed the release record. Let's fix it. Reported-by: Cornelia Huck Signed-off-by: Yanan Wang Reviewed-by: Andrew Jones Reviewed-by: Thomas Huth Message-Id: <20210823030005.165668-4-wangyanan55@huawei.com> Signed-off-by: Thomas Huth --- docs/about/removed-features.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 1c926a8bc1..8feeead449 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -140,8 +140,8 @@ Use ``-rtc driftfix=slew`` instead. Replaced by ``-rtc base=date``. -``-vnc ...,tls=...``, ``-vnc ...,x509=...`` & ``-vnc ...,x509verify=...`` -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``-vnc ...,tls=...``, ``-vnc ...,x509=...`` & ``-vnc ...,x509verify=...`` (removed in 3.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' The "tls-creds" option should be used instead to point to a "tls-creds-x509" object created using "-object". From 9dacf0764b506a67f33dd63bdf48fc273c0bdb7f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:46 +0100 Subject: [PATCH 006/493] target/arm: Note that we handle VMOVL as a special case of VSHLL Although the architecture doesn't define it as an alias, VMOVL (vector move long) is encoded as a VSHLL with a zero shift. Add a comment in the decode file noting that we handle VMOVL as part of VSHLL. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve.decode | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 595d97568e..fa9d921f93 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -364,6 +364,8 @@ VRSHRI_U 111 1 1111 1 . ... ... ... 0 0010 0 1 . 1 ... 0 @2_shr_h VRSHRI_U 111 1 1111 1 . ... ... ... 0 0010 0 1 . 1 ... 0 @2_shr_w # VSHLL T1 encoding; the T2 VSHLL encoding is elsewhere in this file +# Note that VMOVL is encoded as "VSHLL with a zero shift count"; we +# implement it that way rather than special-casing it in the decode. VSHLL_BS 111 0 1110 1 . 1 .. ... ... 0 1111 0 1 . 0 ... 0 @2_shll_b VSHLL_BS 111 0 1110 1 . 1 .. ... ... 0 1111 0 1 . 0 ... 0 @2_shll_h From aa29190826f2f061ed3ffad0a6cabb30eaf7f8f0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:47 +0100 Subject: [PATCH 007/493] target/arm: Print MVE VPR in CPU dumps Include the MVE VPR register value in the CPU dumps produced by arm_cpu_dump_state() if we are printing FPU information. This makes it easier to interpret debug logs when predication is active. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/cpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 2866dd7658..a82e39dd97 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1017,6 +1017,9 @@ static void arm_cpu_dump_state(CPUState *cs, FILE *f, int flags) i, v); } qemu_fprintf(f, "FPSCR: %08x\n", vfp_get_fpscr(env)); + if (cpu_isar_feature(aa32_mve, cpu)) { + qemu_fprintf(f, "VPR: %08x\n", env->v7m.vpr); + } } } From c88ff88498ea95e78d5fbd192de5123c1d88f9a8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:47 +0100 Subject: [PATCH 008/493] target/arm: Fix MVE VSLI by 0 and VSRI by
In the MVE shift-and-insert insns, we special case VSLI by 0 and VSRI by
. VSRI by
means "don't update the destination", which is what we've implemented. However VSLI by 0 is "set destination to the input", so we don't want to use the same special-casing that we do for VSRI by
. Since the generic logic gives the right answer for a shift by 0, just use that. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve_helper.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index db5d622085..f14fa914b6 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1279,11 +1279,12 @@ DO_2SHIFT_S(vrshli_s, DO_VRSHLS) uint16_t mask; \ uint64_t shiftmask; \ unsigned e; \ - if (shift == 0 || shift == ESIZE * 8) { \ + if (shift == ESIZE * 8) { \ /* \ - * Only VSLI can shift by 0; only VSRI can shift by
. \ - * The generic logic would give the right answer for 0 but \ - * fails for
. \ + * Only VSRI can shift by
; it should mean "don't \ + * update the destination". The generic logic can't handle \ + * this because it would try to shift by an out-of-range \ + * amount, so special case it here. \ */ \ goto done; \ } \ From ed5a59d61f1619f4015a7a02f72e3590528008b4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:47 +0100 Subject: [PATCH 009/493] target/arm: Fix signed VADDV A cut-and-paste error meant we handled signed VADDV like unsigned VADDV; fix the type used. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index f14fa914b6..82151b0620 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1182,9 +1182,9 @@ DO_LDAVH(vrmlsldavhxsw, int32_t, int64_t, true, true) return ra; \ } \ -DO_VADDV(vaddvsb, 1, uint8_t) -DO_VADDV(vaddvsh, 2, uint16_t) -DO_VADDV(vaddvsw, 4, uint32_t) +DO_VADDV(vaddvsb, 1, int8_t) +DO_VADDV(vaddvsh, 2, int16_t) +DO_VADDV(vaddvsw, 4, int32_t) DO_VADDV(vaddvub, 1, uint8_t) DO_VADDV(vaddvuh, 2, uint16_t) DO_VADDV(vaddvuw, 4, uint32_t) From a5e59e8dcbbf7b1205370b3f4519749df5d0b726 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:47 +0100 Subject: [PATCH 010/493] target/arm: Fix mask handling for MVE narrowing operations In the MVE helpers for the narrowing operations (DO_VSHRN and DO_VSHRN_SAT) we were using the wrong bits of the predicate mask for the 'top' versions of the insn. This is because the loop works over the double-sized input elements and shifts the predicate mask by that many bits each time, but when we write out the half-sized output we must look at the mask bits for whichever half of the element we are writing to. Correct this by shifting the whole mask right by ESIZE bits for the 'top' insns. This allows us also to simplify the saturation bit checking (where we had noticed that we needed to look at a different mask bit for the 'top' insn.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve_helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 82151b0620..847ef5156a 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1358,6 +1358,7 @@ DO_VSHLL_ALL(vshllt, true) TYPE *d = vd; \ uint16_t mask = mve_element_mask(env); \ unsigned le; \ + mask >>= ESIZE * TOP; \ for (le = 0; le < 16 / LESIZE; le++, mask >>= LESIZE) { \ TYPE r = FN(m[H##LESIZE(le)], shift); \ mergemask(&d[H##ESIZE(le * 2 + TOP)], r, mask); \ @@ -1419,11 +1420,12 @@ static inline int32_t do_sat_bhs(int64_t val, int64_t min, int64_t max, uint16_t mask = mve_element_mask(env); \ bool qc = false; \ unsigned le; \ + mask >>= ESIZE * TOP; \ for (le = 0; le < 16 / LESIZE; le++, mask >>= LESIZE) { \ bool sat = false; \ TYPE r = FN(m[H##LESIZE(le)], shift, &sat); \ mergemask(&d[H##ESIZE(le * 2 + TOP)], r, mask); \ - qc |= sat && (mask & 1 << (TOP * ESIZE)); \ + qc |= sat & mask & 1; \ } \ if (qc) { \ env->vfp.qc[0] = qc; \ From 95351aa76c8f68564c4be547c1d19d9cabffc147 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:48 +0100 Subject: [PATCH 011/493] target/arm: Fix 48-bit saturating shifts In do_sqrshl48_d() and do_uqrshl48_d() we got some of the edge cases wrong and failed to saturate correctly: (1) In do_sqrshl48_d() we used the same code that do_shrshl_bhs() does to obtain the saturated most-negative and most-positive 48-bit signed values for the large-shift-left case. This gives (1 << 47) for saturate-to-most-negative, but we weren't sign-extending this value to the 64-bit output as the pseudocode requires. (2) For left shifts by less than 48, we copied the "8/16 bit" code from do_sqrshl_bhs() and do_uqrshl_bhs(). This doesn't do the right thing because it assumes the C type we're working with is at least twice the number of bits we're saturating to (so that a shift left by bits-1 can't shift anything off the top of the value). This isn't true for bits == 48, so we would incorrectly return 0 rather than the most-positive value for situations like "shift (1 << 44) right by 20". Instead check for saturation by doing the shift and signextend and then testing whether shifting back left again gives the original value. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve_helper.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 847ef5156a..5730b48f35 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1576,9 +1576,8 @@ static inline int64_t do_sqrshl48_d(int64_t src, int64_t shift, } return src >> -shift; } else if (shift < 48) { - int64_t val = src << shift; - int64_t extval = sextract64(val, 0, 48); - if (!sat || val == extval) { + int64_t extval = sextract64(src << shift, 0, 48); + if (!sat || src == (extval >> shift)) { return extval; } } else if (!sat || src == 0) { @@ -1586,7 +1585,7 @@ static inline int64_t do_sqrshl48_d(int64_t src, int64_t shift, } *sat = 1; - return (1ULL << 47) - (src >= 0); + return src >= 0 ? MAKE_64BIT_MASK(0, 47) : MAKE_64BIT_MASK(47, 17); } /* Operate on 64-bit values, but saturate at 48 bits */ @@ -1609,9 +1608,8 @@ static inline uint64_t do_uqrshl48_d(uint64_t src, int64_t shift, return extval; } } else if (shift < 48) { - uint64_t val = src << shift; - uint64_t extval = extract64(val, 0, 48); - if (!sat || val == extval) { + uint64_t extval = extract64(src << shift, 0, 48); + if (!sat || src == (extval >> shift)) { return extval; } } else if (!sat || src == 0) { From fdcf2269c4e0e4f5ca3a389290a71d7aa98bd5c7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:48 +0100 Subject: [PATCH 012/493] target/arm: Fix MVE 48-bit SQRSHRL for small right shifts We got an edge case wrong in the 48-bit SQRSHRL implementation: if the shift is to the right, although it always makes the result smaller than the input value it might not be within the 48-bit range the result is supposed to be if the input had some bits in [63..48] set and the shift didn't bring all of those within the [47..0] range. Handle this similarly to the way we already do for this case in do_uqrshl48_d(): extend the calculated result from 48 bits, and return that if not saturating or if it doesn't change the result; otherwise fall through to return a saturated value. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve_helper.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 5730b48f35..1a4b2ef807 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1563,6 +1563,8 @@ uint64_t HELPER(mve_uqrshll)(CPUARMState *env, uint64_t n, uint32_t shift) static inline int64_t do_sqrshl48_d(int64_t src, int64_t shift, bool round, uint32_t *sat) { + int64_t val, extval; + if (shift <= -48) { /* Rounding the sign bit always produces 0. */ if (round) { @@ -1572,9 +1574,14 @@ static inline int64_t do_sqrshl48_d(int64_t src, int64_t shift, } else if (shift < 0) { if (round) { src >>= -shift - 1; - return (src >> 1) + (src & 1); + val = (src >> 1) + (src & 1); + } else { + val = src >> -shift; + } + extval = sextract64(val, 0, 48); + if (!sat || val == extval) { + return extval; } - return src >> -shift; } else if (shift < 48) { int64_t extval = sextract64(src << shift, 0, 48); if (!sat || src == (extval >> shift)) { From 3f4f1880c245453b75d4c09049845b19de9964bf Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:48 +0100 Subject: [PATCH 013/493] target/arm: Fix calculation of LTP mask when LR is 0 In mve_element_mask(), we calculate a mask for tail predication which should have a number of 1 bits based on the value of LR. However, our MAKE_64BIT_MASK() macro has undefined behaviour when passed a zero length. Special case this to give the all-zeroes mask we require. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve_helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 1a4b2ef807..bc67b86e70 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -64,7 +64,8 @@ static uint16_t mve_element_mask(CPUARMState *env) */ int masklen = env->regs[14] << env->v7m.ltpsize; assert(masklen <= 16); - mask &= MAKE_64BIT_MASK(0, masklen); + uint16_t ltpmask = masklen ? MAKE_64BIT_MASK(0, masklen) : 0; + mask &= ltpmask; } if ((env->condexec_bits & 0xf) == 0) { From e0d40070e1b3b6cf16ad2a51e85fb92261363d2a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:49 +0100 Subject: [PATCH 014/493] target/arm: Factor out mve_eci_mask() In some situations we need a mask telling us which parts of the vector correspond to beats that are not being executed because of ECI, separately from the combined "which bytes are predicated away" mask. Factor this mask calculation out of mve_element_mask() into its own function. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve_helper.c | 58 ++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index bc67b86e70..ffff280726 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -26,6 +26,35 @@ #include "exec/exec-all.h" #include "tcg/tcg.h" +static uint16_t mve_eci_mask(CPUARMState *env) +{ + /* + * Return the mask of which elements in the MVE vector correspond + * to beats being executed. The mask has 1 bits for executed lanes + * and 0 bits where ECI says this beat was already executed. + */ + int eci; + + if ((env->condexec_bits & 0xf) != 0) { + return 0xffff; + } + + eci = env->condexec_bits >> 4; + switch (eci) { + case ECI_NONE: + return 0xffff; + case ECI_A0: + return 0xfff0; + case ECI_A0A1: + return 0xff00; + case ECI_A0A1A2: + case ECI_A0A1A2B0: + return 0xf000; + default: + g_assert_not_reached(); + } +} + static uint16_t mve_element_mask(CPUARMState *env) { /* @@ -68,30 +97,11 @@ static uint16_t mve_element_mask(CPUARMState *env) mask &= ltpmask; } - if ((env->condexec_bits & 0xf) == 0) { - /* - * ECI bits indicate which beats are already executed; - * we handle this by effectively predicating them out. - */ - int eci = env->condexec_bits >> 4; - switch (eci) { - case ECI_NONE: - break; - case ECI_A0: - mask &= 0xfff0; - break; - case ECI_A0A1: - mask &= 0xff00; - break; - case ECI_A0A1A2: - case ECI_A0A1A2B0: - mask &= 0xf000; - break; - default: - g_assert_not_reached(); - } - } - + /* + * ECI bits indicate which beats are already executed; + * we handle this by effectively predicating them out. + */ + mask &= mve_eci_mask(env); return mask; } From e3152d02da21ac6e2169b1bf104a2d0478664a4a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:49 +0100 Subject: [PATCH 015/493] target/arm: Fix VPT advance when ECI is non-zero We were not paying attention to the ECI state when advancing the VPT state. Architecturally, VPT state advance happens for every beat (see the pseudocode VPTAdvance()), so on every beat the 4 bits of VPR.P0 corresponding to the current beat are inverted if required, and at the end of beats 1 and 3 the VPR MASK fields are updated. This means that if the ECI state says we should not be executing all 4 beats then we need to skip some of the updating of the VPR that we currently do in mve_advance_vpt(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve_helper.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index ffff280726..bc89ce94d5 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -110,6 +110,8 @@ static void mve_advance_vpt(CPUARMState *env) /* Advance the VPT and ECI state if necessary */ uint32_t vpr = env->v7m.vpr; unsigned mask01, mask23; + uint16_t inv_mask; + uint16_t eci_mask = mve_eci_mask(env); if ((env->condexec_bits & 0xf) == 0) { env->condexec_bits = (env->condexec_bits == (ECI_A0A1A2B0 << 4)) ? @@ -121,17 +123,25 @@ static void mve_advance_vpt(CPUARMState *env) return; } + /* Invert P0 bits if needed, but only for beats we actually executed */ mask01 = FIELD_EX32(vpr, V7M_VPR, MASK01); mask23 = FIELD_EX32(vpr, V7M_VPR, MASK23); - if (mask01 > 8) { - /* high bit set, but not 0b1000: invert the relevant half of P0 */ - vpr ^= 0xff; + /* Start by assuming we invert all bits corresponding to executed beats */ + inv_mask = eci_mask; + if (mask01 <= 8) { + /* MASK01 says don't invert low half of P0 */ + inv_mask &= ~0xff; } - if (mask23 > 8) { - /* high bit set, but not 0b1000: invert the relevant half of P0 */ - vpr ^= 0xff00; + if (mask23 <= 8) { + /* MASK23 says don't invert high half of P0 */ + inv_mask &= ~0xff00; } - vpr = FIELD_DP32(vpr, V7M_VPR, MASK01, mask01 << 1); + vpr ^= inv_mask; + /* Only update MASK01 if beat 1 executed */ + if (eci_mask & 0xf0) { + vpr = FIELD_DP32(vpr, V7M_VPR, MASK01, mask01 << 1); + } + /* Beat 3 always executes, so update MASK23 */ vpr = FIELD_DP32(vpr, V7M_VPR, MASK23, mask23 << 1); env->v7m.vpr = vpr; } From 41704cc262d6f451470c2074560bc7309064865d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:49 +0100 Subject: [PATCH 016/493] target/arm: Fix VLDRB/H/W for predicated elements For vector loads, predicated elements are zeroed, instead of retaining their previous values (as happens for most data processing operations). This means we need to distinguish "beat not executed due to ECI" (don't touch destination element) from "beat executed but predicated out" (zero destination element). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve_helper.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index bc89ce94d5..be8b954531 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -146,12 +146,13 @@ static void mve_advance_vpt(CPUARMState *env) env->v7m.vpr = vpr; } - +/* For loads, predicated lanes are zeroed instead of keeping their old values */ #define DO_VLDR(OP, MSIZE, LDTYPE, ESIZE, TYPE) \ void HELPER(mve_##OP)(CPUARMState *env, void *vd, uint32_t addr) \ { \ TYPE *d = vd; \ uint16_t mask = mve_element_mask(env); \ + uint16_t eci_mask = mve_eci_mask(env); \ unsigned b, e; \ /* \ * R_SXTM allows the dest reg to become UNKNOWN for abandoned \ @@ -159,8 +160,9 @@ static void mve_advance_vpt(CPUARMState *env) * then take an exception. \ */ \ for (b = 0, e = 0; b < 16; b += ESIZE, e++) { \ - if (mask & (1 << b)) { \ - d[H##ESIZE(e)] = cpu_##LDTYPE##_data_ra(env, addr, GETPC()); \ + if (eci_mask & (1 << b)) { \ + d[H##ESIZE(e)] = (mask & (1 << b)) ? \ + cpu_##LDTYPE##_data_ra(env, addr, GETPC()) : 0; \ } \ addr += MSIZE; \ } \ From c1bd78cb06afb37e4043d2b0db000abfecab5fe4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:50 +0100 Subject: [PATCH 017/493] target/arm: Implement MVE VMULL (polynomial) Implement the MVE VMULL (polynomial) insn. Unlike Neon, this comes in two flavours: 8x8->16 and a 16x16->32. Also unlike Neon, the inputs are in either the low or the high half of each double-width element. The assembler for this insn indicates the size with "P8" or "P16", encoded into bit 28 as size = 0 or 1. We choose to follow the same encoding as VQDMULL and decode this into a->size as MO_16 or MO_32 indicating the size of the result elements. This then carries through to the helper function names where it then matches up with the existing pmull_h() which does an 8x8->16 operation and a new pmull_w() which does the 16x16->32. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 5 +++++ target/arm/mve.decode | 14 ++++++++++---- target/arm/mve_helper.c | 16 ++++++++++++++++ target/arm/translate-mve.c | 28 ++++++++++++++++++++++++++++ target/arm/vec_helper.c | 14 +++++++++++++- target/arm/vec_internal.h | 11 +++++++++++ 6 files changed, 83 insertions(+), 5 deletions(-) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 56e40844ad..84adfb2151 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -145,6 +145,11 @@ DEF_HELPER_FLAGS_4(mve_vmulltub, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vmulltuh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vmulltuw, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) +DEF_HELPER_FLAGS_4(mve_vmullpbh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) +DEF_HELPER_FLAGS_4(mve_vmullpth, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) +DEF_HELPER_FLAGS_4(mve_vmullpbw, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) +DEF_HELPER_FLAGS_4(mve_vmullptw, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) + DEF_HELPER_FLAGS_4(mve_vqdmulhb, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vqdmulhh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vqdmulhw, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index fa9d921f93..de079ec517 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -173,10 +173,16 @@ VHADD_U 111 1 1111 0 . .. ... 0 ... 0 0000 . 1 . 0 ... 0 @2op VHSUB_S 111 0 1111 0 . .. ... 0 ... 0 0010 . 1 . 0 ... 0 @2op VHSUB_U 111 1 1111 0 . .. ... 0 ... 0 0010 . 1 . 0 ... 0 @2op -VMULL_BS 111 0 1110 0 . .. ... 1 ... 0 1110 . 0 . 0 ... 0 @2op -VMULL_BU 111 1 1110 0 . .. ... 1 ... 0 1110 . 0 . 0 ... 0 @2op -VMULL_TS 111 0 1110 0 . .. ... 1 ... 1 1110 . 0 . 0 ... 0 @2op -VMULL_TU 111 1 1110 0 . .. ... 1 ... 1 1110 . 0 . 0 ... 0 @2op +{ + VMULLP_B 111 . 1110 0 . 11 ... 1 ... 0 1110 . 0 . 0 ... 0 @2op_sz28 + VMULL_BS 111 0 1110 0 . .. ... 1 ... 0 1110 . 0 . 0 ... 0 @2op + VMULL_BU 111 1 1110 0 . .. ... 1 ... 0 1110 . 0 . 0 ... 0 @2op +} +{ + VMULLP_T 111 . 1110 0 . 11 ... 1 ... 1 1110 . 0 . 0 ... 0 @2op_sz28 + VMULL_TS 111 0 1110 0 . .. ... 1 ... 1 1110 . 0 . 0 ... 0 @2op + VMULL_TU 111 1 1110 0 . .. ... 1 ... 1 1110 . 0 . 0 ... 0 @2op +} VQDMULH 1110 1111 0 . .. ... 0 ... 0 1011 . 1 . 0 ... 0 @2op VQRDMULH 1111 1111 0 . .. ... 0 ... 0 1011 . 1 . 0 ... 0 @2op diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index be8b954531..91fb346d7e 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -481,6 +481,22 @@ DO_2OP_L(vmulltub, 1, 1, uint8_t, 2, uint16_t, DO_MUL) DO_2OP_L(vmulltuh, 1, 2, uint16_t, 4, uint32_t, DO_MUL) DO_2OP_L(vmulltuw, 1, 4, uint32_t, 8, uint64_t, DO_MUL) +/* + * Polynomial multiply. We can always do this generating 64 bits + * of the result at a time, so we don't need to use DO_2OP_L. + */ +#define VMULLPH_MASK 0x00ff00ff00ff00ffULL +#define VMULLPW_MASK 0x0000ffff0000ffffULL +#define DO_VMULLPBH(N, M) pmull_h((N) & VMULLPH_MASK, (M) & VMULLPH_MASK) +#define DO_VMULLPTH(N, M) DO_VMULLPBH((N) >> 8, (M) >> 8) +#define DO_VMULLPBW(N, M) pmull_w((N) & VMULLPW_MASK, (M) & VMULLPW_MASK) +#define DO_VMULLPTW(N, M) DO_VMULLPBW((N) >> 16, (M) >> 16) + +DO_2OP(vmullpbh, 8, uint64_t, DO_VMULLPBH) +DO_2OP(vmullpth, 8, uint64_t, DO_VMULLPTH) +DO_2OP(vmullpbw, 8, uint64_t, DO_VMULLPBW) +DO_2OP(vmullptw, 8, uint64_t, DO_VMULLPTW) + /* * Because the computation type is at least twice as large as required, * these work for both signed and unsigned source types. diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index a2a45036a0..d318f34b2b 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -464,6 +464,34 @@ static bool trans_VQDMULLT(DisasContext *s, arg_2op *a) return do_2op(s, a, fns[a->size]); } +static bool trans_VMULLP_B(DisasContext *s, arg_2op *a) +{ + /* + * Note that a->size indicates the output size, ie VMULL.P8 + * is the 8x8->16 operation and a->size is MO_16; VMULL.P16 + * is the 16x16->32 operation and a->size is MO_32. + */ + static MVEGenTwoOpFn * const fns[] = { + NULL, + gen_helper_mve_vmullpbh, + gen_helper_mve_vmullpbw, + NULL, + }; + return do_2op(s, a, fns[a->size]); +} + +static bool trans_VMULLP_T(DisasContext *s, arg_2op *a) +{ + /* a->size is as for trans_VMULLP_B */ + static MVEGenTwoOpFn * const fns[] = { + NULL, + gen_helper_mve_vmullpth, + gen_helper_mve_vmullptw, + NULL, + }; + return do_2op(s, a, fns[a->size]); +} + /* * VADC and VSBC: these perform an add-with-carry or subtract-with-carry * of the 32-bit elements in each lane of the input vectors, where the diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index 034f6b84f7..17fb158362 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -2028,11 +2028,23 @@ static uint64_t expand_byte_to_half(uint64_t x) | ((x & 0xff000000) << 24); } -static uint64_t pmull_h(uint64_t op1, uint64_t op2) +uint64_t pmull_w(uint64_t op1, uint64_t op2) { uint64_t result = 0; int i; + for (i = 0; i < 16; ++i) { + uint64_t mask = (op1 & 0x0000000100000001ull) * 0xffffffff; + result ^= op2 & mask; + op1 >>= 1; + op2 <<= 1; + } + return result; +} +uint64_t pmull_h(uint64_t op1, uint64_t op2) +{ + uint64_t result = 0; + int i; for (i = 0; i < 8; ++i) { uint64_t mask = (op1 & 0x0001000100010001ull) * 0xffff; result ^= op2 & mask; diff --git a/target/arm/vec_internal.h b/target/arm/vec_internal.h index 865d213944..2a33558290 100644 --- a/target/arm/vec_internal.h +++ b/target/arm/vec_internal.h @@ -206,4 +206,15 @@ int16_t do_sqrdmlah_h(int16_t, int16_t, int16_t, bool, bool, uint32_t *); int32_t do_sqrdmlah_s(int32_t, int32_t, int32_t, bool, bool, uint32_t *); int64_t do_sqrdmlah_d(int64_t, int64_t, int64_t, bool, bool); +/* + * 8 x 8 -> 16 vector polynomial multiply where the inputs are + * in the low 8 bits of each 16-bit element +*/ +uint64_t pmull_h(uint64_t op1, uint64_t op2); +/* + * 16 x 16 -> 32 vector polynomial multiply where the inputs are + * in the low 16 bits of each 32-bit element + */ +uint64_t pmull_w(uint64_t op1, uint64_t op2); + #endif /* TARGET_ARM_VEC_INTERNALS_H */ From 395b92d50ee2b62b662d5524a61c532a2752336c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:50 +0100 Subject: [PATCH 018/493] target/arm: Implement MVE incrementing/decrementing dup insns Implement the MVE incrementing/decrementing dup insns VIDUP, VDDUP, VIWDUP and VDWDUP. These fill the elements of a vector with successively incrementing values, starting at the offset specified in a general purpose register. The final value of the offset is written back to this register. The wrapping variants take a second general purpose register which specifies the point where the count should wrap back to 0. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 12 ++++ target/arm/mve.decode | 25 ++++++++ target/arm/mve_helper.c | 63 +++++++++++++++++++ target/arm/translate-mve.c | 120 +++++++++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 84adfb2151..b9af03cc03 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -35,6 +35,18 @@ DEF_HELPER_FLAGS_3(mve_vstrh_w, TCG_CALL_NO_WG, void, env, ptr, i32) DEF_HELPER_FLAGS_3(mve_vdup, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vidupb, TCG_CALL_NO_WG, i32, env, ptr, i32, i32) +DEF_HELPER_FLAGS_4(mve_viduph, TCG_CALL_NO_WG, i32, env, ptr, i32, i32) +DEF_HELPER_FLAGS_4(mve_vidupw, TCG_CALL_NO_WG, i32, env, ptr, i32, i32) + +DEF_HELPER_FLAGS_5(mve_viwdupb, TCG_CALL_NO_WG, i32, env, ptr, i32, i32, i32) +DEF_HELPER_FLAGS_5(mve_viwduph, TCG_CALL_NO_WG, i32, env, ptr, i32, i32, i32) +DEF_HELPER_FLAGS_5(mve_viwdupw, TCG_CALL_NO_WG, i32, env, ptr, i32, i32, i32) + +DEF_HELPER_FLAGS_5(mve_vdwdupb, TCG_CALL_NO_WG, i32, env, ptr, i32, i32, i32) +DEF_HELPER_FLAGS_5(mve_vdwduph, TCG_CALL_NO_WG, i32, env, ptr, i32, i32, i32) +DEF_HELPER_FLAGS_5(mve_vdwdupw, TCG_CALL_NO_WG, i32, env, ptr, i32, i32, i32) + DEF_HELPER_FLAGS_3(mve_vclsb, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vclsh, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vclsw, TCG_CALL_NO_WG, void, env, ptr, ptr) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index de079ec517..88c9c18ebf 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -35,6 +35,8 @@ &2scalar qd qn rm size &1imm qd imm cmode op &2shift qd qm shift size +&vidup qd rn size imm +&viwdup qd rn rm size imm @vldr_vstr ....... . . . . l:1 rn:4 ... ...... imm:7 &vldr_vstr qd=%qd u=0 # Note that both Rn and Qd are 3 bits only (no D bit) @@ -259,6 +261,29 @@ VDUP 1110 1110 1 1 10 ... 0 .... 1011 . 0 0 1 0000 @vdup size=0 VDUP 1110 1110 1 0 10 ... 0 .... 1011 . 0 1 1 0000 @vdup size=1 VDUP 1110 1110 1 0 10 ... 0 .... 1011 . 0 0 1 0000 @vdup size=2 +# Incrementing and decrementing dup + +# VIDUP, VDDUP format immediate: 1 << (immh:imml) +%imm_vidup 7:1 0:1 !function=vidup_imm + +# VIDUP, VDDUP registers: Rm bits [3:1] from insn, bit 0 is 1; +# Rn bits [3:1] from insn, bit 0 is 0 +%vidup_rm 1:3 !function=times_2_plus_1 +%vidup_rn 17:3 !function=times_2 + +@vidup .... .... . . size:2 .... .... .... .... .... \ + qd=%qd imm=%imm_vidup rn=%vidup_rn &vidup +@viwdup .... .... . . size:2 .... .... .... .... .... \ + qd=%qd imm=%imm_vidup rm=%vidup_rm rn=%vidup_rn &viwdup +{ + VIDUP 1110 1110 0 . .. ... 1 ... 0 1111 . 110 111 . @vidup + VIWDUP 1110 1110 0 . .. ... 1 ... 0 1111 . 110 ... . @viwdup +} +{ + VDDUP 1110 1110 0 . .. ... 1 ... 1 1111 . 110 111 . @vidup + VDWDUP 1110 1110 0 . .. ... 1 ... 1 1111 . 110 ... . @viwdup +} + # multiply-add long dual accumulate # rdahi: bits [3:1] from insn, bit 0 is 1 # rdalo: bits [3:1] from insn, bit 0 is 0 diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 91fb346d7e..38b4181db2 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1695,3 +1695,66 @@ uint32_t HELPER(mve_sqrshr)(CPUARMState *env, uint32_t n, uint32_t shift) { return do_sqrshl_bhs(n, -(int8_t)shift, 32, true, &env->QF); } + +#define DO_VIDUP(OP, ESIZE, TYPE, FN) \ + uint32_t HELPER(mve_##OP)(CPUARMState *env, void *vd, \ + uint32_t offset, uint32_t imm) \ + { \ + TYPE *d = vd; \ + uint16_t mask = mve_element_mask(env); \ + unsigned e; \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ + mergemask(&d[H##ESIZE(e)], offset, mask); \ + offset = FN(offset, imm); \ + } \ + mve_advance_vpt(env); \ + return offset; \ + } + +#define DO_VIWDUP(OP, ESIZE, TYPE, FN) \ + uint32_t HELPER(mve_##OP)(CPUARMState *env, void *vd, \ + uint32_t offset, uint32_t wrap, \ + uint32_t imm) \ + { \ + TYPE *d = vd; \ + uint16_t mask = mve_element_mask(env); \ + unsigned e; \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ + mergemask(&d[H##ESIZE(e)], offset, mask); \ + offset = FN(offset, wrap, imm); \ + } \ + mve_advance_vpt(env); \ + return offset; \ + } + +#define DO_VIDUP_ALL(OP, FN) \ + DO_VIDUP(OP##b, 1, int8_t, FN) \ + DO_VIDUP(OP##h, 2, int16_t, FN) \ + DO_VIDUP(OP##w, 4, int32_t, FN) + +#define DO_VIWDUP_ALL(OP, FN) \ + DO_VIWDUP(OP##b, 1, int8_t, FN) \ + DO_VIWDUP(OP##h, 2, int16_t, FN) \ + DO_VIWDUP(OP##w, 4, int32_t, FN) + +static uint32_t do_add_wrap(uint32_t offset, uint32_t wrap, uint32_t imm) +{ + offset += imm; + if (offset == wrap) { + offset = 0; + } + return offset; +} + +static uint32_t do_sub_wrap(uint32_t offset, uint32_t wrap, uint32_t imm) +{ + if (offset == 0) { + offset = wrap; + } + offset -= imm; + return offset; +} + +DO_VIDUP_ALL(vidup, DO_ADD) +DO_VIWDUP_ALL(viwdup, do_add_wrap) +DO_VIWDUP_ALL(vdwdup, do_sub_wrap) diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index d318f34b2b..a220521c00 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -25,6 +25,11 @@ #include "translate.h" #include "translate-a32.h" +static inline int vidup_imm(DisasContext *s, int x) +{ + return 1 << x; +} + /* Include the generated decoder */ #include "decode-mve.c.inc" @@ -36,6 +41,8 @@ typedef void MVEGenTwoOpShiftFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void MVEGenDualAccOpFn(TCGv_i64, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i64); typedef void MVEGenVADDVFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void MVEGenOneOpImmFn(TCGv_ptr, TCGv_ptr, TCGv_i64); +typedef void MVEGenVIDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32); +typedef void MVEGenVIWDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32); /* Return the offset of a Qn register (same semantics as aa32_vfp_qreg()) */ static inline long mve_qreg_offset(unsigned reg) @@ -1059,3 +1066,116 @@ static bool trans_VSHLC(DisasContext *s, arg_VSHLC *a) mve_update_eci(s); return true; } + +static bool do_vidup(DisasContext *s, arg_vidup *a, MVEGenVIDUPFn *fn) +{ + TCGv_ptr qd; + TCGv_i32 rn; + + /* + * Vector increment/decrement with wrap and duplicate (VIDUP, VDDUP). + * This fills the vector with elements of successively increasing + * or decreasing values, starting from Rn. + */ + if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qd)) { + return false; + } + if (a->size == MO_64) { + /* size 0b11 is another encoding */ + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + qd = mve_qreg_ptr(a->qd); + rn = load_reg(s, a->rn); + fn(rn, cpu_env, qd, rn, tcg_constant_i32(a->imm)); + store_reg(s, a->rn, rn); + tcg_temp_free_ptr(qd); + mve_update_eci(s); + return true; +} + +static bool do_viwdup(DisasContext *s, arg_viwdup *a, MVEGenVIWDUPFn *fn) +{ + TCGv_ptr qd; + TCGv_i32 rn, rm; + + /* + * Vector increment/decrement with wrap and duplicate (VIWDUp, VDWDUP) + * This fills the vector with elements of successively increasing + * or decreasing values, starting from Rn. Rm specifies a point where + * the count wraps back around to 0. The updated offset is written back + * to Rn. + */ + if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qd)) { + return false; + } + if (!fn || a->rm == 13 || a->rm == 15) { + /* + * size 0b11 is another encoding; Rm == 13 is UNPREDICTABLE; + * Rm == 13 is VIWDUP, VDWDUP. + */ + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + qd = mve_qreg_ptr(a->qd); + rn = load_reg(s, a->rn); + rm = load_reg(s, a->rm); + fn(rn, cpu_env, qd, rn, rm, tcg_constant_i32(a->imm)); + store_reg(s, a->rn, rn); + tcg_temp_free_ptr(qd); + tcg_temp_free_i32(rm); + mve_update_eci(s); + return true; +} + +static bool trans_VIDUP(DisasContext *s, arg_vidup *a) +{ + static MVEGenVIDUPFn * const fns[] = { + gen_helper_mve_vidupb, + gen_helper_mve_viduph, + gen_helper_mve_vidupw, + NULL, + }; + return do_vidup(s, a, fns[a->size]); +} + +static bool trans_VDDUP(DisasContext *s, arg_vidup *a) +{ + static MVEGenVIDUPFn * const fns[] = { + gen_helper_mve_vidupb, + gen_helper_mve_viduph, + gen_helper_mve_vidupw, + NULL, + }; + /* VDDUP is just like VIDUP but with a negative immediate */ + a->imm = -a->imm; + return do_vidup(s, a, fns[a->size]); +} + +static bool trans_VIWDUP(DisasContext *s, arg_viwdup *a) +{ + static MVEGenVIWDUPFn * const fns[] = { + gen_helper_mve_viwdupb, + gen_helper_mve_viwduph, + gen_helper_mve_viwdupw, + NULL, + }; + return do_viwdup(s, a, fns[a->size]); +} + +static bool trans_VDWDUP(DisasContext *s, arg_viwdup *a) +{ + static MVEGenVIWDUPFn * const fns[] = { + gen_helper_mve_vdwdupb, + gen_helper_mve_vdwduph, + gen_helper_mve_vdwdupw, + NULL, + }; + return do_viwdup(s, a, fns[a->size]); +} From 552517861c6553941e8bfbbafbf97b6a6d992636 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:50 +0100 Subject: [PATCH 019/493] target/arm: Factor out gen_vpst() Factor out the "generate code to update VPR.MASK01/MASK23" part of trans_VPST(); we are going to want to reuse it for the VPT insns. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/translate-mve.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index a220521c00..6d8da36146 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -737,33 +737,24 @@ static bool trans_VRMLSLDAVH(DisasContext *s, arg_vmlaldav *a) return do_long_dual_acc(s, a, fns[a->x]); } -static bool trans_VPST(DisasContext *s, arg_VPST *a) +static void gen_vpst(DisasContext *s, uint32_t mask) { - TCGv_i32 vpr; - - /* mask == 0 is a "related encoding" */ - if (!dc_isar_feature(aa32_mve, s) || !a->mask) { - return false; - } - if (!mve_eci_check(s) || !vfp_access_check(s)) { - return true; - } /* * Set the VPR mask fields. We take advantage of MASK01 and MASK23 * being adjacent fields in the register. * - * This insn is not predicated, but it is subject to beat-wise + * Updating the masks is not predicated, but it is subject to beat-wise * execution, and the mask is updated on the odd-numbered beats. * So if PSR.ECI says we should skip beat 1, we mustn't update the * 01 mask field. */ - vpr = load_cpu_field(v7m.vpr); + TCGv_i32 vpr = load_cpu_field(v7m.vpr); switch (s->eci) { case ECI_NONE: case ECI_A0: /* Update both 01 and 23 fields */ tcg_gen_deposit_i32(vpr, vpr, - tcg_constant_i32(a->mask | (a->mask << 4)), + tcg_constant_i32(mask | (mask << 4)), R_V7M_VPR_MASK01_SHIFT, R_V7M_VPR_MASK01_LENGTH + R_V7M_VPR_MASK23_LENGTH); break; @@ -772,13 +763,25 @@ static bool trans_VPST(DisasContext *s, arg_VPST *a) case ECI_A0A1A2B0: /* Update only the 23 mask field */ tcg_gen_deposit_i32(vpr, vpr, - tcg_constant_i32(a->mask), + tcg_constant_i32(mask), R_V7M_VPR_MASK23_SHIFT, R_V7M_VPR_MASK23_LENGTH); break; default: g_assert_not_reached(); } store_cpu_field(vpr, v7m.vpr); +} + +static bool trans_VPST(DisasContext *s, arg_VPST *a) +{ + /* mask == 0 is a "related encoding" */ + if (!dc_isar_feature(aa32_mve, s) || !a->mask) { + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + gen_vpst(s, a->mask); mve_update_and_store_eci(s); return true; } From eff5d9a9bdbabfb1ccdb62c1c61311a575b11e9c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:51 +0100 Subject: [PATCH 020/493] target/arm: Implement MVE integer vector comparisons Implement the MVE integer vector comparison instructions. These are "VCMP (vector)" encodings T1, T2 and T3, and "VPT (vector)" encodings T1, T2 and T3. These insns compare corresponding elements in each vector, and update the VPR.P0 predicate bits with the results of the comparison. VPT also sets the VPR.MASK01 and VPR.MASK23 fields -- it is effectively "VCMP then VPST". Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 32 ++++++++++++++++++++++ target/arm/mve.decode | 18 +++++++++++- target/arm/mve_helper.c | 56 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-mve.c | 47 ++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 1 deletion(-) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index b9af03cc03..ca5a6ab51c 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -480,3 +480,35 @@ DEF_HELPER_FLAGS_3(mve_uqshl, TCG_CALL_NO_RWG, i32, env, i32, i32) DEF_HELPER_FLAGS_3(mve_sqshl, TCG_CALL_NO_RWG, i32, env, i32, i32) DEF_HELPER_FLAGS_3(mve_uqrshl, TCG_CALL_NO_RWG, i32, env, i32, i32) DEF_HELPER_FLAGS_3(mve_sqrshr, TCG_CALL_NO_RWG, i32, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vcmpeqb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpeqh, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpeqw, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vcmpneb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpneh, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpnew, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vcmpcsb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpcsh, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpcsw, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vcmphib, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmphih, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmphiw, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vcmpgeb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpgeh, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpgew, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vcmpltb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmplth, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpltw, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vcmpgtb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpgth, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpgtw, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vcmpleb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmpleh, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vcmplew, TCG_CALL_NO_WG, void, env, ptr, ptr) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 88c9c18ebf..76bbf9a613 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -37,6 +37,7 @@ &2shift qd qm shift size &vidup qd rn size imm &viwdup qd rn rm size imm +&vcmp qm qn size mask @vldr_vstr ....... . . . . l:1 rn:4 ... ...... imm:7 &vldr_vstr qd=%qd u=0 # Note that both Rn and Qd are 3 bits only (no D bit) @@ -86,6 +87,10 @@ @2_shr_w .... .... .. 1 ..... .... .... .... .... &2shift qd=%qd qm=%qm \ size=2 shift=%rshift_i5 +# Vector comparison; 4-bit Qm but 3-bit Qn +%mask_22_13 22:1 13:3 +@vcmp .... .... .. size:2 qn:3 . .... .... .... .... &vcmp qm=%qm mask=%mask_22_13 + # Vector loads and stores # Widening loads and narrowing stores: @@ -345,7 +350,6 @@ VQRDMULH_scalar 1111 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar } # Predicate operations -%mask_22_13 22:1 13:3 VPST 1111 1110 0 . 11 000 1 ... 0 1111 0100 1101 mask=%mask_22_13 # Logical immediate operations (1 reg and modified-immediate) @@ -458,3 +462,15 @@ VQRSHRUNT 111 1 1110 1 . ... ... ... 1 1111 1 1 . 0 ... 0 @2_shr_b VQRSHRUNT 111 1 1110 1 . ... ... ... 1 1111 1 1 . 0 ... 0 @2_shr_h VSHLC 111 0 1110 1 . 1 imm:5 ... 0 1111 1100 rdm:4 qd=%qd + +# Comparisons. We expand out the conditions which are split across +# encodings T1, T2, T3 and the fc bits. These include VPT, which is +# effectively "VCMP then VPST". A plain "VCMP" has a mask field of zero. +VCMPEQ 1111 1110 0 . .. ... 1 ... 0 1111 0 0 . 0 ... 0 @vcmp +VCMPNE 1111 1110 0 . .. ... 1 ... 0 1111 1 0 . 0 ... 0 @vcmp +VCMPCS 1111 1110 0 . .. ... 1 ... 0 1111 0 0 . 0 ... 1 @vcmp +VCMPHI 1111 1110 0 . .. ... 1 ... 0 1111 1 0 . 0 ... 1 @vcmp +VCMPGE 1111 1110 0 . .. ... 1 ... 1 1111 0 0 . 0 ... 0 @vcmp +VCMPLT 1111 1110 0 . .. ... 1 ... 1 1111 1 0 . 0 ... 0 @vcmp +VCMPGT 1111 1110 0 . .. ... 1 ... 1 1111 0 0 . 0 ... 1 @vcmp +VCMPLE 1111 1110 0 . .. ... 1 ... 1 1111 1 0 . 0 ... 1 @vcmp diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 38b4181db2..b0b380b94b 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1758,3 +1758,59 @@ static uint32_t do_sub_wrap(uint32_t offset, uint32_t wrap, uint32_t imm) DO_VIDUP_ALL(vidup, DO_ADD) DO_VIWDUP_ALL(viwdup, do_add_wrap) DO_VIWDUP_ALL(vdwdup, do_sub_wrap) + +/* + * Vector comparison. + * P0 bits for non-executed beats (where eci_mask is 0) are unchanged. + * P0 bits for predicated lanes in executed beats (where mask is 0) are 0. + * P0 bits otherwise are updated with the results of the comparisons. + * We must also keep unchanged the MASK fields at the top of v7m.vpr. + */ +#define DO_VCMP(OP, ESIZE, TYPE, FN) \ + void HELPER(glue(mve_, OP))(CPUARMState *env, void *vn, void *vm) \ + { \ + TYPE *n = vn, *m = vm; \ + uint16_t mask = mve_element_mask(env); \ + uint16_t eci_mask = mve_eci_mask(env); \ + uint16_t beatpred = 0; \ + uint16_t emask = MAKE_64BIT_MASK(0, ESIZE); \ + unsigned e; \ + for (e = 0; e < 16 / ESIZE; e++) { \ + bool r = FN(n[H##ESIZE(e)], m[H##ESIZE(e)]); \ + /* Comparison sets 0/1 bits for each byte in the element */ \ + beatpred |= r * emask; \ + emask <<= ESIZE; \ + } \ + beatpred &= mask; \ + env->v7m.vpr = (env->v7m.vpr & ~(uint32_t)eci_mask) | \ + (beatpred & eci_mask); \ + mve_advance_vpt(env); \ + } + +#define DO_VCMP_S(OP, FN) \ + DO_VCMP(OP##b, 1, int8_t, FN) \ + DO_VCMP(OP##h, 2, int16_t, FN) \ + DO_VCMP(OP##w, 4, int32_t, FN) + +#define DO_VCMP_U(OP, FN) \ + DO_VCMP(OP##b, 1, uint8_t, FN) \ + DO_VCMP(OP##h, 2, uint16_t, FN) \ + DO_VCMP(OP##w, 4, uint32_t, FN) + +#define DO_EQ(N, M) ((N) == (M)) +#define DO_NE(N, M) ((N) != (M)) +#define DO_EQ(N, M) ((N) == (M)) +#define DO_EQ(N, M) ((N) == (M)) +#define DO_GE(N, M) ((N) >= (M)) +#define DO_LT(N, M) ((N) < (M)) +#define DO_GT(N, M) ((N) > (M)) +#define DO_LE(N, M) ((N) <= (M)) + +DO_VCMP_U(vcmpeq, DO_EQ) +DO_VCMP_U(vcmpne, DO_NE) +DO_VCMP_U(vcmpcs, DO_GE) +DO_VCMP_U(vcmphi, DO_GT) +DO_VCMP_S(vcmpge, DO_GE) +DO_VCMP_S(vcmplt, DO_LT) +DO_VCMP_S(vcmpgt, DO_GT) +DO_VCMP_S(vcmple, DO_LE) diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 6d8da36146..2d7211b527 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -43,6 +43,7 @@ typedef void MVEGenVADDVFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void MVEGenOneOpImmFn(TCGv_ptr, TCGv_ptr, TCGv_i64); typedef void MVEGenVIDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32); typedef void MVEGenVIWDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32); +typedef void MVEGenCmpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); /* Return the offset of a Qn register (same semantics as aa32_vfp_qreg()) */ static inline long mve_qreg_offset(unsigned reg) @@ -1182,3 +1183,49 @@ static bool trans_VDWDUP(DisasContext *s, arg_viwdup *a) }; return do_viwdup(s, a, fns[a->size]); } + +static bool do_vcmp(DisasContext *s, arg_vcmp *a, MVEGenCmpFn *fn) +{ + TCGv_ptr qn, qm; + + if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qm) || + !fn) { + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + qn = mve_qreg_ptr(a->qn); + qm = mve_qreg_ptr(a->qm); + fn(cpu_env, qn, qm); + tcg_temp_free_ptr(qn); + tcg_temp_free_ptr(qm); + if (a->mask) { + /* VPT */ + gen_vpst(s, a->mask); + } + mve_update_eci(s); + return true; +} + +#define DO_VCMP(INSN, FN) \ + static bool trans_##INSN(DisasContext *s, arg_vcmp *a) \ + { \ + static MVEGenCmpFn * const fns[] = { \ + gen_helper_mve_##FN##b, \ + gen_helper_mve_##FN##h, \ + gen_helper_mve_##FN##w, \ + NULL, \ + }; \ + return do_vcmp(s, a, fns[a->size]); \ + } + +DO_VCMP(VCMPEQ, vcmpeq) +DO_VCMP(VCMPNE, vcmpne) +DO_VCMP(VCMPCS, vcmpcs) +DO_VCMP(VCMPHI, vcmphi) +DO_VCMP(VCMPGE, vcmpge) +DO_VCMP(VCMPLT, vcmplt) +DO_VCMP(VCMPGT, vcmpgt) +DO_VCMP(VCMPLE, vcmple) From cce81873bcc163a86488deec1e122c303c6762a4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:51 +0100 Subject: [PATCH 021/493] target/arm: Implement MVE integer vector-vs-scalar comparisons Implement the MVE integer vector comparison instructions that compare each element against a scalar from a general purpose register. These are "VCMP (vector)" encodings T4, T5 and T6 and "VPT (vector)" encodings T4, T5 and T6. We have to move the decodetree pattern for VPST, because it overlaps with VCMP T4 with size = 0b11. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 32 +++++++++++++++++++++++++++ target/arm/mve.decode | 18 +++++++++++++--- target/arm/mve_helper.c | 44 +++++++++++++++++++++++++++++++------- target/arm/translate-mve.c | 43 +++++++++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 11 deletions(-) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index ca5a6ab51c..4f9903e66e 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -512,3 +512,35 @@ DEF_HELPER_FLAGS_3(mve_vcmpgtw, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vcmpleb, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vcmpleh, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vcmplew, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vcmpeq_scalarb, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmpeq_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmpeq_scalarw, TCG_CALL_NO_WG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(mve_vcmpne_scalarb, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmpne_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmpne_scalarw, TCG_CALL_NO_WG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(mve_vcmpcs_scalarb, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmpcs_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmpcs_scalarw, TCG_CALL_NO_WG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(mve_vcmphi_scalarb, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmphi_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmphi_scalarw, TCG_CALL_NO_WG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(mve_vcmpge_scalarb, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmpge_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmpge_scalarw, TCG_CALL_NO_WG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(mve_vcmplt_scalarb, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmplt_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmplt_scalarw, TCG_CALL_NO_WG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(mve_vcmpgt_scalarb, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmpgt_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmpgt_scalarw, TCG_CALL_NO_WG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(mve_vcmple_scalarb, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmple_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vcmple_scalarw, TCG_CALL_NO_WG, void, env, ptr, i32) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 76bbf9a613..ef708ba80f 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -38,6 +38,7 @@ &vidup qd rn size imm &viwdup qd rn rm size imm &vcmp qm qn size mask +&vcmp_scalar qn rm size mask @vldr_vstr ....... . . . . l:1 rn:4 ... ...... imm:7 &vldr_vstr qd=%qd u=0 # Note that both Rn and Qd are 3 bits only (no D bit) @@ -90,6 +91,8 @@ # Vector comparison; 4-bit Qm but 3-bit Qn %mask_22_13 22:1 13:3 @vcmp .... .... .. size:2 qn:3 . .... .... .... .... &vcmp qm=%qm mask=%mask_22_13 +@vcmp_scalar .... .... .. size:2 qn:3 . .... .... .... rm:4 &vcmp_scalar \ + mask=%mask_22_13 # Vector loads and stores @@ -349,9 +352,6 @@ VQRDMULH_scalar 1111 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar rdahi=%rdahi rdalo=%rdalo } -# Predicate operations -VPST 1111 1110 0 . 11 000 1 ... 0 1111 0100 1101 mask=%mask_22_13 - # Logical immediate operations (1 reg and modified-immediate) # The cmode/op bits here decode VORR/VBIC/VMOV/VMVN, but @@ -474,3 +474,15 @@ VCMPGE 1111 1110 0 . .. ... 1 ... 1 1111 0 0 . 0 ... 0 @vcmp VCMPLT 1111 1110 0 . .. ... 1 ... 1 1111 1 0 . 0 ... 0 @vcmp VCMPGT 1111 1110 0 . .. ... 1 ... 1 1111 0 0 . 0 ... 1 @vcmp VCMPLE 1111 1110 0 . .. ... 1 ... 1 1111 1 0 . 0 ... 1 @vcmp + +{ + VPST 1111 1110 0 . 11 000 1 ... 0 1111 0100 1101 mask=%mask_22_13 + VCMPEQ_scalar 1111 1110 0 . .. ... 1 ... 0 1111 0 1 0 0 .... @vcmp_scalar +} +VCMPNE_scalar 1111 1110 0 . .. ... 1 ... 0 1111 1 1 0 0 .... @vcmp_scalar +VCMPCS_scalar 1111 1110 0 . .. ... 1 ... 0 1111 0 1 1 0 .... @vcmp_scalar +VCMPHI_scalar 1111 1110 0 . .. ... 1 ... 0 1111 1 1 1 0 .... @vcmp_scalar +VCMPGE_scalar 1111 1110 0 . .. ... 1 ... 1 1111 0 1 0 0 .... @vcmp_scalar +VCMPLT_scalar 1111 1110 0 . .. ... 1 ... 1 1111 1 1 0 0 .... @vcmp_scalar +VCMPGT_scalar 1111 1110 0 . .. ... 1 ... 1 1111 0 1 1 0 .... @vcmp_scalar +VCMPLE_scalar 1111 1110 0 . .. ... 1 ... 1 1111 1 1 1 0 .... @vcmp_scalar diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index b0b380b94b..1a021a9a81 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1787,15 +1787,43 @@ DO_VIWDUP_ALL(vdwdup, do_sub_wrap) mve_advance_vpt(env); \ } -#define DO_VCMP_S(OP, FN) \ - DO_VCMP(OP##b, 1, int8_t, FN) \ - DO_VCMP(OP##h, 2, int16_t, FN) \ - DO_VCMP(OP##w, 4, int32_t, FN) +#define DO_VCMP_SCALAR(OP, ESIZE, TYPE, FN) \ + void HELPER(glue(mve_, OP))(CPUARMState *env, void *vn, \ + uint32_t rm) \ + { \ + TYPE *n = vn; \ + uint16_t mask = mve_element_mask(env); \ + uint16_t eci_mask = mve_eci_mask(env); \ + uint16_t beatpred = 0; \ + uint16_t emask = MAKE_64BIT_MASK(0, ESIZE); \ + unsigned e; \ + for (e = 0; e < 16 / ESIZE; e++) { \ + bool r = FN(n[H##ESIZE(e)], (TYPE)rm); \ + /* Comparison sets 0/1 bits for each byte in the element */ \ + beatpred |= r * emask; \ + emask <<= ESIZE; \ + } \ + beatpred &= mask; \ + env->v7m.vpr = (env->v7m.vpr & ~(uint32_t)eci_mask) | \ + (beatpred & eci_mask); \ + mve_advance_vpt(env); \ + } -#define DO_VCMP_U(OP, FN) \ - DO_VCMP(OP##b, 1, uint8_t, FN) \ - DO_VCMP(OP##h, 2, uint16_t, FN) \ - DO_VCMP(OP##w, 4, uint32_t, FN) +#define DO_VCMP_S(OP, FN) \ + DO_VCMP(OP##b, 1, int8_t, FN) \ + DO_VCMP(OP##h, 2, int16_t, FN) \ + DO_VCMP(OP##w, 4, int32_t, FN) \ + DO_VCMP_SCALAR(OP##_scalarb, 1, int8_t, FN) \ + DO_VCMP_SCALAR(OP##_scalarh, 2, int16_t, FN) \ + DO_VCMP_SCALAR(OP##_scalarw, 4, int32_t, FN) + +#define DO_VCMP_U(OP, FN) \ + DO_VCMP(OP##b, 1, uint8_t, FN) \ + DO_VCMP(OP##h, 2, uint16_t, FN) \ + DO_VCMP(OP##w, 4, uint32_t, FN) \ + DO_VCMP_SCALAR(OP##_scalarb, 1, uint8_t, FN) \ + DO_VCMP_SCALAR(OP##_scalarh, 2, uint16_t, FN) \ + DO_VCMP_SCALAR(OP##_scalarw, 4, uint32_t, FN) #define DO_EQ(N, M) ((N) == (M)) #define DO_NE(N, M) ((N) != (M)) diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 2d7211b527..6c6f159aa3 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -44,6 +44,7 @@ typedef void MVEGenOneOpImmFn(TCGv_ptr, TCGv_ptr, TCGv_i64); typedef void MVEGenVIDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32); typedef void MVEGenVIWDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32); typedef void MVEGenCmpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); +typedef void MVEGenScalarCmpFn(TCGv_ptr, TCGv_ptr, TCGv_i32); /* Return the offset of a Qn register (same semantics as aa32_vfp_qreg()) */ static inline long mve_qreg_offset(unsigned reg) @@ -1209,6 +1210,37 @@ static bool do_vcmp(DisasContext *s, arg_vcmp *a, MVEGenCmpFn *fn) return true; } +static bool do_vcmp_scalar(DisasContext *s, arg_vcmp_scalar *a, + MVEGenScalarCmpFn *fn) +{ + TCGv_ptr qn; + TCGv_i32 rm; + + if (!dc_isar_feature(aa32_mve, s) || !fn || a->rm == 13) { + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + qn = mve_qreg_ptr(a->qn); + if (a->rm == 15) { + /* Encoding Rm=0b1111 means "constant zero" */ + rm = tcg_constant_i32(0); + } else { + rm = load_reg(s, a->rm); + } + fn(cpu_env, qn, rm); + tcg_temp_free_ptr(qn); + tcg_temp_free_i32(rm); + if (a->mask) { + /* VPT */ + gen_vpst(s, a->mask); + } + mve_update_eci(s); + return true; +} + #define DO_VCMP(INSN, FN) \ static bool trans_##INSN(DisasContext *s, arg_vcmp *a) \ { \ @@ -1219,6 +1251,17 @@ static bool do_vcmp(DisasContext *s, arg_vcmp *a, MVEGenCmpFn *fn) NULL, \ }; \ return do_vcmp(s, a, fns[a->size]); \ + } \ + static bool trans_##INSN##_scalar(DisasContext *s, \ + arg_vcmp_scalar *a) \ + { \ + static MVEGenScalarCmpFn * const fns[] = { \ + gen_helper_mve_##FN##_scalarb, \ + gen_helper_mve_##FN##_scalarh, \ + gen_helper_mve_##FN##_scalarw, \ + NULL, \ + }; \ + return do_vcmp_scalar(s, a, fns[a->size]); \ } DO_VCMP(VCMPEQ, vcmpeq) From c386443b163965e44ae7a7f7858ec7985e97926b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:51 +0100 Subject: [PATCH 022/493] target/arm: Implement MVE VPSEL Implement the MVE VPSEL insn, which sets each byte of the destination vector Qd to the byte from either Qn or Qm depending on the value of the corresponding bit in VPR.P0. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 2 ++ target/arm/mve.decode | 7 +++++-- target/arm/mve_helper.c | 19 +++++++++++++++++++ target/arm/translate-mve.c | 2 ++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 4f9903e66e..16c4c3b8f6 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -82,6 +82,8 @@ DEF_HELPER_FLAGS_4(mve_vorr, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vorn, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_veor, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) +DEF_HELPER_FLAGS_4(mve_vpsel, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) + DEF_HELPER_FLAGS_4(mve_vaddb, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vaddh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vaddw, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index ef708ba80f..4bd20a9a31 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -468,8 +468,11 @@ VSHLC 111 0 1110 1 . 1 imm:5 ... 0 1111 1100 rdm:4 qd=%qd # effectively "VCMP then VPST". A plain "VCMP" has a mask field of zero. VCMPEQ 1111 1110 0 . .. ... 1 ... 0 1111 0 0 . 0 ... 0 @vcmp VCMPNE 1111 1110 0 . .. ... 1 ... 0 1111 1 0 . 0 ... 0 @vcmp -VCMPCS 1111 1110 0 . .. ... 1 ... 0 1111 0 0 . 0 ... 1 @vcmp -VCMPHI 1111 1110 0 . .. ... 1 ... 0 1111 1 0 . 0 ... 1 @vcmp +{ + VPSEL 1111 1110 0 . 11 ... 1 ... 0 1111 . 0 . 0 ... 1 @2op_nosz + VCMPCS 1111 1110 0 . .. ... 1 ... 0 1111 0 0 . 0 ... 1 @vcmp + VCMPHI 1111 1110 0 . .. ... 1 ... 0 1111 1 0 . 0 ... 1 @vcmp +} VCMPGE 1111 1110 0 . .. ... 1 ... 1 1111 0 0 . 0 ... 0 @vcmp VCMPLT 1111 1110 0 . .. ... 1 ... 1 1111 1 0 . 0 ... 0 @vcmp VCMPGT 1111 1110 0 . .. ... 1 ... 1 1111 0 0 . 0 ... 1 @vcmp diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 1a021a9a81..03171766b5 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1842,3 +1842,22 @@ DO_VCMP_S(vcmpge, DO_GE) DO_VCMP_S(vcmplt, DO_LT) DO_VCMP_S(vcmpgt, DO_GT) DO_VCMP_S(vcmple, DO_LE) + +void HELPER(mve_vpsel)(CPUARMState *env, void *vd, void *vn, void *vm) +{ + /* + * Qd[n] = VPR.P0[n] ? Qn[n] : Qm[n] + * but note that whether bytes are written to Qd is still subject + * to (all forms of) predication in the usual way. + */ + uint64_t *d = vd, *n = vn, *m = vm; + uint16_t mask = mve_element_mask(env); + uint16_t p0 = FIELD_EX32(env->v7m.vpr, V7M_VPR, P0); + unsigned e; + for (e = 0; e < 16 / 8; e++, mask >>= 8, p0 >>= 8) { + uint64_t r = m[H8(e)]; + mergemask(&r, n[H8(e)], p0); + mergemask(&d[H8(e)], r, mask); + } + mve_advance_vpt(env); +} diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 6c6f159aa3..aa38218e08 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -376,6 +376,8 @@ DO_LOGIC(VORR, gen_helper_mve_vorr) DO_LOGIC(VORN, gen_helper_mve_vorn) DO_LOGIC(VEOR, gen_helper_mve_veor) +DO_LOGIC(VPSEL, gen_helper_mve_vpsel) + #define DO_2OP(INSN, FN) \ static bool trans_##INSN(DisasContext *s, arg_2op *a) \ { \ From 6b895bf8fb088a04a91714a555d2b6234cf1e98d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:52 +0100 Subject: [PATCH 023/493] target/arm: Implement MVE VMLAS Implement the MVE VMLAS insn, which multiplies a vector by a vector and adds a scalar. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 4 ++++ target/arm/mve.decode | 3 +++ target/arm/mve_helper.c | 26 ++++++++++++++++++++++++++ target/arm/translate-mve.c | 1 + 4 files changed, 34 insertions(+) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 16c4c3b8f6..715b1bbd01 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -347,6 +347,10 @@ DEF_HELPER_FLAGS_4(mve_vqdmullb_scalarw, TCG_CALL_NO_WG, void, env, ptr, ptr, i3 DEF_HELPER_FLAGS_4(mve_vqdmullt_scalarh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vqdmullt_scalarw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlasb, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlash, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlasw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(mve_vmlaldavsh, TCG_CALL_NO_WG, i64, env, ptr, ptr, i64) DEF_HELPER_FLAGS_4(mve_vmlaldavsw, TCG_CALL_NO_WG, i64, env, ptr, ptr, i64) DEF_HELPER_FLAGS_4(mve_vmlaldavxsh, TCG_CALL_NO_WG, i64, env, ptr, ptr, i64) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 4bd20a9a31..226b74790b 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -345,6 +345,9 @@ VBRSR 1111 1110 0 . .. ... 1 ... 1 1110 . 110 .... @2scalar VQDMULH_scalar 1110 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar VQRDMULH_scalar 1111 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar +# The U bit (28) is don't-care because it does not affect the result +VMLAS 111- 1110 0 . .. ... 1 ... 1 1110 . 100 .... @2scalar + # Vector add across vector { VADDV 111 u:1 1110 1111 size:2 01 ... 0 1111 0 0 a:1 0 qm:3 0 rda=%rdalo diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 03171766b5..ab02a1e60f 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -948,6 +948,22 @@ DO_VQDMLADH_OP(vqrdmlsdhxw, 4, int32_t, 1, 1, do_vqdmlsdh_w) mve_advance_vpt(env); \ } +/* "accumulating" version where FN takes d as well as n and m */ +#define DO_2OP_ACC_SCALAR(OP, ESIZE, TYPE, FN) \ + void HELPER(glue(mve_, OP))(CPUARMState *env, void *vd, void *vn, \ + uint32_t rm) \ + { \ + TYPE *d = vd, *n = vn; \ + TYPE m = rm; \ + uint16_t mask = mve_element_mask(env); \ + unsigned e; \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ + mergemask(&d[H##ESIZE(e)], \ + FN(d[H##ESIZE(e)], n[H##ESIZE(e)], m), mask); \ + } \ + mve_advance_vpt(env); \ + } + /* provide unsigned 2-op scalar helpers for all sizes */ #define DO_2OP_SCALAR_U(OP, FN) \ DO_2OP_SCALAR(OP##b, 1, uint8_t, FN) \ @@ -958,6 +974,11 @@ DO_VQDMLADH_OP(vqrdmlsdhxw, 4, int32_t, 1, 1, do_vqdmlsdh_w) DO_2OP_SCALAR(OP##h, 2, int16_t, FN) \ DO_2OP_SCALAR(OP##w, 4, int32_t, FN) +#define DO_2OP_ACC_SCALAR_U(OP, FN) \ + DO_2OP_ACC_SCALAR(OP##b, 1, uint8_t, FN) \ + DO_2OP_ACC_SCALAR(OP##h, 2, uint16_t, FN) \ + DO_2OP_ACC_SCALAR(OP##w, 4, uint32_t, FN) + DO_2OP_SCALAR_U(vadd_scalar, DO_ADD) DO_2OP_SCALAR_U(vsub_scalar, DO_SUB) DO_2OP_SCALAR_U(vmul_scalar, DO_MUL) @@ -987,6 +1008,11 @@ DO_2OP_SAT_SCALAR(vqrdmulh_scalarb, 1, int8_t, DO_QRDMULH_B) DO_2OP_SAT_SCALAR(vqrdmulh_scalarh, 2, int16_t, DO_QRDMULH_H) DO_2OP_SAT_SCALAR(vqrdmulh_scalarw, 4, int32_t, DO_QRDMULH_W) +/* Vector by vector plus scalar */ +#define DO_VMLAS(D, N, M) ((N) * (D) + (M)) + +DO_2OP_ACC_SCALAR_U(vmlas, DO_VMLAS) + /* * Long saturating scalar ops. As with DO_2OP_L, TYPE and H are for the * input (smaller) type and LESIZE, LTYPE, LH for the output (long) type. diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index aa38218e08..b56c91db2a 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -596,6 +596,7 @@ DO_2OP_SCALAR(VQSUB_U_scalar, vqsubu_scalar) DO_2OP_SCALAR(VQDMULH_scalar, vqdmulh_scalar) DO_2OP_SCALAR(VQRDMULH_scalar, vqrdmulh_scalar) DO_2OP_SCALAR(VBRSR, vbrsr) +DO_2OP_SCALAR(VMLAS, vmlas) static bool trans_VQDMULLB_scalar(DisasContext *s, arg_2scalar *a) { From 1b15a97d4cf99efdd60e60e0bfd2d185174ab0eb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:52 +0100 Subject: [PATCH 024/493] target/arm: Implement MVE shift-by-scalar Implement the MVE instructions which perform shifts by a scalar. These are VSHL T2, VRSHL T2, VQSHL T1 and VQRSHL T2. They take the shift amount in a general purpose register and shift every element in the vector by that amount. Mostly we can reuse the helper functions for shift-by-immediate; we do need two new helpers for VQRSHL. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 8 +++++++ target/arm/mve.decode | 23 ++++++++++++++++--- target/arm/mve_helper.c | 2 ++ target/arm/translate-mve.c | 46 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 3 deletions(-) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 715b1bbd01..0ee5ea3cab 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -414,6 +414,14 @@ DEF_HELPER_FLAGS_4(mve_vrshli_ub, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vrshli_uh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vrshli_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqrshli_sb, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqrshli_sh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqrshli_sw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(mve_vqrshli_ub, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqrshli_uh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqrshli_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(mve_vshllbsb, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vshllbsh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vshllbub, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 226b74790b..eb26b103d1 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -39,6 +39,7 @@ &viwdup qd rn rm size imm &vcmp qm qn size mask &vcmp_scalar qn rm size mask +&shl_scalar qda rm size @vldr_vstr ....... . . . . l:1 rn:4 ... ...... imm:7 &vldr_vstr qd=%qd u=0 # Note that both Rn and Qd are 3 bits only (no D bit) @@ -88,6 +89,8 @@ @2_shr_w .... .... .. 1 ..... .... .... .... .... &2shift qd=%qd qm=%qm \ size=2 shift=%rshift_i5 +@shl_scalar .... .... .... size:2 .. .... .... .... rm:4 &shl_scalar qda=%qd + # Vector comparison; 4-bit Qm but 3-bit Qn %mask_22_13 22:1 13:3 @vcmp .... .... .. size:2 qn:3 . .... .... .... .... &vcmp qm=%qm mask=%mask_22_13 @@ -320,7 +323,23 @@ VRMLSLDAVH 1111 1110 1 ... ... 0 ... x:1 1110 . 0 a:1 0 ... 1 @vmlaldav_no VADD_scalar 1110 1110 0 . .. ... 1 ... 0 1111 . 100 .... @2scalar VSUB_scalar 1110 1110 0 . .. ... 1 ... 1 1111 . 100 .... @2scalar -VMUL_scalar 1110 1110 0 . .. ... 1 ... 1 1110 . 110 .... @2scalar + +{ + VSHL_S_scalar 1110 1110 0 . 11 .. 01 ... 1 1110 0110 .... @shl_scalar + VRSHL_S_scalar 1110 1110 0 . 11 .. 11 ... 1 1110 0110 .... @shl_scalar + VQSHL_S_scalar 1110 1110 0 . 11 .. 01 ... 1 1110 1110 .... @shl_scalar + VQRSHL_S_scalar 1110 1110 0 . 11 .. 11 ... 1 1110 1110 .... @shl_scalar + VMUL_scalar 1110 1110 0 . .. ... 1 ... 1 1110 . 110 .... @2scalar +} + +{ + VSHL_U_scalar 1111 1110 0 . 11 .. 01 ... 1 1110 0110 .... @shl_scalar + VRSHL_U_scalar 1111 1110 0 . 11 .. 11 ... 1 1110 0110 .... @shl_scalar + VQSHL_U_scalar 1111 1110 0 . 11 .. 01 ... 1 1110 1110 .... @shl_scalar + VQRSHL_U_scalar 1111 1110 0 . 11 .. 11 ... 1 1110 1110 .... @shl_scalar + VBRSR 1111 1110 0 . .. ... 1 ... 1 1110 . 110 .... @2scalar +} + VHADD_S_scalar 1110 1110 0 . .. ... 0 ... 0 1111 . 100 .... @2scalar VHADD_U_scalar 1111 1110 0 . .. ... 0 ... 0 1111 . 100 .... @2scalar VHSUB_S_scalar 1110 1110 0 . .. ... 0 ... 1 1111 . 100 .... @2scalar @@ -340,8 +359,6 @@ VHSUB_U_scalar 1111 1110 0 . .. ... 0 ... 1 1111 . 100 .... @2scalar size=%size_28 } -VBRSR 1111 1110 0 . .. ... 1 ... 1 1110 . 110 .... @2scalar - VQDMULH_scalar 1110 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar VQRDMULH_scalar 1111 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index ab02a1e60f..ac608fc524 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1334,6 +1334,8 @@ DO_2SHIFT_SAT_S(vqshli_s, DO_SQSHL_OP) DO_2SHIFT_SAT_S(vqshlui_s, DO_SUQSHL_OP) DO_2SHIFT_U(vrshli_u, DO_VRSHLU) DO_2SHIFT_S(vrshli_s, DO_VRSHLS) +DO_2SHIFT_SAT_U(vqrshli_u, DO_UQRSHL_OP) +DO_2SHIFT_SAT_S(vqrshli_s, DO_SQRSHL_OP) /* Shift-and-insert; we always work with 64 bits at a time */ #define DO_2SHIFT_INSERT(OP, ESIZE, SHIFTFN, MASKFN) \ diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index b56c91db2a..44731fc4eb 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -1003,6 +1003,52 @@ DO_2SHIFT(VRSHRI_U, vrshli_u, true) DO_2SHIFT(VSRI, vsri, false) DO_2SHIFT(VSLI, vsli, false) +static bool do_2shift_scalar(DisasContext *s, arg_shl_scalar *a, + MVEGenTwoOpShiftFn *fn) +{ + TCGv_ptr qda; + TCGv_i32 rm; + + if (!dc_isar_feature(aa32_mve, s) || + !mve_check_qreg_bank(s, a->qda) || + a->rm == 13 || a->rm == 15 || !fn) { + /* Rm cases are UNPREDICTABLE */ + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + qda = mve_qreg_ptr(a->qda); + rm = load_reg(s, a->rm); + fn(cpu_env, qda, qda, rm); + tcg_temp_free_ptr(qda); + tcg_temp_free_i32(rm); + mve_update_eci(s); + return true; +} + +#define DO_2SHIFT_SCALAR(INSN, FN) \ + static bool trans_##INSN(DisasContext *s, arg_shl_scalar *a) \ + { \ + static MVEGenTwoOpShiftFn * const fns[] = { \ + gen_helper_mve_##FN##b, \ + gen_helper_mve_##FN##h, \ + gen_helper_mve_##FN##w, \ + NULL, \ + }; \ + return do_2shift_scalar(s, a, fns[a->size]); \ + } + +DO_2SHIFT_SCALAR(VSHL_S_scalar, vshli_s) +DO_2SHIFT_SCALAR(VSHL_U_scalar, vshli_u) +DO_2SHIFT_SCALAR(VRSHL_S_scalar, vrshli_s) +DO_2SHIFT_SCALAR(VRSHL_U_scalar, vrshli_u) +DO_2SHIFT_SCALAR(VQSHL_S_scalar, vqshli_s) +DO_2SHIFT_SCALAR(VQSHL_U_scalar, vqshli_u) +DO_2SHIFT_SCALAR(VQRSHL_S_scalar, vqrshli_s) +DO_2SHIFT_SCALAR(VQRSHL_U_scalar, vqrshli_u) + #define DO_VSHLL(INSN, FN) \ static bool trans_##INSN(DisasContext *s, arg_2shift *a) \ { \ From 345910f8c1d687404b62194d929ca32f2ab54e80 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:52 +0100 Subject: [PATCH 025/493] target/arm: Move 'x' and 'a' bit definitions into vmlaldav formats All the users of the vmlaldav formats have an 'x bit in bit 12 and an 'a' bit in bit 5; move these to the format rather than specifying them in each insn pattern. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve.decode | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index eb26b103d1..bdcd660aaf 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -305,19 +305,19 @@ VDUP 1110 1110 1 0 10 ... 0 .... 1011 . 0 0 1 0000 @vdup size=2 &vmlaldav rdahi rdalo size qn qm x a -@vmlaldav .... .... . ... ... . ... . .... .... qm:3 . \ +@vmlaldav .... .... . ... ... . ... x:1 .... .. a:1 . qm:3 . \ qn=%qn rdahi=%rdahi rdalo=%rdalo size=%size_16 &vmlaldav -@vmlaldav_nosz .... .... . ... ... . ... . .... .... qm:3 . \ +@vmlaldav_nosz .... .... . ... ... . ... x:1 .... .. a:1 . qm:3 . \ qn=%qn rdahi=%rdahi rdalo=%rdalo size=0 &vmlaldav -VMLALDAV_S 1110 1110 1 ... ... . ... x:1 1110 . 0 a:1 0 ... 0 @vmlaldav -VMLALDAV_U 1111 1110 1 ... ... . ... x:1 1110 . 0 a:1 0 ... 0 @vmlaldav +VMLALDAV_S 1110 1110 1 ... ... . ... . 1110 . 0 . 0 ... 0 @vmlaldav +VMLALDAV_U 1111 1110 1 ... ... . ... . 1110 . 0 . 0 ... 0 @vmlaldav -VMLSLDAV 1110 1110 1 ... ... . ... x:1 1110 . 0 a:1 0 ... 1 @vmlaldav +VMLSLDAV 1110 1110 1 ... ... . ... . 1110 . 0 . 0 ... 1 @vmlaldav -VRMLALDAVH_S 1110 1110 1 ... ... 0 ... x:1 1111 . 0 a:1 0 ... 0 @vmlaldav_nosz -VRMLALDAVH_U 1111 1110 1 ... ... 0 ... x:1 1111 . 0 a:1 0 ... 0 @vmlaldav_nosz +VRMLALDAVH_S 1110 1110 1 ... ... 0 ... . 1111 . 0 . 0 ... 0 @vmlaldav_nosz +VRMLALDAVH_U 1111 1110 1 ... ... 0 ... . 1111 . 0 . 0 ... 0 @vmlaldav_nosz -VRMLSLDAVH 1111 1110 1 ... ... 0 ... x:1 1110 . 0 a:1 0 ... 1 @vmlaldav_nosz +VRMLSLDAVH 1111 1110 1 ... ... 0 ... . 1110 . 0 . 0 ... 1 @vmlaldav_nosz # Scalar operations From 688ba4cf33f4976e26124c4c24e9eb738615b0bf Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:52 +0100 Subject: [PATCH 026/493] target/arm: Implement MVE integer min/max across vector Implement the MVE integer min/max across vector insns VMAXV, VMINV, VMAXAV and VMINAV, which find the maximum from the vector elements and a general purpose register, and store the maximum back into the general purpose register. These insns overlap with VRMLALDAVH (they use what would be RdaHi=0b110). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 20 ++++++++++++ target/arm/mve.decode | 18 +++++++++-- target/arm/mve_helper.c | 66 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-mve.c | 48 +++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 2 deletions(-) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 0ee5ea3cab..2c66fcba79 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -379,6 +379,26 @@ DEF_HELPER_FLAGS_3(mve_vaddvuh, TCG_CALL_NO_WG, i32, env, ptr, i32) DEF_HELPER_FLAGS_3(mve_vaddvsw, TCG_CALL_NO_WG, i32, env, ptr, i32) DEF_HELPER_FLAGS_3(mve_vaddvuw, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vmaxvsb, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vmaxvsh, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vmaxvsw, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vmaxvub, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vmaxvuh, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vmaxvuw, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vmaxavb, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vmaxavh, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vmaxavw, TCG_CALL_NO_WG, i32, env, ptr, i32) + +DEF_HELPER_FLAGS_3(mve_vminvsb, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vminvsh, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vminvsw, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vminvub, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vminvuh, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vminvuw, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vminavb, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vminavh, TCG_CALL_NO_WG, i32, env, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vminavw, TCG_CALL_NO_WG, i32, env, ptr, i32) + DEF_HELPER_FLAGS_3(mve_vaddlv_s, TCG_CALL_NO_WG, i64, env, ptr, i64) DEF_HELPER_FLAGS_3(mve_vaddlv_u, TCG_CALL_NO_WG, i64, env, ptr, i64) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index bdcd660aaf..83dc0300d6 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -40,6 +40,7 @@ &vcmp qm qn size mask &vcmp_scalar qn rm size mask &shl_scalar qda rm size +&vmaxv qm rda size @vldr_vstr ....... . . . . l:1 rn:4 ... ...... imm:7 &vldr_vstr qd=%qd u=0 # Note that both Rn and Qd are 3 bits only (no D bit) @@ -97,6 +98,8 @@ @vcmp_scalar .... .... .. size:2 qn:3 . .... .... .... rm:4 &vcmp_scalar \ mask=%mask_22_13 +@vmaxv .... .... .... size:2 .. rda:4 .... .... .... &vmaxv qm=%qm + # Vector loads and stores # Widening loads and narrowing stores: @@ -314,8 +317,19 @@ VMLALDAV_U 1111 1110 1 ... ... . ... . 1110 . 0 . 0 ... 0 @vmlaldav VMLSLDAV 1110 1110 1 ... ... . ... . 1110 . 0 . 0 ... 1 @vmlaldav -VRMLALDAVH_S 1110 1110 1 ... ... 0 ... . 1111 . 0 . 0 ... 0 @vmlaldav_nosz -VRMLALDAVH_U 1111 1110 1 ... ... 0 ... . 1111 . 0 . 0 ... 0 @vmlaldav_nosz +{ + VMAXV_S 1110 1110 1110 .. 10 .... 1111 0 0 . 0 ... 0 @vmaxv + VMINV_S 1110 1110 1110 .. 10 .... 1111 1 0 . 0 ... 0 @vmaxv + VMAXAV 1110 1110 1110 .. 00 .... 1111 0 0 . 0 ... 0 @vmaxv + VMINAV 1110 1110 1110 .. 00 .... 1111 1 0 . 0 ... 0 @vmaxv + VRMLALDAVH_S 1110 1110 1 ... ... 0 ... . 1111 . 0 . 0 ... 0 @vmlaldav_nosz +} + +{ + VMAXV_U 1111 1110 1110 .. 10 .... 1111 0 0 . 0 ... 0 @vmaxv + VMINV_U 1111 1110 1110 .. 10 .... 1111 1 0 . 0 ... 0 @vmaxv + VRMLALDAVH_U 1111 1110 1 ... ... 0 ... . 1111 . 0 . 0 ... 0 @vmlaldav_nosz +} VRMLSLDAVH 1111 1110 1 ... ... 0 ... . 1110 . 0 . 0 ... 1 @vmlaldav_nosz diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index ac608fc524..924ad7f2bd 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1254,6 +1254,72 @@ DO_VADDV(vaddvub, 1, uint8_t) DO_VADDV(vaddvuh, 2, uint16_t) DO_VADDV(vaddvuw, 4, uint32_t) +/* + * Vector max/min across vector. Unlike VADDV, we must + * read ra as the element size, not its full width. + * We work with int64_t internally for simplicity. + */ +#define DO_VMAXMINV(OP, ESIZE, TYPE, RATYPE, FN) \ + uint32_t HELPER(glue(mve_, OP))(CPUARMState *env, void *vm, \ + uint32_t ra_in) \ + { \ + uint16_t mask = mve_element_mask(env); \ + unsigned e; \ + TYPE *m = vm; \ + int64_t ra = (RATYPE)ra_in; \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ + if (mask & 1) { \ + ra = FN(ra, m[H##ESIZE(e)]); \ + } \ + } \ + mve_advance_vpt(env); \ + return ra; \ + } \ + +#define DO_VMAXMINV_U(INSN, FN) \ + DO_VMAXMINV(INSN##b, 1, uint8_t, uint8_t, FN) \ + DO_VMAXMINV(INSN##h, 2, uint16_t, uint16_t, FN) \ + DO_VMAXMINV(INSN##w, 4, uint32_t, uint32_t, FN) +#define DO_VMAXMINV_S(INSN, FN) \ + DO_VMAXMINV(INSN##b, 1, int8_t, int8_t, FN) \ + DO_VMAXMINV(INSN##h, 2, int16_t, int16_t, FN) \ + DO_VMAXMINV(INSN##w, 4, int32_t, int32_t, FN) + +/* + * Helpers for max and min of absolute values across vector: + * note that we only take the absolute value of 'm', not 'n' + */ +static int64_t do_maxa(int64_t n, int64_t m) +{ + if (m < 0) { + m = -m; + } + return MAX(n, m); +} + +static int64_t do_mina(int64_t n, int64_t m) +{ + if (m < 0) { + m = -m; + } + return MIN(n, m); +} + +DO_VMAXMINV_S(vmaxvs, DO_MAX) +DO_VMAXMINV_U(vmaxvu, DO_MAX) +DO_VMAXMINV_S(vminvs, DO_MIN) +DO_VMAXMINV_U(vminvu, DO_MIN) +/* + * VMAXAV, VMINAV treat the general purpose input as unsigned + * and the vector elements as signed. + */ +DO_VMAXMINV(vmaxavb, 1, int8_t, uint8_t, do_maxa) +DO_VMAXMINV(vmaxavh, 2, int16_t, uint16_t, do_maxa) +DO_VMAXMINV(vmaxavw, 4, int32_t, uint32_t, do_maxa) +DO_VMAXMINV(vminavb, 1, int8_t, uint8_t, do_mina) +DO_VMAXMINV(vminavh, 2, int16_t, uint16_t, do_mina) +DO_VMAXMINV(vminavw, 4, int32_t, uint32_t, do_mina) + #define DO_VADDLV(OP, TYPE, LTYPE) \ uint64_t HELPER(glue(mve_, OP))(CPUARMState *env, void *vm, \ uint64_t ra) \ diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 44731fc4eb..2fce74f86a 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -1321,3 +1321,51 @@ DO_VCMP(VCMPGE, vcmpge) DO_VCMP(VCMPLT, vcmplt) DO_VCMP(VCMPGT, vcmpgt) DO_VCMP(VCMPLE, vcmple) + +static bool do_vmaxv(DisasContext *s, arg_vmaxv *a, MVEGenVADDVFn fn) +{ + /* + * MIN/MAX operations across a vector: compute the min or + * max of the initial value in a general purpose register + * and all the elements in the vector, and store it back + * into the general purpose register. + */ + TCGv_ptr qm; + TCGv_i32 rda; + + if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qm) || + !fn || a->rda == 13 || a->rda == 15) { + /* Rda cases are UNPREDICTABLE */ + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + qm = mve_qreg_ptr(a->qm); + rda = load_reg(s, a->rda); + fn(rda, cpu_env, qm, rda); + store_reg(s, a->rda, rda); + tcg_temp_free_ptr(qm); + mve_update_eci(s); + return true; +} + +#define DO_VMAXV(INSN, FN) \ + static bool trans_##INSN(DisasContext *s, arg_vmaxv *a) \ + { \ + static MVEGenVADDVFn * const fns[] = { \ + gen_helper_mve_##FN##b, \ + gen_helper_mve_##FN##h, \ + gen_helper_mve_##FN##w, \ + NULL, \ + }; \ + return do_vmaxv(s, a, fns[a->size]); \ + } + +DO_VMAXV(VMAXV_S, vmaxvs) +DO_VMAXV(VMAXV_U, vmaxvu) +DO_VMAXV(VMAXAV, vmaxav) +DO_VMAXV(VMINV_S, vminvs) +DO_VMAXV(VMINV_U, vminvu) +DO_VMAXV(VMINAV, vminav) From 7f061c0ab9289cb0ed55eaf09bec1b6cb474e6ee Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:53 +0100 Subject: [PATCH 027/493] target/arm: Implement MVE VABAV Implement the MVE VABAV insn, which computes absolute differences between elements of two vectors and accumulates the result into a general purpose register. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 7 +++++++ target/arm/mve.decode | 6 ++++++ target/arm/mve_helper.c | 26 +++++++++++++++++++++++ target/arm/translate-mve.c | 43 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 2c66fcba79..c7e7aab2cb 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -402,6 +402,13 @@ DEF_HELPER_FLAGS_3(mve_vminavw, TCG_CALL_NO_WG, i32, env, ptr, i32) DEF_HELPER_FLAGS_3(mve_vaddlv_s, TCG_CALL_NO_WG, i64, env, ptr, i64) DEF_HELPER_FLAGS_3(mve_vaddlv_u, TCG_CALL_NO_WG, i64, env, ptr, i64) +DEF_HELPER_FLAGS_4(mve_vabavsb, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vabavsh, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vabavsw, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vabavub, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vabavuh, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vabavuw, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) + DEF_HELPER_FLAGS_3(mve_vmovi, TCG_CALL_NO_WG, void, env, ptr, i64) DEF_HELPER_FLAGS_3(mve_vandi, TCG_CALL_NO_WG, void, env, ptr, i64) DEF_HELPER_FLAGS_3(mve_vorri, TCG_CALL_NO_WG, void, env, ptr, i64) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 83dc0300d6..c8a06edca7 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -41,6 +41,7 @@ &vcmp_scalar qn rm size mask &shl_scalar qda rm size &vmaxv qm rda size +&vabav qn qm rda size @vldr_vstr ....... . . . . l:1 rn:4 ... ...... imm:7 &vldr_vstr qd=%qd u=0 # Note that both Rn and Qd are 3 bits only (no D bit) @@ -386,6 +387,11 @@ VMLAS 111- 1110 0 . .. ... 1 ... 1 1110 . 100 .... @2scalar rdahi=%rdahi rdalo=%rdalo } +@vabav .... .... .. size:2 .... rda:4 .... .... .... &vabav qn=%qn qm=%qm + +VABAV_S 111 0 1110 10 .. ... 0 .... 1111 . 0 . 0 ... 1 @vabav +VABAV_U 111 1 1110 10 .. ... 0 .... 1111 . 0 . 0 ... 1 @vabav + # Logical immediate operations (1 reg and modified-immediate) # The cmode/op bits here decode VORR/VBIC/VMOV/VMVN, but diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 924ad7f2bd..fed0f3cd61 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1320,6 +1320,32 @@ DO_VMAXMINV(vminavb, 1, int8_t, uint8_t, do_mina) DO_VMAXMINV(vminavh, 2, int16_t, uint16_t, do_mina) DO_VMAXMINV(vminavw, 4, int32_t, uint32_t, do_mina) +#define DO_VABAV(OP, ESIZE, TYPE) \ + uint32_t HELPER(glue(mve_, OP))(CPUARMState *env, void *vn, \ + void *vm, uint32_t ra) \ + { \ + uint16_t mask = mve_element_mask(env); \ + unsigned e; \ + TYPE *m = vm, *n = vn; \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ + if (mask & 1) { \ + int64_t n0 = n[H##ESIZE(e)]; \ + int64_t m0 = m[H##ESIZE(e)]; \ + uint32_t r = n0 >= m0 ? (n0 - m0) : (m0 - n0); \ + ra += r; \ + } \ + } \ + mve_advance_vpt(env); \ + return ra; \ + } + +DO_VABAV(vabavsb, 1, int8_t) +DO_VABAV(vabavsh, 2, int16_t) +DO_VABAV(vabavsw, 4, int32_t) +DO_VABAV(vabavub, 1, uint8_t) +DO_VABAV(vabavuh, 2, uint16_t) +DO_VABAV(vabavuw, 4, uint32_t) + #define DO_VADDLV(OP, TYPE, LTYPE) \ uint64_t HELPER(glue(mve_, OP))(CPUARMState *env, void *vm, \ uint64_t ra) \ diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 2fce74f86a..247f6719e6 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -45,6 +45,7 @@ typedef void MVEGenVIDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32); typedef void MVEGenVIWDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32); typedef void MVEGenCmpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); typedef void MVEGenScalarCmpFn(TCGv_ptr, TCGv_ptr, TCGv_i32); +typedef void MVEGenVABAVFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); /* Return the offset of a Qn register (same semantics as aa32_vfp_qreg()) */ static inline long mve_qreg_offset(unsigned reg) @@ -1369,3 +1370,45 @@ DO_VMAXV(VMAXAV, vmaxav) DO_VMAXV(VMINV_S, vminvs) DO_VMAXV(VMINV_U, vminvu) DO_VMAXV(VMINAV, vminav) + +static bool do_vabav(DisasContext *s, arg_vabav *a, MVEGenVABAVFn *fn) +{ + /* Absolute difference accumulated across vector */ + TCGv_ptr qn, qm; + TCGv_i32 rda; + + if (!dc_isar_feature(aa32_mve, s) || + !mve_check_qreg_bank(s, a->qm | a->qn) || + !fn || a->rda == 13 || a->rda == 15) { + /* Rda cases are UNPREDICTABLE */ + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + qm = mve_qreg_ptr(a->qm); + qn = mve_qreg_ptr(a->qn); + rda = load_reg(s, a->rda); + fn(rda, cpu_env, qn, qm, rda); + store_reg(s, a->rda, rda); + tcg_temp_free_ptr(qm); + tcg_temp_free_ptr(qn); + mve_update_eci(s); + return true; +} + +#define DO_VABAV(INSN, FN) \ + static bool trans_##INSN(DisasContext *s, arg_vabav *a) \ + { \ + static MVEGenVABAVFn * const fns[] = { \ + gen_helper_mve_##FN##b, \ + gen_helper_mve_##FN##h, \ + gen_helper_mve_##FN##w, \ + NULL, \ + }; \ + return do_vabav(s, a, fns[a->size]); \ + } + +DO_VABAV(VABAV_S, vabavs) +DO_VABAV(VABAV_U, vabavu) From 54dc78a901188d208a3dfedb0f98230043509120 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:53 +0100 Subject: [PATCH 028/493] target/arm: Implement MVE narrowing moves Implement the MVE narrowing move insns VMOVN, VQMOVN and VQMOVUN. These take a double-width input, narrow it (possibly saturating) and store the result to either the top or bottom half of the output element. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 20 ++++++++++ target/arm/mve.decode | 12 ++++++ target/arm/mve_helper.c | 78 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-mve.c | 22 +++++++++++ 4 files changed, 132 insertions(+) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index c7e7aab2cb..17484f7432 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -76,6 +76,26 @@ DEF_HELPER_FLAGS_3(mve_vnegw, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vfnegh, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vfnegs, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vmovnbb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vmovnbh, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vmovntb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vmovnth, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vqmovunbb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqmovunbh, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqmovuntb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqmovunth, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vqmovnbsb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqmovnbsh, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqmovntsb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqmovntsh, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vqmovnbub, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqmovnbuh, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqmovntub, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqmovntuh, TCG_CALL_NO_WG, void, env, ptr, ptr) + DEF_HELPER_FLAGS_4(mve_vand, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vbic, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vorr, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index c8a06edca7..d295a693b1 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -153,6 +153,9 @@ VMUL 1110 1111 0 . .. ... 0 ... 0 1001 . 1 . 1 ... 0 @2op VSHLL_BS 111 0 1110 0 . 11 .. 01 ... 0 1110 0 0 . 0 ... 1 @2_shll_esize_b VSHLL_BS 111 0 1110 0 . 11 .. 01 ... 0 1110 0 0 . 0 ... 1 @2_shll_esize_h + VQMOVUNB 111 0 1110 0 . 11 .. 01 ... 0 1110 1 0 . 0 ... 1 @1op + VQMOVN_BS 111 0 1110 0 . 11 .. 11 ... 0 1110 0 0 . 0 ... 1 @1op + VMULH_S 111 0 1110 0 . .. ...1 ... 0 1110 . 0 . 0 ... 1 @2op } @@ -160,6 +163,9 @@ VMUL 1110 1111 0 . .. ... 0 ... 0 1001 . 1 . 1 ... 0 @2op VSHLL_BU 111 1 1110 0 . 11 .. 01 ... 0 1110 0 0 . 0 ... 1 @2_shll_esize_b VSHLL_BU 111 1 1110 0 . 11 .. 01 ... 0 1110 0 0 . 0 ... 1 @2_shll_esize_h + VMOVNB 111 1 1110 0 . 11 .. 01 ... 0 1110 1 0 . 0 ... 1 @1op + VQMOVN_BU 111 1 1110 0 . 11 .. 11 ... 0 1110 0 0 . 0 ... 1 @1op + VMULH_U 111 1 1110 0 . .. ...1 ... 0 1110 . 0 . 0 ... 1 @2op } @@ -167,6 +173,9 @@ VMUL 1110 1111 0 . .. ... 0 ... 0 1001 . 1 . 1 ... 0 @2op VSHLL_TS 111 0 1110 0 . 11 .. 01 ... 1 1110 0 0 . 0 ... 1 @2_shll_esize_b VSHLL_TS 111 0 1110 0 . 11 .. 01 ... 1 1110 0 0 . 0 ... 1 @2_shll_esize_h + VQMOVUNT 111 0 1110 0 . 11 .. 01 ... 1 1110 1 0 . 0 ... 1 @1op + VQMOVN_TS 111 0 1110 0 . 11 .. 11 ... 1 1110 0 0 . 0 ... 1 @1op + VRMULH_S 111 0 1110 0 . .. ...1 ... 1 1110 . 0 . 0 ... 1 @2op } @@ -174,6 +183,9 @@ VMUL 1110 1111 0 . .. ... 0 ... 0 1001 . 1 . 1 ... 0 @2op VSHLL_TU 111 1 1110 0 . 11 .. 01 ... 1 1110 0 0 . 0 ... 1 @2_shll_esize_b VSHLL_TU 111 1 1110 0 . 11 .. 01 ... 1 1110 0 0 . 0 ... 1 @2_shll_esize_h + VMOVNT 111 1 1110 0 . 11 .. 01 ... 1 1110 1 0 . 0 ... 1 @1op + VQMOVN_TU 111 1 1110 0 . 11 .. 11 ... 1 1110 0 0 . 0 ... 1 @1op + VRMULH_U 111 1 1110 0 . .. ...1 ... 1 1110 . 0 . 0 ... 1 @2op } diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index fed0f3cd61..72c30f360a 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1650,6 +1650,84 @@ DO_VSHRN_SAT_UH(vqrshrnb_uh, vqrshrnt_uh, DO_RSHRN_UH) DO_VSHRN_SAT_SB(vqrshrunbb, vqrshruntb, DO_RSHRUN_B) DO_VSHRN_SAT_SH(vqrshrunbh, vqrshrunth, DO_RSHRUN_H) +#define DO_VMOVN(OP, TOP, ESIZE, TYPE, LESIZE, LTYPE) \ + void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm) \ + { \ + LTYPE *m = vm; \ + TYPE *d = vd; \ + uint16_t mask = mve_element_mask(env); \ + unsigned le; \ + mask >>= ESIZE * TOP; \ + for (le = 0; le < 16 / LESIZE; le++, mask >>= LESIZE) { \ + mergemask(&d[H##ESIZE(le * 2 + TOP)], \ + m[H##LESIZE(le)], mask); \ + } \ + mve_advance_vpt(env); \ + } + +DO_VMOVN(vmovnbb, false, 1, uint8_t, 2, uint16_t) +DO_VMOVN(vmovnbh, false, 2, uint16_t, 4, uint32_t) +DO_VMOVN(vmovntb, true, 1, uint8_t, 2, uint16_t) +DO_VMOVN(vmovnth, true, 2, uint16_t, 4, uint32_t) + +#define DO_VMOVN_SAT(OP, TOP, ESIZE, TYPE, LESIZE, LTYPE, FN) \ + void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm) \ + { \ + LTYPE *m = vm; \ + TYPE *d = vd; \ + uint16_t mask = mve_element_mask(env); \ + bool qc = false; \ + unsigned le; \ + mask >>= ESIZE * TOP; \ + for (le = 0; le < 16 / LESIZE; le++, mask >>= LESIZE) { \ + bool sat = false; \ + TYPE r = FN(m[H##LESIZE(le)], &sat); \ + mergemask(&d[H##ESIZE(le * 2 + TOP)], r, mask); \ + qc |= sat & mask & 1; \ + } \ + if (qc) { \ + env->vfp.qc[0] = qc; \ + } \ + mve_advance_vpt(env); \ + } + +#define DO_VMOVN_SAT_UB(BOP, TOP, FN) \ + DO_VMOVN_SAT(BOP, false, 1, uint8_t, 2, uint16_t, FN) \ + DO_VMOVN_SAT(TOP, true, 1, uint8_t, 2, uint16_t, FN) + +#define DO_VMOVN_SAT_UH(BOP, TOP, FN) \ + DO_VMOVN_SAT(BOP, false, 2, uint16_t, 4, uint32_t, FN) \ + DO_VMOVN_SAT(TOP, true, 2, uint16_t, 4, uint32_t, FN) + +#define DO_VMOVN_SAT_SB(BOP, TOP, FN) \ + DO_VMOVN_SAT(BOP, false, 1, int8_t, 2, int16_t, FN) \ + DO_VMOVN_SAT(TOP, true, 1, int8_t, 2, int16_t, FN) + +#define DO_VMOVN_SAT_SH(BOP, TOP, FN) \ + DO_VMOVN_SAT(BOP, false, 2, int16_t, 4, int32_t, FN) \ + DO_VMOVN_SAT(TOP, true, 2, int16_t, 4, int32_t, FN) + +#define DO_VQMOVN_SB(N, SATP) \ + do_sat_bhs((int64_t)(N), INT8_MIN, INT8_MAX, SATP) +#define DO_VQMOVN_UB(N, SATP) \ + do_sat_bhs((uint64_t)(N), 0, UINT8_MAX, SATP) +#define DO_VQMOVUN_B(N, SATP) \ + do_sat_bhs((int64_t)(N), 0, UINT8_MAX, SATP) + +#define DO_VQMOVN_SH(N, SATP) \ + do_sat_bhs((int64_t)(N), INT16_MIN, INT16_MAX, SATP) +#define DO_VQMOVN_UH(N, SATP) \ + do_sat_bhs((uint64_t)(N), 0, UINT16_MAX, SATP) +#define DO_VQMOVUN_H(N, SATP) \ + do_sat_bhs((int64_t)(N), 0, UINT16_MAX, SATP) + +DO_VMOVN_SAT_SB(vqmovnbsb, vqmovntsb, DO_VQMOVN_SB) +DO_VMOVN_SAT_SH(vqmovnbsh, vqmovntsh, DO_VQMOVN_SH) +DO_VMOVN_SAT_UB(vqmovnbub, vqmovntub, DO_VQMOVN_UB) +DO_VMOVN_SAT_UH(vqmovnbuh, vqmovntuh, DO_VQMOVN_UH) +DO_VMOVN_SAT_SB(vqmovunbb, vqmovuntb, DO_VQMOVUN_B) +DO_VMOVN_SAT_SH(vqmovunbh, vqmovunth, DO_VQMOVUN_H) + uint32_t HELPER(mve_vshlc)(CPUARMState *env, void *vd, uint32_t rdm, uint32_t shift) { diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 247f6719e6..5c3655efc3 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -275,6 +275,28 @@ DO_1OP(VCLS, vcls) DO_1OP(VABS, vabs) DO_1OP(VNEG, vneg) +/* Narrowing moves: only size 0 and 1 are valid */ +#define DO_VMOVN(INSN, FN) \ + static bool trans_##INSN(DisasContext *s, arg_1op *a) \ + { \ + static MVEGenOneOpFn * const fns[] = { \ + gen_helper_mve_##FN##b, \ + gen_helper_mve_##FN##h, \ + NULL, \ + NULL, \ + }; \ + return do_1op(s, a, fns[a->size]); \ + } + +DO_VMOVN(VMOVNB, vmovnb) +DO_VMOVN(VMOVNT, vmovnt) +DO_VMOVN(VQMOVUNB, vqmovunb) +DO_VMOVN(VQMOVUNT, vqmovunt) +DO_VMOVN(VQMOVN_BS, vqmovnbs) +DO_VMOVN(VQMOVN_TS, vqmovnts) +DO_VMOVN(VQMOVN_BU, vqmovnbu) +DO_VMOVN(VQMOVN_TU, vqmovntu) + static bool trans_VREV16(DisasContext *s, arg_1op *a) { static MVEGenOneOpFn * const fns[] = { From 640cdf20a25d0021f4e93b6207b648a973df320b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:53 +0100 Subject: [PATCH 029/493] target/arm: Rename MVEGenDualAccOpFn to MVEGenLongDualAccOpFn The MVEGenDualAccOpFn is a bit misnamed, since it is used for the "long dual accumulate" operations that use a 64-bit accumulator. Rename it to MVEGenLongDualAccOpFn so we can use the former name for the 32-bit accumulator insns. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/translate-mve.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 5c3655efc3..676411e05c 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -38,7 +38,7 @@ typedef void MVEGenOneOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); typedef void MVEGenTwoOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr); typedef void MVEGenTwoOpScalarFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void MVEGenTwoOpShiftFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); -typedef void MVEGenDualAccOpFn(TCGv_i64, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i64); +typedef void MVEGenLongDualAccOpFn(TCGv_i64, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i64); typedef void MVEGenVADDVFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void MVEGenOneOpImmFn(TCGv_ptr, TCGv_ptr, TCGv_i64); typedef void MVEGenVIDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32); @@ -652,7 +652,7 @@ static bool trans_VQDMULLT_scalar(DisasContext *s, arg_2scalar *a) } static bool do_long_dual_acc(DisasContext *s, arg_vmlaldav *a, - MVEGenDualAccOpFn *fn) + MVEGenLongDualAccOpFn *fn) { TCGv_ptr qn, qm; TCGv_i64 rda; @@ -710,7 +710,7 @@ static bool do_long_dual_acc(DisasContext *s, arg_vmlaldav *a, static bool trans_VMLALDAV_S(DisasContext *s, arg_vmlaldav *a) { - static MVEGenDualAccOpFn * const fns[4][2] = { + static MVEGenLongDualAccOpFn * const fns[4][2] = { { NULL, NULL }, { gen_helper_mve_vmlaldavsh, gen_helper_mve_vmlaldavxsh }, { gen_helper_mve_vmlaldavsw, gen_helper_mve_vmlaldavxsw }, @@ -721,7 +721,7 @@ static bool trans_VMLALDAV_S(DisasContext *s, arg_vmlaldav *a) static bool trans_VMLALDAV_U(DisasContext *s, arg_vmlaldav *a) { - static MVEGenDualAccOpFn * const fns[4][2] = { + static MVEGenLongDualAccOpFn * const fns[4][2] = { { NULL, NULL }, { gen_helper_mve_vmlaldavuh, NULL }, { gen_helper_mve_vmlaldavuw, NULL }, @@ -732,7 +732,7 @@ static bool trans_VMLALDAV_U(DisasContext *s, arg_vmlaldav *a) static bool trans_VMLSLDAV(DisasContext *s, arg_vmlaldav *a) { - static MVEGenDualAccOpFn * const fns[4][2] = { + static MVEGenLongDualAccOpFn * const fns[4][2] = { { NULL, NULL }, { gen_helper_mve_vmlsldavsh, gen_helper_mve_vmlsldavxsh }, { gen_helper_mve_vmlsldavsw, gen_helper_mve_vmlsldavxsw }, @@ -743,7 +743,7 @@ static bool trans_VMLSLDAV(DisasContext *s, arg_vmlaldav *a) static bool trans_VRMLALDAVH_S(DisasContext *s, arg_vmlaldav *a) { - static MVEGenDualAccOpFn * const fns[] = { + static MVEGenLongDualAccOpFn * const fns[] = { gen_helper_mve_vrmlaldavhsw, gen_helper_mve_vrmlaldavhxsw, }; return do_long_dual_acc(s, a, fns[a->x]); @@ -751,7 +751,7 @@ static bool trans_VRMLALDAVH_S(DisasContext *s, arg_vmlaldav *a) static bool trans_VRMLALDAVH_U(DisasContext *s, arg_vmlaldav *a) { - static MVEGenDualAccOpFn * const fns[] = { + static MVEGenLongDualAccOpFn * const fns[] = { gen_helper_mve_vrmlaldavhuw, NULL, }; return do_long_dual_acc(s, a, fns[a->x]); @@ -759,7 +759,7 @@ static bool trans_VRMLALDAVH_U(DisasContext *s, arg_vmlaldav *a) static bool trans_VRMLSLDAVH(DisasContext *s, arg_vmlaldav *a) { - static MVEGenDualAccOpFn * const fns[] = { + static MVEGenLongDualAccOpFn * const fns[] = { gen_helper_mve_vrmlsldavhsw, gen_helper_mve_vrmlsldavhxsw, }; return do_long_dual_acc(s, a, fns[a->x]); From f0ffff5163cb503de236fc766121601592f08744 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:54 +0100 Subject: [PATCH 030/493] target/arm: Implement MVE VMLADAV and VMLSLDAV Implement the MVE VMLADAV and VMLSLDAV insns. Like the VMLALDAV and VMLSLDAV insns already implemented, these accumulate multiplied vector elements; but they accumulate a 32-bit result rather than a 64-bit one. Note that these encodings overlap with what would be RdaHi=0b111 for VMLALDAV, VMLSLDAV, VRMLALDAVH and VRMLSLDAVH. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 17 ++++++++++ target/arm/mve.decode | 33 +++++++++++++++++--- target/arm/mve_helper.c | 41 ++++++++++++++++++++++++ target/arm/translate-mve.c | 64 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 5 deletions(-) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 17484f7432..34d644a519 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -392,6 +392,23 @@ DEF_HELPER_FLAGS_4(mve_vrmlaldavhuw, TCG_CALL_NO_WG, i64, env, ptr, ptr, i64) DEF_HELPER_FLAGS_4(mve_vrmlsldavhsw, TCG_CALL_NO_WG, i64, env, ptr, ptr, i64) DEF_HELPER_FLAGS_4(mve_vrmlsldavhxsw, TCG_CALL_NO_WG, i64, env, ptr, ptr, i64) +DEF_HELPER_FLAGS_4(mve_vmladavsb, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmladavsh, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmladavsw, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmladavub, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmladavuh, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmladavuw, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlsdavb, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlsdavh, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlsdavw, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(mve_vmladavsxb, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmladavsxh, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmladavsxw, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlsdavxb, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlsdavxh, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlsdavxw, TCG_CALL_NO_WG, i32, env, ptr, ptr, i32) + DEF_HELPER_FLAGS_3(mve_vaddvsb, TCG_CALL_NO_WG, i32, env, ptr, i32) DEF_HELPER_FLAGS_3(mve_vaddvub, TCG_CALL_NO_WG, i32, env, ptr, i32) DEF_HELPER_FLAGS_3(mve_vaddvsh, TCG_CALL_NO_WG, i32, env, ptr, i32) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index d295a693b1..cec5a51b0e 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -320,32 +320,55 @@ VDUP 1110 1110 1 0 10 ... 0 .... 1011 . 0 0 1 0000 @vdup size=2 %size_16 16:1 !function=plus_1 &vmlaldav rdahi rdalo size qn qm x a +&vmladav rda size qn qm x a @vmlaldav .... .... . ... ... . ... x:1 .... .. a:1 . qm:3 . \ qn=%qn rdahi=%rdahi rdalo=%rdalo size=%size_16 &vmlaldav @vmlaldav_nosz .... .... . ... ... . ... x:1 .... .. a:1 . qm:3 . \ qn=%qn rdahi=%rdahi rdalo=%rdalo size=0 &vmlaldav -VMLALDAV_S 1110 1110 1 ... ... . ... . 1110 . 0 . 0 ... 0 @vmlaldav -VMLALDAV_U 1111 1110 1 ... ... . ... . 1110 . 0 . 0 ... 0 @vmlaldav +@vmladav .... .... .... ... . ... x:1 .... . . a:1 . qm:3 . \ + qn=%qn rda=%rdalo size=%size_16 &vmladav +@vmladav_nosz .... .... .... ... . ... x:1 .... . . a:1 . qm:3 . \ + qn=%qn rda=%rdalo size=0 &vmladav -VMLSLDAV 1110 1110 1 ... ... . ... . 1110 . 0 . 0 ... 1 @vmlaldav +{ + VMLADAV_S 1110 1110 1111 ... . ... . 1110 . 0 . 0 ... 0 @vmladav + VMLALDAV_S 1110 1110 1 ... ... . ... . 1110 . 0 . 0 ... 0 @vmlaldav +} +{ + VMLADAV_U 1111 1110 1111 ... . ... . 1110 . 0 . 0 ... 0 @vmladav + VMLALDAV_U 1111 1110 1 ... ... . ... . 1110 . 0 . 0 ... 0 @vmlaldav +} + +{ + VMLSDAV 1110 1110 1111 ... . ... . 1110 . 0 . 0 ... 1 @vmladav + VMLSLDAV 1110 1110 1 ... ... . ... . 1110 . 0 . 0 ... 1 @vmlaldav +} + +{ + VMLSDAV 1111 1110 1111 ... 0 ... . 1110 . 0 . 0 ... 1 @vmladav_nosz + VRMLSLDAVH 1111 1110 1 ... ... 0 ... . 1110 . 0 . 0 ... 1 @vmlaldav_nosz +} + +VMLADAV_S 1110 1110 1111 ... 0 ... . 1111 . 0 . 0 ... 1 @vmladav_nosz +VMLADAV_U 1111 1110 1111 ... 0 ... . 1111 . 0 . 0 ... 1 @vmladav_nosz { VMAXV_S 1110 1110 1110 .. 10 .... 1111 0 0 . 0 ... 0 @vmaxv VMINV_S 1110 1110 1110 .. 10 .... 1111 1 0 . 0 ... 0 @vmaxv VMAXAV 1110 1110 1110 .. 00 .... 1111 0 0 . 0 ... 0 @vmaxv VMINAV 1110 1110 1110 .. 00 .... 1111 1 0 . 0 ... 0 @vmaxv + VMLADAV_S 1110 1110 1111 ... 0 ... . 1111 . 0 . 0 ... 0 @vmladav_nosz VRMLALDAVH_S 1110 1110 1 ... ... 0 ... . 1111 . 0 . 0 ... 0 @vmlaldav_nosz } { VMAXV_U 1111 1110 1110 .. 10 .... 1111 0 0 . 0 ... 0 @vmaxv VMINV_U 1111 1110 1110 .. 10 .... 1111 1 0 . 0 ... 0 @vmaxv + VMLADAV_U 1111 1110 1111 ... 0 ... . 1111 . 0 . 0 ... 0 @vmladav_nosz VRMLALDAVH_U 1111 1110 1 ... ... 0 ... . 1111 . 0 . 0 ... 0 @vmlaldav_nosz } -VRMLSLDAVH 1111 1110 1 ... ... 0 ... . 1110 . 0 . 0 ... 1 @vmlaldav_nosz - # Scalar operations VADD_scalar 1110 1110 0 . .. ... 1 ... 0 1111 . 100 .... @2scalar diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 72c30f360a..ea206c932b 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1189,6 +1189,47 @@ DO_LDAV(vmlsldavxsh, 2, int16_t, true, +=, -=) DO_LDAV(vmlsldavsw, 4, int32_t, false, +=, -=) DO_LDAV(vmlsldavxsw, 4, int32_t, true, +=, -=) +/* + * Multiply add dual accumulate ops + */ +#define DO_DAV(OP, ESIZE, TYPE, XCHG, EVENACC, ODDACC) \ + uint32_t HELPER(glue(mve_, OP))(CPUARMState *env, void *vn, \ + void *vm, uint32_t a) \ + { \ + uint16_t mask = mve_element_mask(env); \ + unsigned e; \ + TYPE *n = vn, *m = vm; \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ + if (mask & 1) { \ + if (e & 1) { \ + a ODDACC \ + n[H##ESIZE(e - 1 * XCHG)] * m[H##ESIZE(e)]; \ + } else { \ + a EVENACC \ + n[H##ESIZE(e + 1 * XCHG)] * m[H##ESIZE(e)]; \ + } \ + } \ + } \ + mve_advance_vpt(env); \ + return a; \ + } + +#define DO_DAV_S(INSN, XCHG, EVENACC, ODDACC) \ + DO_DAV(INSN##b, 1, int8_t, XCHG, EVENACC, ODDACC) \ + DO_DAV(INSN##h, 2, int16_t, XCHG, EVENACC, ODDACC) \ + DO_DAV(INSN##w, 4, int32_t, XCHG, EVENACC, ODDACC) + +#define DO_DAV_U(INSN, XCHG, EVENACC, ODDACC) \ + DO_DAV(INSN##b, 1, uint8_t, XCHG, EVENACC, ODDACC) \ + DO_DAV(INSN##h, 2, uint16_t, XCHG, EVENACC, ODDACC) \ + DO_DAV(INSN##w, 4, uint32_t, XCHG, EVENACC, ODDACC) + +DO_DAV_S(vmladavs, false, +=, +=) +DO_DAV_U(vmladavu, false, +=, +=) +DO_DAV_S(vmlsdav, false, +=, -=) +DO_DAV_S(vmladavsx, true, +=, +=) +DO_DAV_S(vmlsdavx, true, +=, -=) + /* * Rounding multiply add long dual accumulate high. In the pseudocode * this is implemented with a 72-bit internal accumulator value of which diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 676411e05c..92ed1be83e 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -46,6 +46,7 @@ typedef void MVEGenVIWDUPFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32, TC typedef void MVEGenCmpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); typedef void MVEGenScalarCmpFn(TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void MVEGenVABAVFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); +typedef void MVEGenDualAccOpFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); /* Return the offset of a Qn register (same semantics as aa32_vfp_qreg()) */ static inline long mve_qreg_offset(unsigned reg) @@ -765,6 +766,69 @@ static bool trans_VRMLSLDAVH(DisasContext *s, arg_vmlaldav *a) return do_long_dual_acc(s, a, fns[a->x]); } +static bool do_dual_acc(DisasContext *s, arg_vmladav *a, MVEGenDualAccOpFn *fn) +{ + TCGv_ptr qn, qm; + TCGv_i32 rda; + + if (!dc_isar_feature(aa32_mve, s) || + !mve_check_qreg_bank(s, a->qn) || + !fn) { + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + qn = mve_qreg_ptr(a->qn); + qm = mve_qreg_ptr(a->qm); + + /* + * This insn is subject to beat-wise execution. Partial execution + * of an A=0 (no-accumulate) insn which does not execute the first + * beat must start with the current rda value, not 0. + */ + if (a->a || mve_skip_first_beat(s)) { + rda = load_reg(s, a->rda); + } else { + rda = tcg_const_i32(0); + } + + fn(rda, cpu_env, qn, qm, rda); + store_reg(s, a->rda, rda); + tcg_temp_free_ptr(qn); + tcg_temp_free_ptr(qm); + + mve_update_eci(s); + return true; +} + +#define DO_DUAL_ACC(INSN, FN) \ + static bool trans_##INSN(DisasContext *s, arg_vmladav *a) \ + { \ + static MVEGenDualAccOpFn * const fns[4][2] = { \ + { gen_helper_mve_##FN##b, gen_helper_mve_##FN##xb }, \ + { gen_helper_mve_##FN##h, gen_helper_mve_##FN##xh }, \ + { gen_helper_mve_##FN##w, gen_helper_mve_##FN##xw }, \ + { NULL, NULL }, \ + }; \ + return do_dual_acc(s, a, fns[a->size][a->x]); \ + } + +DO_DUAL_ACC(VMLADAV_S, vmladavs) +DO_DUAL_ACC(VMLSDAV, vmlsdav) + +static bool trans_VMLADAV_U(DisasContext *s, arg_vmladav *a) +{ + static MVEGenDualAccOpFn * const fns[4][2] = { + { gen_helper_mve_vmladavub, NULL }, + { gen_helper_mve_vmladavuh, NULL }, + { gen_helper_mve_vmladavuw, NULL }, + { NULL, NULL }, + }; + return do_dual_acc(s, a, fns[a->size][a->x]); +} + static void gen_vpst(DisasContext *s, uint32_t mask) { /* From c69e34c6debfb567f6118b59e6efa96a20765dda Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:54 +0100 Subject: [PATCH 031/493] target/arm: Implement MVE VMLA Implement the MVE VMLA insn, which multiplies a vector by a scalar and accumulates into another vector. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 4 ++++ target/arm/mve.decode | 1 + target/arm/mve_helper.c | 5 +++++ target/arm/translate-mve.c | 1 + 4 files changed, 11 insertions(+) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 34d644a519..328e31e266 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -367,6 +367,10 @@ DEF_HELPER_FLAGS_4(mve_vqdmullb_scalarw, TCG_CALL_NO_WG, void, env, ptr, ptr, i3 DEF_HELPER_FLAGS_4(mve_vqdmullt_scalarh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vqdmullt_scalarw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlab, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlah, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vmlaw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(mve_vmlasb, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vmlash, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vmlasw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index cec5a51b0e..cd9c806a11 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -413,6 +413,7 @@ VQDMULH_scalar 1110 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar VQRDMULH_scalar 1111 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar # The U bit (28) is don't-care because it does not affect the result +VMLA 111- 1110 0 . .. ... 1 ... 0 1110 . 100 .... @2scalar VMLAS 111- 1110 0 . .. ... 1 ... 1 1110 . 100 .... @2scalar # Vector add across vector diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index ea206c932b..8004b9bb72 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -1008,6 +1008,11 @@ DO_2OP_SAT_SCALAR(vqrdmulh_scalarb, 1, int8_t, DO_QRDMULH_B) DO_2OP_SAT_SCALAR(vqrdmulh_scalarh, 2, int16_t, DO_QRDMULH_H) DO_2OP_SAT_SCALAR(vqrdmulh_scalarw, 4, int32_t, DO_QRDMULH_W) +/* Vector by scalar plus vector */ +#define DO_VMLA(D, N, M) ((N) * (M) + (D)) + +DO_2OP_ACC_SCALAR_U(vmla, DO_VMLA) + /* Vector by vector plus scalar */ #define DO_VMLAS(D, N, M) ((N) * (D) + (M)) diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 92ed1be83e..f8899af352 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -620,6 +620,7 @@ DO_2OP_SCALAR(VQSUB_U_scalar, vqsubu_scalar) DO_2OP_SCALAR(VQDMULH_scalar, vqdmulh_scalar) DO_2OP_SCALAR(VQRDMULH_scalar, vqrdmulh_scalar) DO_2OP_SCALAR(VBRSR, vbrsr) +DO_2OP_SCALAR(VMLA, vmla) DO_2OP_SCALAR(VMLAS, vmlas) static bool trans_VQDMULLB_scalar(DisasContext *s, arg_2scalar *a) From 8be9a25058f9a5505d6864f06de86ee01d42fc59 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:54 +0100 Subject: [PATCH 032/493] target/arm: Implement MVE saturating doubling multiply accumulates Implement the MVE saturating doubling multiply accumulate insns VQDMLAH, VQRDMLAH, VQDMLASH and VQRDMLASH. These perform a multiply, double, add the accumulator shifted by the element size, possibly round, saturate to twice the element size, then take the high half of the result. The *MLAH insns do vector * scalar + vector, and the *MLASH insns do vector * vector + scalar. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 16 +++++++ target/arm/mve.decode | 5 ++ target/arm/mve_helper.c | 95 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-mve.c | 4 ++ 4 files changed, 120 insertions(+) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 328e31e266..2f54396b2d 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -375,6 +375,22 @@ DEF_HELPER_FLAGS_4(mve_vmlasb, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vmlash, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vmlasw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqdmlahb, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqdmlahh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqdmlahw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(mve_vqrdmlahb, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqrdmlahh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqrdmlahw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(mve_vqdmlashb, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqdmlashh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqdmlashw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(mve_vqrdmlashb, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqrdmlashh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vqrdmlashw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(mve_vmlaldavsh, TCG_CALL_NO_WG, i64, env, ptr, ptr, i64) DEF_HELPER_FLAGS_4(mve_vmlaldavsw, TCG_CALL_NO_WG, i64, env, ptr, ptr, i64) DEF_HELPER_FLAGS_4(mve_vmlaldavxsh, TCG_CALL_NO_WG, i64, env, ptr, ptr, i64) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index cd9c806a11..7a6de3991b 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -416,6 +416,11 @@ VQRDMULH_scalar 1111 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar VMLA 111- 1110 0 . .. ... 1 ... 0 1110 . 100 .... @2scalar VMLAS 111- 1110 0 . .. ... 1 ... 1 1110 . 100 .... @2scalar +VQRDMLAH 1110 1110 0 . .. ... 0 ... 0 1110 . 100 .... @2scalar +VQRDMLASH 1110 1110 0 . .. ... 0 ... 1 1110 . 100 .... @2scalar +VQDMLAH 1110 1110 0 . .. ... 0 ... 0 1110 . 110 .... @2scalar +VQDMLASH 1110 1110 0 . .. ... 0 ... 1 1110 . 110 .... @2scalar + # Vector add across vector { VADDV 111 u:1 1110 1111 size:2 01 ... 0 1111 0 0 a:1 0 qm:3 0 rda=%rdalo diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 8004b9bb72..a69fcd2243 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -964,6 +964,28 @@ DO_VQDMLADH_OP(vqrdmlsdhxw, 4, int32_t, 1, 1, do_vqdmlsdh_w) mve_advance_vpt(env); \ } +#define DO_2OP_SAT_ACC_SCALAR(OP, ESIZE, TYPE, FN) \ + void HELPER(glue(mve_, OP))(CPUARMState *env, void *vd, void *vn, \ + uint32_t rm) \ + { \ + TYPE *d = vd, *n = vn; \ + TYPE m = rm; \ + uint16_t mask = mve_element_mask(env); \ + unsigned e; \ + bool qc = false; \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ + bool sat = false; \ + mergemask(&d[H##ESIZE(e)], \ + FN(d[H##ESIZE(e)], n[H##ESIZE(e)], m, &sat), \ + mask); \ + qc |= sat & mask & 1; \ + } \ + if (qc) { \ + env->vfp.qc[0] = qc; \ + } \ + mve_advance_vpt(env); \ + } + /* provide unsigned 2-op scalar helpers for all sizes */ #define DO_2OP_SCALAR_U(OP, FN) \ DO_2OP_SCALAR(OP##b, 1, uint8_t, FN) \ @@ -1008,6 +1030,79 @@ DO_2OP_SAT_SCALAR(vqrdmulh_scalarb, 1, int8_t, DO_QRDMULH_B) DO_2OP_SAT_SCALAR(vqrdmulh_scalarh, 2, int16_t, DO_QRDMULH_H) DO_2OP_SAT_SCALAR(vqrdmulh_scalarw, 4, int32_t, DO_QRDMULH_W) +static int8_t do_vqdmlah_b(int8_t a, int8_t b, int8_t c, int round, bool *sat) +{ + int64_t r = (int64_t)a * b * 2 + ((int64_t)c << 8) + (round << 7); + return do_sat_bhw(r, INT16_MIN, INT16_MAX, sat) >> 8; +} + +static int16_t do_vqdmlah_h(int16_t a, int16_t b, int16_t c, + int round, bool *sat) +{ + int64_t r = (int64_t)a * b * 2 + ((int64_t)c << 16) + (round << 15); + return do_sat_bhw(r, INT32_MIN, INT32_MAX, sat) >> 16; +} + +static int32_t do_vqdmlah_w(int32_t a, int32_t b, int32_t c, + int round, bool *sat) +{ + /* + * Architecturally we should do the entire add, double, round + * and then check for saturation. We do three saturating adds, + * but we need to be careful about the order. If the first + * m1 + m2 saturates then it's impossible for the *2+rc to + * bring it back into the non-saturated range. However, if + * m1 + m2 is negative then it's possible that doing the doubling + * would take the intermediate result below INT64_MAX and the + * addition of the rounding constant then brings it back in range. + * So we add half the rounding constant and half the "c << esize" + * before doubling rather than adding the rounding constant after + * the doubling. + */ + int64_t m1 = (int64_t)a * b; + int64_t m2 = (int64_t)c << 31; + int64_t r; + if (sadd64_overflow(m1, m2, &r) || + sadd64_overflow(r, (round << 30), &r) || + sadd64_overflow(r, r, &r)) { + *sat = true; + return r < 0 ? INT32_MAX : INT32_MIN; + } + return r >> 32; +} + +/* + * The *MLAH insns are vector * scalar + vector; + * the *MLASH insns are vector * vector + scalar + */ +#define DO_VQDMLAH_B(D, N, M, S) do_vqdmlah_b(N, M, D, 0, S) +#define DO_VQDMLAH_H(D, N, M, S) do_vqdmlah_h(N, M, D, 0, S) +#define DO_VQDMLAH_W(D, N, M, S) do_vqdmlah_w(N, M, D, 0, S) +#define DO_VQRDMLAH_B(D, N, M, S) do_vqdmlah_b(N, M, D, 1, S) +#define DO_VQRDMLAH_H(D, N, M, S) do_vqdmlah_h(N, M, D, 1, S) +#define DO_VQRDMLAH_W(D, N, M, S) do_vqdmlah_w(N, M, D, 1, S) + +#define DO_VQDMLASH_B(D, N, M, S) do_vqdmlah_b(N, D, M, 0, S) +#define DO_VQDMLASH_H(D, N, M, S) do_vqdmlah_h(N, D, M, 0, S) +#define DO_VQDMLASH_W(D, N, M, S) do_vqdmlah_w(N, D, M, 0, S) +#define DO_VQRDMLASH_B(D, N, M, S) do_vqdmlah_b(N, D, M, 1, S) +#define DO_VQRDMLASH_H(D, N, M, S) do_vqdmlah_h(N, D, M, 1, S) +#define DO_VQRDMLASH_W(D, N, M, S) do_vqdmlah_w(N, D, M, 1, S) + +DO_2OP_SAT_ACC_SCALAR(vqdmlahb, 1, int8_t, DO_VQDMLAH_B) +DO_2OP_SAT_ACC_SCALAR(vqdmlahh, 2, int16_t, DO_VQDMLAH_H) +DO_2OP_SAT_ACC_SCALAR(vqdmlahw, 4, int32_t, DO_VQDMLAH_W) +DO_2OP_SAT_ACC_SCALAR(vqrdmlahb, 1, int8_t, DO_VQRDMLAH_B) +DO_2OP_SAT_ACC_SCALAR(vqrdmlahh, 2, int16_t, DO_VQRDMLAH_H) +DO_2OP_SAT_ACC_SCALAR(vqrdmlahw, 4, int32_t, DO_VQRDMLAH_W) + +DO_2OP_SAT_ACC_SCALAR(vqdmlashb, 1, int8_t, DO_VQDMLASH_B) +DO_2OP_SAT_ACC_SCALAR(vqdmlashh, 2, int16_t, DO_VQDMLASH_H) +DO_2OP_SAT_ACC_SCALAR(vqdmlashw, 4, int32_t, DO_VQDMLASH_W) +DO_2OP_SAT_ACC_SCALAR(vqrdmlashb, 1, int8_t, DO_VQRDMLASH_B) +DO_2OP_SAT_ACC_SCALAR(vqrdmlashh, 2, int16_t, DO_VQRDMLASH_H) +DO_2OP_SAT_ACC_SCALAR(vqrdmlashw, 4, int32_t, DO_VQRDMLASH_W) + /* Vector by scalar plus vector */ #define DO_VMLA(D, N, M) ((N) * (M) + (D)) diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index f8899af352..e3e115c1aa 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -622,6 +622,10 @@ DO_2OP_SCALAR(VQRDMULH_scalar, vqrdmulh_scalar) DO_2OP_SCALAR(VBRSR, vbrsr) DO_2OP_SCALAR(VMLA, vmla) DO_2OP_SCALAR(VMLAS, vmlas) +DO_2OP_SCALAR(VQDMLAH, vqdmlah) +DO_2OP_SCALAR(VQRDMLAH, vqrdmlah) +DO_2OP_SCALAR(VQDMLASH, vqdmlash) +DO_2OP_SCALAR(VQRDMLASH, vqrdmlash) static bool trans_VQDMULLB_scalar(DisasContext *s, arg_2scalar *a) { From 398e7cd3cd7a82eb04d236c7e30171f058f234b7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:55 +0100 Subject: [PATCH 033/493] target/arm: Implement MVE VQABS, VQNEG Implement the MVE 1-operand saturating operations VQABS and VQNEG. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 8 ++++++++ target/arm/mve.decode | 3 +++ target/arm/mve_helper.c | 37 +++++++++++++++++++++++++++++++++++++ target/arm/translate-mve.c | 2 ++ 4 files changed, 50 insertions(+) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 2f54396b2d..f9345bfafc 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -76,6 +76,14 @@ DEF_HELPER_FLAGS_3(mve_vnegw, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vfnegh, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vfnegs, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqabsb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqabsh, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqabsw, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vqnegb, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqnegh, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vqnegw, TCG_CALL_NO_WG, void, env, ptr, ptr) + DEF_HELPER_FLAGS_3(mve_vmovnbb, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vmovnbh, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vmovntb, TCG_CALL_NO_WG, void, env, ptr, ptr) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 7a6de3991b..a05b882f9d 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -279,6 +279,9 @@ VABS_fp 1111 1111 1 . 11 .. 01 ... 0 0111 01 . 0 ... 0 @1op VNEG 1111 1111 1 . 11 .. 01 ... 0 0011 11 . 0 ... 0 @1op VNEG_fp 1111 1111 1 . 11 .. 01 ... 0 0111 11 . 0 ... 0 @1op +VQABS 1111 1111 1 . 11 .. 00 ... 0 0111 01 . 0 ... 0 @1op +VQNEG 1111 1111 1 . 11 .. 00 ... 0 0111 11 . 0 ... 0 @1op + &vdup qd rt size # Qd is in the fields usually named Qn @vdup .... .... . . .. ... . rt:4 .... . . . . .... qd=%qn &vdup diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index a69fcd2243..6539012ddd 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -2200,3 +2200,40 @@ void HELPER(mve_vpsel)(CPUARMState *env, void *vd, void *vn, void *vm) } mve_advance_vpt(env); } + +#define DO_1OP_SAT(OP, ESIZE, TYPE, FN) \ + void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm) \ + { \ + TYPE *d = vd, *m = vm; \ + uint16_t mask = mve_element_mask(env); \ + unsigned e; \ + bool qc = false; \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ + bool sat = false; \ + mergemask(&d[H##ESIZE(e)], FN(m[H##ESIZE(e)], &sat), mask); \ + qc |= sat & mask & 1; \ + } \ + if (qc) { \ + env->vfp.qc[0] = qc; \ + } \ + mve_advance_vpt(env); \ + } + +#define DO_VQABS_B(N, SATP) \ + do_sat_bhs(DO_ABS((int64_t)N), INT8_MIN, INT8_MAX, SATP) +#define DO_VQABS_H(N, SATP) \ + do_sat_bhs(DO_ABS((int64_t)N), INT16_MIN, INT16_MAX, SATP) +#define DO_VQABS_W(N, SATP) \ + do_sat_bhs(DO_ABS((int64_t)N), INT32_MIN, INT32_MAX, SATP) + +#define DO_VQNEG_B(N, SATP) do_sat_bhs(-(int64_t)N, INT8_MIN, INT8_MAX, SATP) +#define DO_VQNEG_H(N, SATP) do_sat_bhs(-(int64_t)N, INT16_MIN, INT16_MAX, SATP) +#define DO_VQNEG_W(N, SATP) do_sat_bhs(-(int64_t)N, INT32_MIN, INT32_MAX, SATP) + +DO_1OP_SAT(vqabsb, 1, int8_t, DO_VQABS_B) +DO_1OP_SAT(vqabsh, 2, int16_t, DO_VQABS_H) +DO_1OP_SAT(vqabsw, 4, int32_t, DO_VQABS_W) + +DO_1OP_SAT(vqnegb, 1, int8_t, DO_VQNEG_B) +DO_1OP_SAT(vqnegh, 2, int16_t, DO_VQNEG_H) +DO_1OP_SAT(vqnegw, 4, int32_t, DO_VQNEG_W) diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index e3e115c1aa..f2213ec8cd 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -275,6 +275,8 @@ DO_1OP(VCLZ, vclz) DO_1OP(VCLS, vcls) DO_1OP(VABS, vabs) DO_1OP(VNEG, vneg) +DO_1OP(VQABS, vqabs) +DO_1OP(VQNEG, vqneg) /* Narrowing moves: only size 0 and 1 are valid */ #define DO_VMOVN(INSN, FN) \ From d5c571ea6d1558934b0d1a95c51a2c084cf4fd85 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:55 +0100 Subject: [PATCH 034/493] target/arm: Implement MVE VMAXA, VMINA Implement the MVE VMAXA and VMINA insns, which take the absolute value of the signed elements in the input vector and then accumulate the unsigned max or min into the destination vector. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 8 ++++++++ target/arm/mve.decode | 4 ++++ target/arm/mve_helper.c | 26 ++++++++++++++++++++++++++ target/arm/translate-mve.c | 2 ++ 4 files changed, 40 insertions(+) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index f9345bfafc..651020aaad 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -84,6 +84,14 @@ DEF_HELPER_FLAGS_3(mve_vqnegb, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vqnegh, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vqnegw, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vmaxab, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vmaxah, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vmaxaw, TCG_CALL_NO_WG, void, env, ptr, ptr) + +DEF_HELPER_FLAGS_3(mve_vminab, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vminah, TCG_CALL_NO_WG, void, env, ptr, ptr) +DEF_HELPER_FLAGS_3(mve_vminaw, TCG_CALL_NO_WG, void, env, ptr, ptr) + DEF_HELPER_FLAGS_3(mve_vmovnbb, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vmovnbh, TCG_CALL_NO_WG, void, env, ptr, ptr) DEF_HELPER_FLAGS_3(mve_vmovntb, TCG_CALL_NO_WG, void, env, ptr, ptr) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index a05b882f9d..0955ed0cc2 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -156,6 +156,8 @@ VMUL 1110 1111 0 . .. ... 0 ... 0 1001 . 1 . 1 ... 0 @2op VQMOVUNB 111 0 1110 0 . 11 .. 01 ... 0 1110 1 0 . 0 ... 1 @1op VQMOVN_BS 111 0 1110 0 . 11 .. 11 ... 0 1110 0 0 . 0 ... 1 @1op + VMAXA 111 0 1110 0 . 11 .. 11 ... 0 1110 1 0 . 0 ... 1 @1op + VMULH_S 111 0 1110 0 . .. ...1 ... 0 1110 . 0 . 0 ... 1 @2op } @@ -176,6 +178,8 @@ VMUL 1110 1111 0 . .. ... 0 ... 0 1001 . 1 . 1 ... 0 @2op VQMOVUNT 111 0 1110 0 . 11 .. 01 ... 1 1110 1 0 . 0 ... 1 @1op VQMOVN_TS 111 0 1110 0 . 11 .. 11 ... 1 1110 0 0 . 0 ... 1 @1op + VMINA 111 0 1110 0 . 11 .. 11 ... 1 1110 1 0 . 0 ... 1 @1op + VRMULH_S 111 0 1110 0 . .. ...1 ... 1 1110 . 0 . 0 ... 1 @2op } diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 6539012ddd..d326205cbf 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -2237,3 +2237,29 @@ DO_1OP_SAT(vqabsw, 4, int32_t, DO_VQABS_W) DO_1OP_SAT(vqnegb, 1, int8_t, DO_VQNEG_B) DO_1OP_SAT(vqnegh, 2, int16_t, DO_VQNEG_H) DO_1OP_SAT(vqnegw, 4, int32_t, DO_VQNEG_W) + +/* + * VMAXA, VMINA: vd is unsigned; vm is signed, and we take its + * absolute value; we then do an unsigned comparison. + */ +#define DO_VMAXMINA(OP, ESIZE, STYPE, UTYPE, FN) \ + void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm) \ + { \ + UTYPE *d = vd; \ + STYPE *m = vm; \ + uint16_t mask = mve_element_mask(env); \ + unsigned e; \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ + UTYPE r = DO_ABS(m[H##ESIZE(e)]); \ + r = FN(d[H##ESIZE(e)], r); \ + mergemask(&d[H##ESIZE(e)], r, mask); \ + } \ + mve_advance_vpt(env); \ + } + +DO_VMAXMINA(vmaxab, 1, int8_t, uint8_t, DO_MAX) +DO_VMAXMINA(vmaxah, 2, int16_t, uint16_t, DO_MAX) +DO_VMAXMINA(vmaxaw, 4, int32_t, uint32_t, DO_MAX) +DO_VMAXMINA(vminab, 1, int8_t, uint8_t, DO_MIN) +DO_VMAXMINA(vminah, 2, int16_t, uint16_t, DO_MIN) +DO_VMAXMINA(vminaw, 4, int32_t, uint32_t, DO_MIN) diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index f2213ec8cd..02c26987a2 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -277,6 +277,8 @@ DO_1OP(VABS, vabs) DO_1OP(VNEG, vneg) DO_1OP(VQABS, vqabs) DO_1OP(VQNEG, vqneg) +DO_1OP(VMAXA, vmaxa) +DO_1OP(VMINA, vmina) /* Narrowing moves: only size 0 and 1 are valid */ #define DO_VMOVN(INSN, FN) \ From 1241f148d52eea7c9350df918da0eafdfc539327 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:55 +0100 Subject: [PATCH 035/493] target/arm: Implement MVE VMOV to/from 2 general-purpose registers Implement the MVE VMOV forms that move data between 2 general-purpose registers and 2 32-bit lanes in a vector register. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/mve.decode | 4 ++ target/arm/translate-a32.h | 1 + target/arm/translate-mve.c | 85 ++++++++++++++++++++++++++++++++++++++ target/arm/translate-vfp.c | 2 +- 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 0955ed0cc2..774ee2a1a5 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -136,6 +136,10 @@ VLDR_VSTR 1110110 1 a:1 . w:1 . .... ... 111101 ....... @vldr_vstr \ VLDR_VSTR 1110110 1 a:1 . w:1 . .... ... 111110 ....... @vldr_vstr \ size=2 p=1 +# Moves between 2 32-bit vector lanes and 2 general purpose registers +VMOV_to_2gp 1110 1100 0 . 00 rt2:4 ... 0 1111 000 idx:1 rt:4 qd=%qd +VMOV_from_2gp 1110 1100 0 . 01 rt2:4 ... 0 1111 000 idx:1 rt:4 qd=%qd + # Vector 2-op VAND 1110 1111 0 . 00 ... 0 ... 0 0001 . 1 . 1 ... 0 @2op_nosz VBIC 1110 1111 0 . 01 ... 0 ... 0 0001 . 1 . 1 ... 0 @2op_nosz diff --git a/target/arm/translate-a32.h b/target/arm/translate-a32.h index 6dfcafe179..6f4d65ddb0 100644 --- a/target/arm/translate-a32.h +++ b/target/arm/translate-a32.h @@ -49,6 +49,7 @@ void gen_rev16(TCGv_i32 dest, TCGv_i32 var); void clear_eci_state(DisasContext *s); bool mve_eci_check(DisasContext *s); void mve_update_and_store_eci(DisasContext *s); +bool mve_skip_vmov(DisasContext *s, int vn, int index, int size); static inline TCGv_i32 load_cpu_offset(int offset) { diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 02c26987a2..93707fdd68 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -1507,3 +1507,88 @@ static bool do_vabav(DisasContext *s, arg_vabav *a, MVEGenVABAVFn *fn) DO_VABAV(VABAV_S, vabavs) DO_VABAV(VABAV_U, vabavu) + +static bool trans_VMOV_to_2gp(DisasContext *s, arg_VMOV_to_2gp *a) +{ + /* + * VMOV two 32-bit vector lanes to two general-purpose registers. + * This insn is not predicated but it is subject to beat-wise + * execution if it is not in an IT block. For us this means + * only that if PSR.ECI says we should not be executing the beat + * corresponding to the lane of the vector register being accessed + * then we should skip perfoming the move, and that we need to do + * the usual check for bad ECI state and advance of ECI state. + * (If PSR.ECI is non-zero then we cannot be in an IT block.) + */ + TCGv_i32 tmp; + int vd; + + if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qd) || + a->rt == 13 || a->rt == 15 || a->rt2 == 13 || a->rt2 == 15 || + a->rt == a->rt2) { + /* Rt/Rt2 cases are UNPREDICTABLE */ + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + /* Convert Qreg index to Dreg for read_neon_element32() etc */ + vd = a->qd * 2; + + if (!mve_skip_vmov(s, vd, a->idx, MO_32)) { + tmp = tcg_temp_new_i32(); + read_neon_element32(tmp, vd, a->idx, MO_32); + store_reg(s, a->rt, tmp); + } + if (!mve_skip_vmov(s, vd + 1, a->idx, MO_32)) { + tmp = tcg_temp_new_i32(); + read_neon_element32(tmp, vd + 1, a->idx, MO_32); + store_reg(s, a->rt2, tmp); + } + + mve_update_and_store_eci(s); + return true; +} + +static bool trans_VMOV_from_2gp(DisasContext *s, arg_VMOV_to_2gp *a) +{ + /* + * VMOV two general-purpose registers to two 32-bit vector lanes. + * This insn is not predicated but it is subject to beat-wise + * execution if it is not in an IT block. For us this means + * only that if PSR.ECI says we should not be executing the beat + * corresponding to the lane of the vector register being accessed + * then we should skip perfoming the move, and that we need to do + * the usual check for bad ECI state and advance of ECI state. + * (If PSR.ECI is non-zero then we cannot be in an IT block.) + */ + TCGv_i32 tmp; + int vd; + + if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qd) || + a->rt == 13 || a->rt == 15 || a->rt2 == 13 || a->rt2 == 15) { + /* Rt/Rt2 cases are UNPREDICTABLE */ + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + /* Convert Qreg idx to Dreg for read_neon_element32() etc */ + vd = a->qd * 2; + + if (!mve_skip_vmov(s, vd, a->idx, MO_32)) { + tmp = load_reg(s, a->rt); + write_neon_element32(tmp, vd, a->idx, MO_32); + tcg_temp_free_i32(tmp); + } + if (!mve_skip_vmov(s, vd + 1, a->idx, MO_32)) { + tmp = load_reg(s, a->rt2); + write_neon_element32(tmp, vd + 1, a->idx, MO_32); + tcg_temp_free_i32(tmp); + } + + mve_update_and_store_eci(s); + return true; +} diff --git a/target/arm/translate-vfp.c b/target/arm/translate-vfp.c index b2991e21ec..e2eb797c82 100644 --- a/target/arm/translate-vfp.c +++ b/target/arm/translate-vfp.c @@ -581,7 +581,7 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) return true; } -static bool mve_skip_vmov(DisasContext *s, int vn, int index, int size) +bool mve_skip_vmov(DisasContext *s, int vn, int index, int size) { /* * In a CPU with MVE, the VMOV (vector lane to general-purpose register) From fea3958fa11c75b4f3f335ac0ce4cfc5cf0af7de Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:56 +0100 Subject: [PATCH 036/493] target/arm: Implement MVE VPNOT Implement the MVE VPNOT insn, which inverts the bits in VPR.P0 (subject to both predication and to beatwise execution). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 1 + target/arm/mve.decode | 1 + target/arm/mve_helper.c | 17 +++++++++++++++++ target/arm/translate-mve.c | 19 +++++++++++++++++++ 4 files changed, 38 insertions(+) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 651020aaad..8cb941912f 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -119,6 +119,7 @@ DEF_HELPER_FLAGS_4(mve_vorn, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_veor, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vpsel, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) +DEF_HELPER_FLAGS_1(mve_vpnot, TCG_CALL_NO_WG, void, env) DEF_HELPER_FLAGS_4(mve_vaddb, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vaddh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 774ee2a1a5..40bd0c04b5 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -571,6 +571,7 @@ VCMPGT 1111 1110 0 . .. ... 1 ... 1 1111 0 0 . 0 ... 1 @vcmp VCMPLE 1111 1110 0 . .. ... 1 ... 1 1111 1 0 . 0 ... 1 @vcmp { + VPNOT 1111 1110 0 0 11 000 1 000 0 1111 0100 1101 VPST 1111 1110 0 . 11 000 1 ... 0 1111 0100 1101 mask=%mask_22_13 VCMPEQ_scalar 1111 1110 0 . .. ... 1 ... 0 1111 0 1 0 0 .... @vcmp_scalar } diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index d326205cbf..c22a00c5ed 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -2201,6 +2201,23 @@ void HELPER(mve_vpsel)(CPUARMState *env, void *vd, void *vn, void *vm) mve_advance_vpt(env); } +void HELPER(mve_vpnot)(CPUARMState *env) +{ + /* + * P0 bits for unexecuted beats (where eci_mask is 0) are unchanged. + * P0 bits for predicated lanes in executed bits (where mask is 0) are 0. + * P0 bits otherwise are inverted. + * (This is the same logic as VCMP.) + * This insn is itself subject to predication and to beat-wise execution, + * and after it executes VPT state advances in the usual way. + */ + uint16_t mask = mve_element_mask(env); + uint16_t eci_mask = mve_eci_mask(env); + uint16_t beatpred = ~env->v7m.vpr & mask; + env->v7m.vpr = (env->v7m.vpr & ~(uint32_t)eci_mask) | (beatpred & eci_mask); + mve_advance_vpt(env); +} + #define DO_1OP_SAT(OP, ESIZE, TYPE, FN) \ void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm) \ { \ diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 93707fdd68..cc2e58cfe2 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -887,6 +887,25 @@ static bool trans_VPST(DisasContext *s, arg_VPST *a) return true; } +static bool trans_VPNOT(DisasContext *s, arg_VPNOT *a) +{ + /* + * Invert the predicate in VPR.P0. We have call out to + * a helper because this insn itself is beatwise and can + * be predicated. + */ + if (!dc_isar_feature(aa32_mve, s)) { + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + gen_helper_mve_vpnot(cpu_env); + mve_update_eci(s); + return true; +} + static bool trans_VADDV(DisasContext *s, arg_VADDV *a) { /* VADDV: vector add across vector */ From 0f31e37c7f0b9577c6ce46304158ccd7c935006b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:56 +0100 Subject: [PATCH 037/493] target/arm: Implement MVE VCTP Implement the MVE VCTP insn, which sets the VPR.P0 predicate bits so as to predicate any element at index Rn or greater is predicated. As with VPNOT, this insn itself is predicable and subject to beatwise execution. The calculation of the mask is the same as is used to determine ltpmask in mve_element_mask(), but we precalculate masklen in generated code to avoid having to have 4 helpers specialized by size. We put the decode line in with the low-overhead-loop insns in t32.decode because it's logically part of that collection of insn patterns, even though it is an MVE only insn. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 2 ++ target/arm/mve_helper.c | 20 ++++++++++++++++++++ target/arm/t32.decode | 1 + target/arm/translate-a32.h | 1 + target/arm/translate-mve.c | 2 +- target/arm/translate.c | 33 +++++++++++++++++++++++++++++++++ 6 files changed, 58 insertions(+), 1 deletion(-) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index 8cb941912f..b6cf3f0c94 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -121,6 +121,8 @@ DEF_HELPER_FLAGS_4(mve_veor, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vpsel, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_1(mve_vpnot, TCG_CALL_NO_WG, void, env) +DEF_HELPER_FLAGS_2(mve_vctp, TCG_CALL_NO_WG, void, env, i32) + DEF_HELPER_FLAGS_4(mve_vaddb, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vaddh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) DEF_HELPER_FLAGS_4(mve_vaddw, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr) diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index c22a00c5ed..1752555a21 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -2218,6 +2218,26 @@ void HELPER(mve_vpnot)(CPUARMState *env) mve_advance_vpt(env); } +/* + * VCTP: P0 unexecuted bits unchanged, predicated bits zeroed, + * otherwise set according to value of Rn. The calculation of + * newmask here works in the same way as the calculation of the + * ltpmask in mve_element_mask(), but we have pre-calculated + * the masklen in the generated code. + */ +void HELPER(mve_vctp)(CPUARMState *env, uint32_t masklen) +{ + uint16_t mask = mve_element_mask(env); + uint16_t eci_mask = mve_eci_mask(env); + uint16_t newmask; + + assert(masklen <= 16); + newmask = masklen ? MAKE_64BIT_MASK(0, masklen) : 0; + newmask &= mask; + env->v7m.vpr = (env->v7m.vpr & ~(uint32_t)eci_mask) | (newmask & eci_mask); + mve_advance_vpt(env); +} + #define DO_1OP_SAT(OP, ESIZE, TYPE, FN) \ void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm) \ { \ diff --git a/target/arm/t32.decode b/target/arm/t32.decode index 2d47f31f14..78fadef9d6 100644 --- a/target/arm/t32.decode +++ b/target/arm/t32.decode @@ -748,5 +748,6 @@ BL 1111 0. .......... 11.1 ............ @branch24 # This is DLSTP DLS 1111 0 0000 0 size:2 rn:4 1110 0000 0000 0001 } + VCTP 1111 0 0000 0 size:2 rn:4 1110 1000 0000 0001 ] } diff --git a/target/arm/translate-a32.h b/target/arm/translate-a32.h index 6f4d65ddb0..88f15df60e 100644 --- a/target/arm/translate-a32.h +++ b/target/arm/translate-a32.h @@ -48,6 +48,7 @@ long neon_element_offset(int reg, int element, MemOp memop); void gen_rev16(TCGv_i32 dest, TCGv_i32 var); void clear_eci_state(DisasContext *s); bool mve_eci_check(DisasContext *s); +void mve_update_eci(DisasContext *s); void mve_update_and_store_eci(DisasContext *s); bool mve_skip_vmov(DisasContext *s, int vn, int index, int size); diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index cc2e58cfe2..865d5acbe7 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -93,7 +93,7 @@ bool mve_eci_check(DisasContext *s) } } -static void mve_update_eci(DisasContext *s) +void mve_update_eci(DisasContext *s) { /* * The helper function will always update the CPUState field, diff --git a/target/arm/translate.c b/target/arm/translate.c index 80c282669f..804a53279b 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -8669,6 +8669,39 @@ static bool trans_LCTP(DisasContext *s, arg_LCTP *a) return true; } +static bool trans_VCTP(DisasContext *s, arg_VCTP *a) +{ + /* + * M-profile Create Vector Tail Predicate. This insn is itself + * predicated and is subject to beatwise execution. + */ + TCGv_i32 rn_shifted, masklen; + + if (!dc_isar_feature(aa32_mve, s) || a->rn == 13 || a->rn == 15) { + return false; + } + + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + /* + * We pre-calculate the mask length here to avoid having + * to have multiple helpers specialized for size. + * We pass the helper "rn <= (1 << (4 - size)) ? (rn << size) : 16". + */ + rn_shifted = tcg_temp_new_i32(); + masklen = load_reg(s, a->rn); + tcg_gen_shli_i32(rn_shifted, masklen, a->size); + tcg_gen_movcond_i32(TCG_COND_LEU, masklen, + masklen, tcg_constant_i32(1 << (4 - a->size)), + rn_shifted, tcg_constant_i32(16)); + gen_helper_mve_vctp(cpu_env, masklen); + tcg_temp_free_i32(masklen); + tcg_temp_free_i32(rn_shifted); + mve_update_eci(s); + return true; +} static bool op_tbranch(DisasContext *s, arg_tbranch *a, bool half) { From dc18628b1833157a50a424cb6b83b63eca560402 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:56 +0100 Subject: [PATCH 038/493] target/arm: Implement MVE scatter-gather insns Implement the MVE gather-loads and scatter-stores which form the address by adding a base value from a scalar register to an offset in each element of a vector. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 32 +++++++++ target/arm/mve.decode | 12 ++++ target/arm/mve_helper.c | 129 +++++++++++++++++++++++++++++++++++++ target/arm/translate-mve.c | 97 ++++++++++++++++++++++++++++ 4 files changed, 270 insertions(+) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index b6cf3f0c94..ba842b97c1 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -33,6 +33,38 @@ DEF_HELPER_FLAGS_3(mve_vstrb_h, TCG_CALL_NO_WG, void, env, ptr, i32) DEF_HELPER_FLAGS_3(mve_vstrb_w, TCG_CALL_NO_WG, void, env, ptr, i32) DEF_HELPER_FLAGS_3(mve_vstrh_w, TCG_CALL_NO_WG, void, env, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrb_sg_sh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrb_sg_sw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrh_sg_sw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(mve_vldrb_sg_ub, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrb_sg_uh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrb_sg_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrh_sg_uh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrh_sg_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrw_sg_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrd_sg_ud, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(mve_vstrb_sg_ub, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vstrb_sg_uh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vstrb_sg_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vstrh_sg_uh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vstrh_sg_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vstrw_sg_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vstrd_sg_ud, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(mve_vldrh_sg_os_sw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(mve_vldrh_sg_os_uh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrh_sg_os_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrw_sg_os_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrd_sg_os_ud, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(mve_vstrh_sg_os_uh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vstrh_sg_os_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vstrw_sg_os_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vstrd_sg_os_ud, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + DEF_HELPER_FLAGS_3(mve_vdup, TCG_CALL_NO_WG, void, env, ptr, i32) DEF_HELPER_FLAGS_4(mve_vidupb, TCG_CALL_NO_WG, i32, env, ptr, i32, i32) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 40bd0c04b5..6c3f45c719 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -42,11 +42,18 @@ &shl_scalar qda rm size &vmaxv qm rda size &vabav qn qm rda size +&vldst_sg qd qm rn size msize os + +# scatter-gather memory size is in bits 6:4 +%sg_msize 6:1 4:1 @vldr_vstr ....... . . . . l:1 rn:4 ... ...... imm:7 &vldr_vstr qd=%qd u=0 # Note that both Rn and Qd are 3 bits only (no D bit) @vldst_wn ... u:1 ... . . . . l:1 . rn:3 qd:3 . ... .. imm:7 &vldr_vstr +@vldst_sg .... .... .... rn:4 .... ... size:2 ... ... os:1 &vldst_sg \ + qd=%qd qm=%qm msize=%sg_msize + @1op .... .... .... size:2 .. .... .... .... .... &1op qd=%qd qm=%qm @1op_nosz .... .... .... .... .... .... .... .... &1op qd=%qd qm=%qm size=0 @2op .... .... .. size:2 .... .... .... .... .... &2op qd=%qd qm=%qm qn=%qn @@ -136,6 +143,11 @@ VLDR_VSTR 1110110 1 a:1 . w:1 . .... ... 111101 ....... @vldr_vstr \ VLDR_VSTR 1110110 1 a:1 . w:1 . .... ... 111110 ....... @vldr_vstr \ size=2 p=1 +# gather loads/scatter stores +VLDR_S_sg 111 0 1100 1 . 01 .... ... 0 111 . .... .... @vldst_sg +VLDR_U_sg 111 1 1100 1 . 01 .... ... 0 111 . .... .... @vldst_sg +VSTR_sg 111 0 1100 1 . 00 .... ... 0 111 . .... .... @vldst_sg + # Moves between 2 32-bit vector lanes and 2 general purpose registers VMOV_to_2gp 1110 1100 0 . 00 rt2:4 ... 0 1111 000 idx:1 rt:4 qd=%qd VMOV_from_2gp 1110 1100 0 . 01 rt2:4 ... 0 1111 000 idx:1 rt:4 qd=%qd diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 1752555a21..2b882db1c3 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -206,6 +206,135 @@ DO_VSTR(vstrh_w, 2, stw, 4, int32_t) #undef DO_VLDR #undef DO_VSTR +/* + * Gather loads/scatter stores. Here each element of Qm specifies + * an offset to use from the base register Rm. In the _os_ versions + * that offset is scaled by the element size. + * For loads, predicated lanes are zeroed instead of retaining + * their previous values. + */ +#define DO_VLDR_SG(OP, LDTYPE, ESIZE, TYPE, OFFTYPE, ADDRFN) \ + void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm, \ + uint32_t base) \ + { \ + TYPE *d = vd; \ + OFFTYPE *m = vm; \ + uint16_t mask = mve_element_mask(env); \ + uint16_t eci_mask = mve_eci_mask(env); \ + unsigned e; \ + uint32_t addr; \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ + if (!(eci_mask & 1)) { \ + continue; \ + } \ + addr = ADDRFN(base, m[H##ESIZE(e)]); \ + d[H##ESIZE(e)] = (mask & 1) ? \ + cpu_##LDTYPE##_data_ra(env, addr, GETPC()) : 0; \ + } \ + mve_advance_vpt(env); \ + } + +/* We know here TYPE is unsigned so always the same as the offset type */ +#define DO_VSTR_SG(OP, STTYPE, ESIZE, TYPE, ADDRFN) \ + void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm, \ + uint32_t base) \ + { \ + TYPE *d = vd; \ + TYPE *m = vm; \ + uint16_t mask = mve_element_mask(env); \ + unsigned e; \ + uint32_t addr; \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ + addr = ADDRFN(base, m[H##ESIZE(e)]); \ + if (mask & 1) { \ + cpu_##STTYPE##_data_ra(env, addr, d[H##ESIZE(e)], GETPC()); \ + } \ + } \ + mve_advance_vpt(env); \ + } + +/* + * 64-bit accesses are slightly different: they are done as two 32-bit + * accesses, controlled by the predicate mask for the relevant beat, + * and with a single 32-bit offset in the first of the two Qm elements. + * Note that for QEMU our IMPDEF AIRCR.ENDIANNESS is always 0 (little). + */ +#define DO_VLDR64_SG(OP, ADDRFN) \ + void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm, \ + uint32_t base) \ + { \ + uint32_t *d = vd; \ + uint32_t *m = vm; \ + uint16_t mask = mve_element_mask(env); \ + uint16_t eci_mask = mve_eci_mask(env); \ + unsigned e; \ + uint32_t addr; \ + for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ + if (!(eci_mask & 1)) { \ + continue; \ + } \ + addr = ADDRFN(base, m[H4(e & ~1)]); \ + addr += 4 * (e & 1); \ + d[H4(e)] = (mask & 1) ? cpu_ldl_data_ra(env, addr, GETPC()) : 0; \ + } \ + mve_advance_vpt(env); \ + } + +#define DO_VSTR64_SG(OP, ADDRFN) \ + void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm, \ + uint32_t base) \ + { \ + uint32_t *d = vd; \ + uint32_t *m = vm; \ + uint16_t mask = mve_element_mask(env); \ + unsigned e; \ + uint32_t addr; \ + for (e = 0; e < 16 / 4; e++, mask >>= 4) { \ + addr = ADDRFN(base, m[H4(e & ~1)]); \ + addr += 4 * (e & 1); \ + if (mask & 1) { \ + cpu_stl_data_ra(env, addr, d[H4(e)], GETPC()); \ + } \ + } \ + mve_advance_vpt(env); \ + } + +#define ADDR_ADD(BASE, OFFSET) ((BASE) + (OFFSET)) +#define ADDR_ADD_OSH(BASE, OFFSET) ((BASE) + ((OFFSET) << 1)) +#define ADDR_ADD_OSW(BASE, OFFSET) ((BASE) + ((OFFSET) << 2)) +#define ADDR_ADD_OSD(BASE, OFFSET) ((BASE) + ((OFFSET) << 3)) + +DO_VLDR_SG(vldrb_sg_sh, ldsb, 2, int16_t, uint16_t, ADDR_ADD) +DO_VLDR_SG(vldrb_sg_sw, ldsb, 4, int32_t, uint32_t, ADDR_ADD) +DO_VLDR_SG(vldrh_sg_sw, ldsw, 4, int32_t, uint32_t, ADDR_ADD) + +DO_VLDR_SG(vldrb_sg_ub, ldub, 1, uint8_t, uint8_t, ADDR_ADD) +DO_VLDR_SG(vldrb_sg_uh, ldub, 2, uint16_t, uint16_t, ADDR_ADD) +DO_VLDR_SG(vldrb_sg_uw, ldub, 4, uint32_t, uint32_t, ADDR_ADD) +DO_VLDR_SG(vldrh_sg_uh, lduw, 2, uint16_t, uint16_t, ADDR_ADD) +DO_VLDR_SG(vldrh_sg_uw, lduw, 4, uint32_t, uint32_t, ADDR_ADD) +DO_VLDR_SG(vldrw_sg_uw, ldl, 4, uint32_t, uint32_t, ADDR_ADD) +DO_VLDR64_SG(vldrd_sg_ud, ADDR_ADD) + +DO_VLDR_SG(vldrh_sg_os_sw, ldsw, 4, int32_t, uint32_t, ADDR_ADD_OSH) +DO_VLDR_SG(vldrh_sg_os_uh, lduw, 2, uint16_t, uint16_t, ADDR_ADD_OSH) +DO_VLDR_SG(vldrh_sg_os_uw, lduw, 4, uint32_t, uint32_t, ADDR_ADD_OSH) +DO_VLDR_SG(vldrw_sg_os_uw, ldl, 4, uint32_t, uint32_t, ADDR_ADD_OSW) +DO_VLDR64_SG(vldrd_sg_os_ud, ADDR_ADD_OSD) + +DO_VSTR_SG(vstrb_sg_ub, stb, 1, uint8_t, ADDR_ADD) +DO_VSTR_SG(vstrb_sg_uh, stb, 2, uint16_t, ADDR_ADD) +DO_VSTR_SG(vstrb_sg_uw, stb, 4, uint32_t, ADDR_ADD) +DO_VSTR_SG(vstrh_sg_uh, stw, 2, uint16_t, ADDR_ADD) +DO_VSTR_SG(vstrh_sg_uw, stw, 4, uint32_t, ADDR_ADD) +DO_VSTR_SG(vstrw_sg_uw, stl, 4, uint32_t, ADDR_ADD) +DO_VSTR64_SG(vstrd_sg_ud, ADDR_ADD) + +DO_VSTR_SG(vstrh_sg_os_uh, stw, 2, uint16_t, ADDR_ADD_OSH) +DO_VSTR_SG(vstrh_sg_os_uw, stw, 4, uint32_t, ADDR_ADD_OSH) +DO_VSTR_SG(vstrw_sg_os_uw, stl, 4, uint32_t, ADDR_ADD_OSW) +DO_VSTR64_SG(vstrd_sg_os_ud, ADDR_ADD_OSD) + /* * The mergemask(D, R, M) macro performs the operation "*D = R" but * storing only the bytes which correspond to 1 bits in M, diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 865d5acbe7..24d4e57ead 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -34,6 +34,7 @@ static inline int vidup_imm(DisasContext *s, int x) #include "decode-mve.c.inc" typedef void MVEGenLdStFn(TCGv_ptr, TCGv_ptr, TCGv_i32); +typedef void MVEGenLdStSGFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void MVEGenOneOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); typedef void MVEGenTwoOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr); typedef void MVEGenTwoOpScalarFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); @@ -209,6 +210,102 @@ DO_VLDST_WIDE_NARROW(VLDSTB_H, vldrb_sh, vldrb_uh, vstrb_h, MO_8) DO_VLDST_WIDE_NARROW(VLDSTB_W, vldrb_sw, vldrb_uw, vstrb_w, MO_8) DO_VLDST_WIDE_NARROW(VLDSTH_W, vldrh_sw, vldrh_uw, vstrh_w, MO_16) +static bool do_ldst_sg(DisasContext *s, arg_vldst_sg *a, MVEGenLdStSGFn fn) +{ + TCGv_i32 addr; + TCGv_ptr qd, qm; + + if (!dc_isar_feature(aa32_mve, s) || + !mve_check_qreg_bank(s, a->qd | a->qm) || + !fn || a->rn == 15) { + /* Rn case is UNPREDICTABLE */ + return false; + } + + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + addr = load_reg(s, a->rn); + + qd = mve_qreg_ptr(a->qd); + qm = mve_qreg_ptr(a->qm); + fn(cpu_env, qd, qm, addr); + tcg_temp_free_ptr(qd); + tcg_temp_free_ptr(qm); + tcg_temp_free_i32(addr); + mve_update_eci(s); + return true; +} + +/* + * The naming scheme here is "vldrb_sg_sh == in-memory byte loads + * signextended to halfword elements in register". _os_ indicates that + * the offsets in Qm should be scaled by the element size. + */ +/* This macro is just to make the arrays more compact in these functions */ +#define F(N) gen_helper_mve_##N + +/* VLDRB/VSTRB (ie msize 1) with OS=1 is UNPREDICTABLE; we UNDEF */ +static bool trans_VLDR_S_sg(DisasContext *s, arg_vldst_sg *a) +{ + static MVEGenLdStSGFn * const fns[2][4][4] = { { + { NULL, F(vldrb_sg_sh), F(vldrb_sg_sw), NULL }, + { NULL, NULL, F(vldrh_sg_sw), NULL }, + { NULL, NULL, NULL, NULL }, + { NULL, NULL, NULL, NULL } + }, { + { NULL, NULL, NULL, NULL }, + { NULL, NULL, F(vldrh_sg_os_sw), NULL }, + { NULL, NULL, NULL, NULL }, + { NULL, NULL, NULL, NULL } + } + }; + if (a->qd == a->qm) { + return false; /* UNPREDICTABLE */ + } + return do_ldst_sg(s, a, fns[a->os][a->msize][a->size]); +} + +static bool trans_VLDR_U_sg(DisasContext *s, arg_vldst_sg *a) +{ + static MVEGenLdStSGFn * const fns[2][4][4] = { { + { F(vldrb_sg_ub), F(vldrb_sg_uh), F(vldrb_sg_uw), NULL }, + { NULL, F(vldrh_sg_uh), F(vldrh_sg_uw), NULL }, + { NULL, NULL, F(vldrw_sg_uw), NULL }, + { NULL, NULL, NULL, F(vldrd_sg_ud) } + }, { + { NULL, NULL, NULL, NULL }, + { NULL, F(vldrh_sg_os_uh), F(vldrh_sg_os_uw), NULL }, + { NULL, NULL, F(vldrw_sg_os_uw), NULL }, + { NULL, NULL, NULL, F(vldrd_sg_os_ud) } + } + }; + if (a->qd == a->qm) { + return false; /* UNPREDICTABLE */ + } + return do_ldst_sg(s, a, fns[a->os][a->msize][a->size]); +} + +static bool trans_VSTR_sg(DisasContext *s, arg_vldst_sg *a) +{ + static MVEGenLdStSGFn * const fns[2][4][4] = { { + { F(vstrb_sg_ub), F(vstrb_sg_uh), F(vstrb_sg_uw), NULL }, + { NULL, F(vstrh_sg_uh), F(vstrh_sg_uw), NULL }, + { NULL, NULL, F(vstrw_sg_uw), NULL }, + { NULL, NULL, NULL, F(vstrd_sg_ud) } + }, { + { NULL, NULL, NULL, NULL }, + { NULL, F(vstrh_sg_os_uh), F(vstrh_sg_os_uw), NULL }, + { NULL, NULL, F(vstrw_sg_os_uw), NULL }, + { NULL, NULL, NULL, F(vstrd_sg_os_ud) } + } + }; + return do_ldst_sg(s, a, fns[a->os][a->msize][a->size]); +} + +#undef F + static bool trans_VDUP(DisasContext *s, arg_VDUP *a) { TCGv_ptr qd; From fac80f0856cc465b21e2e59a64146b3540e055db Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:57 +0100 Subject: [PATCH 039/493] target/arm: Implement MVE scatter-gather immediate forms Implement the MVE VLDR/VSTR insns which do scatter-gather using base addresses from Qm plus or minus an immediate offset (possibly with writeback). Note that writeback is not predicated but it does have to honour ECI state, so we have to add an eci_mask check to the VSTR_SG macros (the VLDR_SG macros already needed this to be able to distinguish "skip beat" from "set predicated element to 0"). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 5 +++ target/arm/mve.decode | 10 +++++ target/arm/mve_helper.c | 91 ++++++++++++++++++++++++-------------- target/arm/translate-mve.c | 72 ++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 32 deletions(-) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index ba842b97c1..a85a7e1b75 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -65,6 +65,11 @@ DEF_HELPER_FLAGS_4(mve_vstrh_sg_os_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vstrw_sg_os_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vstrd_sg_os_ud, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrw_sg_wb_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vldrd_sg_wb_ud, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vstrw_sg_wb_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(mve_vstrd_sg_wb_ud, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) + DEF_HELPER_FLAGS_3(mve_vdup, TCG_CALL_NO_WG, void, env, ptr, i32) DEF_HELPER_FLAGS_4(mve_vidupb, TCG_CALL_NO_WG, i32, env, ptr, i32, i32) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 6c3f45c719..48882dd7f3 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -43,6 +43,7 @@ &vmaxv qm rda size &vabav qn qm rda size &vldst_sg qd qm rn size msize os +&vldst_sg_imm qd qm a w imm # scatter-gather memory size is in bits 6:4 %sg_msize 6:1 4:1 @@ -54,6 +55,10 @@ @vldst_sg .... .... .... rn:4 .... ... size:2 ... ... os:1 &vldst_sg \ qd=%qd qm=%qm msize=%sg_msize +# Qm is in the fields usually labeled Qn +@vldst_sg_imm .... .... a:1 . w:1 . .... .... .... . imm:7 &vldst_sg_imm \ + qd=%qd qm=%qn + @1op .... .... .... size:2 .. .... .... .... .... &1op qd=%qd qm=%qm @1op_nosz .... .... .... .... .... .... .... .... &1op qd=%qd qm=%qm size=0 @2op .... .... .. size:2 .... .... .... .... .... &2op qd=%qd qm=%qm qn=%qn @@ -148,6 +153,11 @@ VLDR_S_sg 111 0 1100 1 . 01 .... ... 0 111 . .... .... @vldst_sg VLDR_U_sg 111 1 1100 1 . 01 .... ... 0 111 . .... .... @vldst_sg VSTR_sg 111 0 1100 1 . 00 .... ... 0 111 . .... .... @vldst_sg +VLDRW_sg_imm 111 1 1101 ... 1 ... 0 ... 1 1110 .... .... @vldst_sg_imm +VLDRD_sg_imm 111 1 1101 ... 1 ... 0 ... 1 1111 .... .... @vldst_sg_imm +VSTRW_sg_imm 111 1 1101 ... 0 ... 0 ... 1 1110 .... .... @vldst_sg_imm +VSTRD_sg_imm 111 1 1101 ... 0 ... 0 ... 1 1111 .... .... @vldst_sg_imm + # Moves between 2 32-bit vector lanes and 2 general purpose registers VMOV_to_2gp 1110 1100 0 . 00 rt2:4 ... 0 1111 000 idx:1 rt:4 qd=%qd VMOV_from_2gp 1110 1100 0 . 01 rt2:4 ... 0 1111 000 idx:1 rt:4 qd=%qd diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index 2b882db1c3..bbbaa53807 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -213,7 +213,7 @@ DO_VSTR(vstrh_w, 2, stw, 4, int32_t) * For loads, predicated lanes are zeroed instead of retaining * their previous values. */ -#define DO_VLDR_SG(OP, LDTYPE, ESIZE, TYPE, OFFTYPE, ADDRFN) \ +#define DO_VLDR_SG(OP, LDTYPE, ESIZE, TYPE, OFFTYPE, ADDRFN, WB) \ void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm, \ uint32_t base) \ { \ @@ -230,25 +230,35 @@ DO_VSTR(vstrh_w, 2, stw, 4, int32_t) addr = ADDRFN(base, m[H##ESIZE(e)]); \ d[H##ESIZE(e)] = (mask & 1) ? \ cpu_##LDTYPE##_data_ra(env, addr, GETPC()) : 0; \ + if (WB) { \ + m[H##ESIZE(e)] = addr; \ + } \ } \ mve_advance_vpt(env); \ } /* We know here TYPE is unsigned so always the same as the offset type */ -#define DO_VSTR_SG(OP, STTYPE, ESIZE, TYPE, ADDRFN) \ +#define DO_VSTR_SG(OP, STTYPE, ESIZE, TYPE, ADDRFN, WB) \ void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm, \ uint32_t base) \ { \ TYPE *d = vd; \ TYPE *m = vm; \ uint16_t mask = mve_element_mask(env); \ + uint16_t eci_mask = mve_eci_mask(env); \ unsigned e; \ uint32_t addr; \ - for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ + for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ + if (!(eci_mask & 1)) { \ + continue; \ + } \ addr = ADDRFN(base, m[H##ESIZE(e)]); \ if (mask & 1) { \ cpu_##STTYPE##_data_ra(env, addr, d[H##ESIZE(e)], GETPC()); \ } \ + if (WB) { \ + m[H##ESIZE(e)] = addr; \ + } \ } \ mve_advance_vpt(env); \ } @@ -258,8 +268,10 @@ DO_VSTR(vstrh_w, 2, stw, 4, int32_t) * accesses, controlled by the predicate mask for the relevant beat, * and with a single 32-bit offset in the first of the two Qm elements. * Note that for QEMU our IMPDEF AIRCR.ENDIANNESS is always 0 (little). + * Address writeback happens on the odd beats and updates the address + * stored in the even-beat element. */ -#define DO_VLDR64_SG(OP, ADDRFN) \ +#define DO_VLDR64_SG(OP, ADDRFN, WB) \ void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm, \ uint32_t base) \ { \ @@ -276,25 +288,35 @@ DO_VSTR(vstrh_w, 2, stw, 4, int32_t) addr = ADDRFN(base, m[H4(e & ~1)]); \ addr += 4 * (e & 1); \ d[H4(e)] = (mask & 1) ? cpu_ldl_data_ra(env, addr, GETPC()) : 0; \ + if (WB && (e & 1)) { \ + m[H4(e & ~1)] = addr - 4; \ + } \ } \ mve_advance_vpt(env); \ } -#define DO_VSTR64_SG(OP, ADDRFN) \ +#define DO_VSTR64_SG(OP, ADDRFN, WB) \ void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm, \ uint32_t base) \ { \ uint32_t *d = vd; \ uint32_t *m = vm; \ uint16_t mask = mve_element_mask(env); \ + uint16_t eci_mask = mve_eci_mask(env); \ unsigned e; \ uint32_t addr; \ - for (e = 0; e < 16 / 4; e++, mask >>= 4) { \ + for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ + if (!(eci_mask & 1)) { \ + continue; \ + } \ addr = ADDRFN(base, m[H4(e & ~1)]); \ addr += 4 * (e & 1); \ if (mask & 1) { \ cpu_stl_data_ra(env, addr, d[H4(e)], GETPC()); \ } \ + if (WB && (e & 1)) { \ + m[H4(e & ~1)] = addr - 4; \ + } \ } \ mve_advance_vpt(env); \ } @@ -304,36 +326,41 @@ DO_VSTR(vstrh_w, 2, stw, 4, int32_t) #define ADDR_ADD_OSW(BASE, OFFSET) ((BASE) + ((OFFSET) << 2)) #define ADDR_ADD_OSD(BASE, OFFSET) ((BASE) + ((OFFSET) << 3)) -DO_VLDR_SG(vldrb_sg_sh, ldsb, 2, int16_t, uint16_t, ADDR_ADD) -DO_VLDR_SG(vldrb_sg_sw, ldsb, 4, int32_t, uint32_t, ADDR_ADD) -DO_VLDR_SG(vldrh_sg_sw, ldsw, 4, int32_t, uint32_t, ADDR_ADD) +DO_VLDR_SG(vldrb_sg_sh, ldsb, 2, int16_t, uint16_t, ADDR_ADD, false) +DO_VLDR_SG(vldrb_sg_sw, ldsb, 4, int32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_sw, ldsw, 4, int32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrb_sg_ub, ldub, 1, uint8_t, uint8_t, ADDR_ADD) -DO_VLDR_SG(vldrb_sg_uh, ldub, 2, uint16_t, uint16_t, ADDR_ADD) -DO_VLDR_SG(vldrb_sg_uw, ldub, 4, uint32_t, uint32_t, ADDR_ADD) -DO_VLDR_SG(vldrh_sg_uh, lduw, 2, uint16_t, uint16_t, ADDR_ADD) -DO_VLDR_SG(vldrh_sg_uw, lduw, 4, uint32_t, uint32_t, ADDR_ADD) -DO_VLDR_SG(vldrw_sg_uw, ldl, 4, uint32_t, uint32_t, ADDR_ADD) -DO_VLDR64_SG(vldrd_sg_ud, ADDR_ADD) +DO_VLDR_SG(vldrb_sg_ub, ldub, 1, uint8_t, uint8_t, ADDR_ADD, false) +DO_VLDR_SG(vldrb_sg_uh, ldub, 2, uint16_t, uint16_t, ADDR_ADD, false) +DO_VLDR_SG(vldrb_sg_uw, ldub, 4, uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uh, lduw, 2, uint16_t, uint16_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uw, lduw, 4, uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrw_sg_uw, ldl, 4, uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR64_SG(vldrd_sg_ud, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_os_sw, ldsw, 4, int32_t, uint32_t, ADDR_ADD_OSH) -DO_VLDR_SG(vldrh_sg_os_uh, lduw, 2, uint16_t, uint16_t, ADDR_ADD_OSH) -DO_VLDR_SG(vldrh_sg_os_uw, lduw, 4, uint32_t, uint32_t, ADDR_ADD_OSH) -DO_VLDR_SG(vldrw_sg_os_uw, ldl, 4, uint32_t, uint32_t, ADDR_ADD_OSW) -DO_VLDR64_SG(vldrd_sg_os_ud, ADDR_ADD_OSD) +DO_VLDR_SG(vldrh_sg_os_sw, ldsw, 4, int32_t, uint32_t, ADDR_ADD_OSH, false) +DO_VLDR_SG(vldrh_sg_os_uh, lduw, 2, uint16_t, uint16_t, ADDR_ADD_OSH, false) +DO_VLDR_SG(vldrh_sg_os_uw, lduw, 4, uint32_t, uint32_t, ADDR_ADD_OSH, false) +DO_VLDR_SG(vldrw_sg_os_uw, ldl, 4, uint32_t, uint32_t, ADDR_ADD_OSW, false) +DO_VLDR64_SG(vldrd_sg_os_ud, ADDR_ADD_OSD, false) -DO_VSTR_SG(vstrb_sg_ub, stb, 1, uint8_t, ADDR_ADD) -DO_VSTR_SG(vstrb_sg_uh, stb, 2, uint16_t, ADDR_ADD) -DO_VSTR_SG(vstrb_sg_uw, stb, 4, uint32_t, ADDR_ADD) -DO_VSTR_SG(vstrh_sg_uh, stw, 2, uint16_t, ADDR_ADD) -DO_VSTR_SG(vstrh_sg_uw, stw, 4, uint32_t, ADDR_ADD) -DO_VSTR_SG(vstrw_sg_uw, stl, 4, uint32_t, ADDR_ADD) -DO_VSTR64_SG(vstrd_sg_ud, ADDR_ADD) +DO_VSTR_SG(vstrb_sg_ub, stb, 1, uint8_t, ADDR_ADD, false) +DO_VSTR_SG(vstrb_sg_uh, stb, 2, uint16_t, ADDR_ADD, false) +DO_VSTR_SG(vstrb_sg_uw, stb, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uh, stw, 2, uint16_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uw, stw, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrw_sg_uw, stl, 4, uint32_t, ADDR_ADD, false) +DO_VSTR64_SG(vstrd_sg_ud, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_os_uh, stw, 2, uint16_t, ADDR_ADD_OSH) -DO_VSTR_SG(vstrh_sg_os_uw, stw, 4, uint32_t, ADDR_ADD_OSH) -DO_VSTR_SG(vstrw_sg_os_uw, stl, 4, uint32_t, ADDR_ADD_OSW) -DO_VSTR64_SG(vstrd_sg_os_ud, ADDR_ADD_OSD) +DO_VSTR_SG(vstrh_sg_os_uh, stw, 2, uint16_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrh_sg_os_uw, stw, 4, uint32_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrw_sg_os_uw, stl, 4, uint32_t, ADDR_ADD_OSW, false) +DO_VSTR64_SG(vstrd_sg_os_ud, ADDR_ADD_OSD, false) + +DO_VLDR_SG(vldrw_sg_wb_uw, ldl, 4, uint32_t, uint32_t, ADDR_ADD, true) +DO_VLDR64_SG(vldrd_sg_wb_ud, ADDR_ADD, true) +DO_VSTR_SG(vstrw_sg_wb_uw, stl, 4, uint32_t, ADDR_ADD, true) +DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) /* * The mergemask(D, R, M) macro performs the operation "*D = R" but diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index 24d4e57ead..d3cb339686 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -306,6 +306,78 @@ static bool trans_VSTR_sg(DisasContext *s, arg_vldst_sg *a) #undef F +static bool do_ldst_sg_imm(DisasContext *s, arg_vldst_sg_imm *a, + MVEGenLdStSGFn *fn, unsigned msize) +{ + uint32_t offset; + TCGv_ptr qd, qm; + + if (!dc_isar_feature(aa32_mve, s) || + !mve_check_qreg_bank(s, a->qd | a->qm) || + !fn) { + return false; + } + + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + offset = a->imm << msize; + if (!a->a) { + offset = -offset; + } + + qd = mve_qreg_ptr(a->qd); + qm = mve_qreg_ptr(a->qm); + fn(cpu_env, qd, qm, tcg_constant_i32(offset)); + tcg_temp_free_ptr(qd); + tcg_temp_free_ptr(qm); + mve_update_eci(s); + return true; +} + +static bool trans_VLDRW_sg_imm(DisasContext *s, arg_vldst_sg_imm *a) +{ + static MVEGenLdStSGFn * const fns[] = { + gen_helper_mve_vldrw_sg_uw, + gen_helper_mve_vldrw_sg_wb_uw, + }; + if (a->qd == a->qm) { + return false; /* UNPREDICTABLE */ + } + return do_ldst_sg_imm(s, a, fns[a->w], MO_32); +} + +static bool trans_VLDRD_sg_imm(DisasContext *s, arg_vldst_sg_imm *a) +{ + static MVEGenLdStSGFn * const fns[] = { + gen_helper_mve_vldrd_sg_ud, + gen_helper_mve_vldrd_sg_wb_ud, + }; + if (a->qd == a->qm) { + return false; /* UNPREDICTABLE */ + } + return do_ldst_sg_imm(s, a, fns[a->w], MO_64); +} + +static bool trans_VSTRW_sg_imm(DisasContext *s, arg_vldst_sg_imm *a) +{ + static MVEGenLdStSGFn * const fns[] = { + gen_helper_mve_vstrw_sg_uw, + gen_helper_mve_vstrw_sg_wb_uw, + }; + return do_ldst_sg_imm(s, a, fns[a->w], MO_32); +} + +static bool trans_VSTRD_sg_imm(DisasContext *s, arg_vldst_sg_imm *a) +{ + static MVEGenLdStSGFn * const fns[] = { + gen_helper_mve_vstrd_sg_ud, + gen_helper_mve_vstrd_sg_wb_ud, + }; + return do_ldst_sg_imm(s, a, fns[a->w], MO_64); +} + static bool trans_VDUP(DisasContext *s, arg_VDUP *a) { TCGv_ptr qd; From 075e7e97e3a042854b8ea2827559891a577b4a6b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 17:11:57 +0100 Subject: [PATCH 040/493] target/arm: Implement MVE interleaving loads/stores Implement the MVE interleaving load/store functions VLD2, VLD4, VST2 and VST4. VLD2 loads 16 bytes of data from memory and writes to 2 consecutive Qregs; VLD4 loads 16 bytes of data from memory and writes to 4 consecutive Qregs. The 'pattern' field in the encoding determines the offset into memory which is accessed and also which elements in the Qregs are written to. (The intention is that a sequence of four consecutive VLD4 with different pattern values performs a complete de-interleaving load of 64 bytes into all elements of the 4 Qregs.) VST2 and VST4 do the same, but for stores. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper-mve.h | 48 ++++++ target/arm/mve.decode | 11 ++ target/arm/mve_helper.c | 342 +++++++++++++++++++++++++++++++++++++ target/arm/translate-mve.c | 94 ++++++++++ 4 files changed, 495 insertions(+) diff --git a/target/arm/helper-mve.h b/target/arm/helper-mve.h index a85a7e1b75..3db9b15f12 100644 --- a/target/arm/helper-mve.h +++ b/target/arm/helper-mve.h @@ -70,6 +70,54 @@ DEF_HELPER_FLAGS_4(mve_vldrd_sg_wb_ud, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vstrw_sg_wb_uw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) DEF_HELPER_FLAGS_4(mve_vstrd_sg_wb_ud, TCG_CALL_NO_WG, void, env, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(mve_vld20b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld20h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld20w, TCG_CALL_NO_WG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vld21b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld21h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld21w, TCG_CALL_NO_WG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vld40b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld40h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld40w, TCG_CALL_NO_WG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vld41b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld41h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld41w, TCG_CALL_NO_WG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vld42b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld42h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld42w, TCG_CALL_NO_WG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vld43b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld43h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vld43w, TCG_CALL_NO_WG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vst20b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst20h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst20w, TCG_CALL_NO_WG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vst21b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst21h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst21w, TCG_CALL_NO_WG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vst40b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst40h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst40w, TCG_CALL_NO_WG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vst41b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst41h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst41w, TCG_CALL_NO_WG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vst42b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst42h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst42w, TCG_CALL_NO_WG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(mve_vst43b, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst43h, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_3(mve_vst43w, TCG_CALL_NO_WG, void, env, i32, i32) + DEF_HELPER_FLAGS_3(mve_vdup, TCG_CALL_NO_WG, void, env, ptr, i32) DEF_HELPER_FLAGS_4(mve_vidupb, TCG_CALL_NO_WG, i32, env, ptr, i32, i32) diff --git a/target/arm/mve.decode b/target/arm/mve.decode index 48882dd7f3..8744681629 100644 --- a/target/arm/mve.decode +++ b/target/arm/mve.decode @@ -44,6 +44,7 @@ &vabav qn qm rda size &vldst_sg qd qm rn size msize os &vldst_sg_imm qd qm a w imm +&vldst_il qd rn size pat w # scatter-gather memory size is in bits 6:4 %sg_msize 6:1 4:1 @@ -59,6 +60,10 @@ @vldst_sg_imm .... .... a:1 . w:1 . .... .... .... . imm:7 &vldst_sg_imm \ qd=%qd qm=%qn +# Deinterleaving load/interleaving store +@vldst_il .... .... .. w:1 . rn:4 .... ... size:2 pat:2 ..... &vldst_il \ + qd=%qd + @1op .... .... .... size:2 .. .... .... .... .... &1op qd=%qd qm=%qm @1op_nosz .... .... .... .... .... .... .... .... &1op qd=%qd qm=%qm size=0 @2op .... .... .. size:2 .... .... .... .... .... &2op qd=%qd qm=%qm qn=%qn @@ -158,6 +163,12 @@ VLDRD_sg_imm 111 1 1101 ... 1 ... 0 ... 1 1111 .... .... @vldst_sg_imm VSTRW_sg_imm 111 1 1101 ... 0 ... 0 ... 1 1110 .... .... @vldst_sg_imm VSTRD_sg_imm 111 1 1101 ... 0 ... 0 ... 1 1111 .... .... @vldst_sg_imm +# deinterleaving loads/interleaving stores +VLD2 1111 1100 1 .. 1 .... ... 1 111 .. .. 00000 @vldst_il +VLD4 1111 1100 1 .. 1 .... ... 1 111 .. .. 00001 @vldst_il +VST2 1111 1100 1 .. 0 .... ... 1 111 .. .. 00000 @vldst_il +VST4 1111 1100 1 .. 0 .... ... 1 111 .. .. 00001 @vldst_il + # Moves between 2 32-bit vector lanes and 2 general purpose registers VMOV_to_2gp 1110 1100 0 . 00 rt2:4 ... 0 1111 000 idx:1 rt:4 qd=%qd VMOV_from_2gp 1110 1100 0 . 01 rt2:4 ... 0 1111 000 idx:1 rt:4 qd=%qd diff --git a/target/arm/mve_helper.c b/target/arm/mve_helper.c index bbbaa53807..c2826eb5f9 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/mve_helper.c @@ -362,6 +362,348 @@ DO_VLDR64_SG(vldrd_sg_wb_ud, ADDR_ADD, true) DO_VSTR_SG(vstrw_sg_wb_uw, stl, 4, uint32_t, ADDR_ADD, true) DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) +/* + * Deinterleaving loads/interleaving stores. + * + * For these helpers we are passed the index of the first Qreg + * (VLD2/VST2 will also access Qn+1, VLD4/VST4 access Qn .. Qn+3) + * and the value of the base address register Rn. + * The helpers are specialized for pattern and element size, so + * for instance vld42h is VLD4 with pattern 2, element size MO_16. + * + * These insns are beatwise but not predicated, so we must honour ECI, + * but need not look at mve_element_mask(). + * + * The pseudocode implements these insns with multiple memory accesses + * of the element size, but rules R_VVVG and R_FXDM permit us to make + * one 32-bit memory access per beat. + */ +#define DO_VLD4B(OP, O1, O2, O3, O4) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat, e; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O2, O3, O4 }; \ + uint32_t addr, data; \ + for (beat = 0; beat < 4; beat++, mask >>= 4) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat] * 4; \ + data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + for (e = 0; e < 4; e++, data >>= 8) { \ + uint8_t *qd = (uint8_t *)aa32_vfp_qreg(env, qnidx + e); \ + qd[H1(off[beat])] = data; \ + } \ + } \ + } + +#define DO_VLD4H(OP, O1, O2) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O1, O2, O2 }; \ + uint32_t addr, data; \ + int y; /* y counts 0 2 0 2 */ \ + uint16_t *qd; \ + for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat] * 8 + (beat & 1) * 4; \ + data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + qd = (uint16_t *)aa32_vfp_qreg(env, qnidx + y); \ + qd[H2(off[beat])] = data; \ + data >>= 16; \ + qd = (uint16_t *)aa32_vfp_qreg(env, qnidx + y + 1); \ + qd[H2(off[beat])] = data; \ + } \ + } + +#define DO_VLD4W(OP, O1, O2, O3, O4) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O2, O3, O4 }; \ + uint32_t addr, data; \ + uint32_t *qd; \ + int y; \ + for (beat = 0; beat < 4; beat++, mask >>= 4) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat] * 4; \ + data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + y = (beat + (O1 & 2)) & 3; \ + qd = (uint32_t *)aa32_vfp_qreg(env, qnidx + y); \ + qd[H4(off[beat] >> 2)] = data; \ + } \ + } + +DO_VLD4B(vld40b, 0, 1, 10, 11) +DO_VLD4B(vld41b, 2, 3, 12, 13) +DO_VLD4B(vld42b, 4, 5, 14, 15) +DO_VLD4B(vld43b, 6, 7, 8, 9) + +DO_VLD4H(vld40h, 0, 5) +DO_VLD4H(vld41h, 1, 6) +DO_VLD4H(vld42h, 2, 7) +DO_VLD4H(vld43h, 3, 4) + +DO_VLD4W(vld40w, 0, 1, 10, 11) +DO_VLD4W(vld41w, 2, 3, 12, 13) +DO_VLD4W(vld42w, 4, 5, 14, 15) +DO_VLD4W(vld43w, 6, 7, 8, 9) + +#define DO_VLD2B(OP, O1, O2, O3, O4) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat, e; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O2, O3, O4 }; \ + uint32_t addr, data; \ + uint8_t *qd; \ + for (beat = 0; beat < 4; beat++, mask >>= 4) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat] * 2; \ + data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + for (e = 0; e < 4; e++, data >>= 8) { \ + qd = (uint8_t *)aa32_vfp_qreg(env, qnidx + (e & 1)); \ + qd[H1(off[beat] + (e >> 1))] = data; \ + } \ + } \ + } + +#define DO_VLD2H(OP, O1, O2, O3, O4) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O2, O3, O4 }; \ + uint32_t addr, data; \ + int e; \ + uint16_t *qd; \ + for (beat = 0; beat < 4; beat++, mask >>= 4) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat] * 4; \ + data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + for (e = 0; e < 2; e++, data >>= 16) { \ + qd = (uint16_t *)aa32_vfp_qreg(env, qnidx + e); \ + qd[H2(off[beat])] = data; \ + } \ + } \ + } + +#define DO_VLD2W(OP, O1, O2, O3, O4) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O2, O3, O4 }; \ + uint32_t addr, data; \ + uint32_t *qd; \ + for (beat = 0; beat < 4; beat++, mask >>= 4) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat]; \ + data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + qd = (uint32_t *)aa32_vfp_qreg(env, qnidx + (beat & 1)); \ + qd[H4(off[beat] >> 3)] = data; \ + } \ + } + +DO_VLD2B(vld20b, 0, 2, 12, 14) +DO_VLD2B(vld21b, 4, 6, 8, 10) + +DO_VLD2H(vld20h, 0, 1, 6, 7) +DO_VLD2H(vld21h, 2, 3, 4, 5) + +DO_VLD2W(vld20w, 0, 4, 24, 28) +DO_VLD2W(vld21w, 8, 12, 16, 20) + +#define DO_VST4B(OP, O1, O2, O3, O4) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat, e; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O2, O3, O4 }; \ + uint32_t addr, data; \ + for (beat = 0; beat < 4; beat++, mask >>= 4) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat] * 4; \ + data = 0; \ + for (e = 3; e >= 0; e--) { \ + uint8_t *qd = (uint8_t *)aa32_vfp_qreg(env, qnidx + e); \ + data = (data << 8) | qd[H1(off[beat])]; \ + } \ + cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + } \ + } + +#define DO_VST4H(OP, O1, O2) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O1, O2, O2 }; \ + uint32_t addr, data; \ + int y; /* y counts 0 2 0 2 */ \ + uint16_t *qd; \ + for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat] * 8 + (beat & 1) * 4; \ + qd = (uint16_t *)aa32_vfp_qreg(env, qnidx + y); \ + data = qd[H2(off[beat])]; \ + qd = (uint16_t *)aa32_vfp_qreg(env, qnidx + y + 1); \ + data |= qd[H2(off[beat])] << 16; \ + cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + } \ + } + +#define DO_VST4W(OP, O1, O2, O3, O4) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O2, O3, O4 }; \ + uint32_t addr, data; \ + uint32_t *qd; \ + int y; \ + for (beat = 0; beat < 4; beat++, mask >>= 4) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat] * 4; \ + y = (beat + (O1 & 2)) & 3; \ + qd = (uint32_t *)aa32_vfp_qreg(env, qnidx + y); \ + data = qd[H4(off[beat] >> 2)]; \ + cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + } \ + } + +DO_VST4B(vst40b, 0, 1, 10, 11) +DO_VST4B(vst41b, 2, 3, 12, 13) +DO_VST4B(vst42b, 4, 5, 14, 15) +DO_VST4B(vst43b, 6, 7, 8, 9) + +DO_VST4H(vst40h, 0, 5) +DO_VST4H(vst41h, 1, 6) +DO_VST4H(vst42h, 2, 7) +DO_VST4H(vst43h, 3, 4) + +DO_VST4W(vst40w, 0, 1, 10, 11) +DO_VST4W(vst41w, 2, 3, 12, 13) +DO_VST4W(vst42w, 4, 5, 14, 15) +DO_VST4W(vst43w, 6, 7, 8, 9) + +#define DO_VST2B(OP, O1, O2, O3, O4) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat, e; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O2, O3, O4 }; \ + uint32_t addr, data; \ + uint8_t *qd; \ + for (beat = 0; beat < 4; beat++, mask >>= 4) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat] * 2; \ + data = 0; \ + for (e = 3; e >= 0; e--) { \ + qd = (uint8_t *)aa32_vfp_qreg(env, qnidx + (e & 1)); \ + data = (data << 8) | qd[H1(off[beat] + (e >> 1))]; \ + } \ + cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + } \ + } + +#define DO_VST2H(OP, O1, O2, O3, O4) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O2, O3, O4 }; \ + uint32_t addr, data; \ + int e; \ + uint16_t *qd; \ + for (beat = 0; beat < 4; beat++, mask >>= 4) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat] * 4; \ + data = 0; \ + for (e = 1; e >= 0; e--) { \ + qd = (uint16_t *)aa32_vfp_qreg(env, qnidx + e); \ + data = (data << 16) | qd[H2(off[beat])]; \ + } \ + cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + } \ + } + +#define DO_VST2W(OP, O1, O2, O3, O4) \ + void HELPER(mve_##OP)(CPUARMState *env, uint32_t qnidx, \ + uint32_t base) \ + { \ + int beat; \ + uint16_t mask = mve_eci_mask(env); \ + static const uint8_t off[4] = { O1, O2, O3, O4 }; \ + uint32_t addr, data; \ + uint32_t *qd; \ + for (beat = 0; beat < 4; beat++, mask >>= 4) { \ + if ((mask & 1) == 0) { \ + /* ECI says skip this beat */ \ + continue; \ + } \ + addr = base + off[beat]; \ + qd = (uint32_t *)aa32_vfp_qreg(env, qnidx + (beat & 1)); \ + data = qd[H4(off[beat] >> 3)]; \ + cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + } \ + } + +DO_VST2B(vst20b, 0, 2, 12, 14) +DO_VST2B(vst21b, 4, 6, 8, 10) + +DO_VST2H(vst20h, 0, 1, 6, 7) +DO_VST2H(vst21h, 2, 3, 4, 5) + +DO_VST2W(vst20w, 0, 4, 24, 28) +DO_VST2W(vst21w, 8, 12, 16, 20) + /* * The mergemask(D, R, M) macro performs the operation "*D = R" but * storing only the bytes which correspond to 1 bits in M, diff --git a/target/arm/translate-mve.c b/target/arm/translate-mve.c index d3cb339686..78229c44c6 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/translate-mve.c @@ -35,6 +35,7 @@ static inline int vidup_imm(DisasContext *s, int x) typedef void MVEGenLdStFn(TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void MVEGenLdStSGFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); +typedef void MVEGenLdStIlFn(TCGv_ptr, TCGv_i32, TCGv_i32); typedef void MVEGenOneOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); typedef void MVEGenTwoOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr); typedef void MVEGenTwoOpScalarFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); @@ -378,6 +379,99 @@ static bool trans_VSTRD_sg_imm(DisasContext *s, arg_vldst_sg_imm *a) return do_ldst_sg_imm(s, a, fns[a->w], MO_64); } +static bool do_vldst_il(DisasContext *s, arg_vldst_il *a, MVEGenLdStIlFn *fn, + int addrinc) +{ + TCGv_i32 rn; + + if (!dc_isar_feature(aa32_mve, s) || + !mve_check_qreg_bank(s, a->qd) || + !fn || (a->rn == 13 && a->w) || a->rn == 15) { + /* Variously UNPREDICTABLE or UNDEF or related-encoding */ + return false; + } + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + rn = load_reg(s, a->rn); + /* + * We pass the index of Qd, not a pointer, because the helper must + * access multiple Q registers starting at Qd and working up. + */ + fn(cpu_env, tcg_constant_i32(a->qd), rn); + + if (a->w) { + tcg_gen_addi_i32(rn, rn, addrinc); + store_reg(s, a->rn, rn); + } else { + tcg_temp_free_i32(rn); + } + mve_update_and_store_eci(s); + return true; +} + +/* This macro is just to make the arrays more compact in these functions */ +#define F(N) gen_helper_mve_##N + +static bool trans_VLD2(DisasContext *s, arg_vldst_il *a) +{ + static MVEGenLdStIlFn * const fns[4][4] = { + { F(vld20b), F(vld20h), F(vld20w), NULL, }, + { F(vld21b), F(vld21h), F(vld21w), NULL, }, + { NULL, NULL, NULL, NULL }, + { NULL, NULL, NULL, NULL }, + }; + if (a->qd > 6) { + return false; + } + return do_vldst_il(s, a, fns[a->pat][a->size], 32); +} + +static bool trans_VLD4(DisasContext *s, arg_vldst_il *a) +{ + static MVEGenLdStIlFn * const fns[4][4] = { + { F(vld40b), F(vld40h), F(vld40w), NULL, }, + { F(vld41b), F(vld41h), F(vld41w), NULL, }, + { F(vld42b), F(vld42h), F(vld42w), NULL, }, + { F(vld43b), F(vld43h), F(vld43w), NULL, }, + }; + if (a->qd > 4) { + return false; + } + return do_vldst_il(s, a, fns[a->pat][a->size], 64); +} + +static bool trans_VST2(DisasContext *s, arg_vldst_il *a) +{ + static MVEGenLdStIlFn * const fns[4][4] = { + { F(vst20b), F(vst20h), F(vst20w), NULL, }, + { F(vst21b), F(vst21h), F(vst21w), NULL, }, + { NULL, NULL, NULL, NULL }, + { NULL, NULL, NULL, NULL }, + }; + if (a->qd > 6) { + return false; + } + return do_vldst_il(s, a, fns[a->pat][a->size], 32); +} + +static bool trans_VST4(DisasContext *s, arg_vldst_il *a) +{ + static MVEGenLdStIlFn * const fns[4][4] = { + { F(vst40b), F(vst40h), F(vst40w), NULL, }, + { F(vst41b), F(vst41h), F(vst41w), NULL, }, + { F(vst42b), F(vst42h), F(vst42w), NULL, }, + { F(vst43b), F(vst43h), F(vst43w), NULL, }, + }; + if (a->qd > 4) { + return false; + } + return do_vldst_il(s, a, fns[a->pat][a->size], 64); +} + +#undef F + static bool trans_VDUP(DisasContext *s, arg_VDUP *a) { TCGv_ptr qd; From fc7a5038a6b70a3e474e58a5ed9845e4f5eed6dd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 30 Jul 2021 16:16:35 +0100 Subject: [PATCH 041/493] target/arm: Re-indent sdiv and udiv helpers We're about to make a code change to the sdiv and udiv helper functions, so first fix their indentation and coding style. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20210730151636.17254-2-peter.maydell@linaro.org --- target/arm/helper.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 155d8bf239..8e9c2a2cf8 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9355,17 +9355,20 @@ uint32_t HELPER(uxtb16)(uint32_t x) int32_t HELPER(sdiv)(int32_t num, int32_t den) { - if (den == 0) - return 0; - if (num == INT_MIN && den == -1) - return INT_MIN; + if (den == 0) { + return 0; + } + if (num == INT_MIN && den == -1) { + return INT_MIN; + } return num / den; } uint32_t HELPER(udiv)(uint32_t num, uint32_t den) { - if (den == 0) - return 0; + if (den == 0) { + return 0; + } return num / den; } From e5346292966f5348cd36668f2451ca0e44d820b2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 30 Jul 2021 16:16:36 +0100 Subject: [PATCH 042/493] target/arm: Implement M-profile trapping on division by zero Unlike A-profile, for M-profile the UDIV and SDIV insns can be configured to raise an exception on division by zero, using the CCR DIV_0_TRP bit. Implement support for setting this bit by making the helper functions raise the appropriate exception. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20210730151636.17254-3-peter.maydell@linaro.org --- target/arm/cpu.h | 1 + target/arm/helper.c | 19 +++++++++++++++++-- target/arm/helper.h | 4 ++-- target/arm/m_helper.c | 4 ++++ target/arm/translate.c | 4 ++-- 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 9f0a5f84d5..5cf8996ae3 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -54,6 +54,7 @@ #define EXCP_LAZYFP 20 /* v7M fault during lazy FP stacking */ #define EXCP_LSERR 21 /* v8M LSERR SecureFault */ #define EXCP_UNALIGNED 22 /* v7M UNALIGNED UsageFault */ +#define EXCP_DIVBYZERO 23 /* v7M DIVBYZERO UsageFault */ /* NB: add new EXCP_ defines to the array in arm_log_exception() too */ #define ARMV7M_EXCP_RESET 1 diff --git a/target/arm/helper.c b/target/arm/helper.c index 8e9c2a2cf8..56c520cf8e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9345,6 +9345,18 @@ uint32_t HELPER(sxtb16)(uint32_t x) return res; } +static void handle_possible_div0_trap(CPUARMState *env, uintptr_t ra) +{ + /* + * Take a division-by-zero exception if necessary; otherwise return + * to get the usual non-trapping division behaviour (result of 0) + */ + if (arm_feature(env, ARM_FEATURE_M) + && (env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_DIV_0_TRP_MASK)) { + raise_exception_ra(env, EXCP_DIVBYZERO, 0, 1, ra); + } +} + uint32_t HELPER(uxtb16)(uint32_t x) { uint32_t res; @@ -9353,9 +9365,10 @@ uint32_t HELPER(uxtb16)(uint32_t x) return res; } -int32_t HELPER(sdiv)(int32_t num, int32_t den) +int32_t HELPER(sdiv)(CPUARMState *env, int32_t num, int32_t den) { if (den == 0) { + handle_possible_div0_trap(env, GETPC()); return 0; } if (num == INT_MIN && den == -1) { @@ -9364,9 +9377,10 @@ int32_t HELPER(sdiv)(int32_t num, int32_t den) return num / den; } -uint32_t HELPER(udiv)(uint32_t num, uint32_t den) +uint32_t HELPER(udiv)(CPUARMState *env, uint32_t num, uint32_t den) { if (den == 0) { + handle_possible_div0_trap(env, GETPC()); return 0; } return num / den; @@ -9567,6 +9581,7 @@ void arm_log_exception(int idx) [EXCP_LAZYFP] = "v7M exception during lazy FP stacking", [EXCP_LSERR] = "v8M LSERR UsageFault", [EXCP_UNALIGNED] = "v7M UNALIGNED UsageFault", + [EXCP_DIVBYZERO] = "v7M DIVBYZERO UsageFault", }; if (idx >= 0 && idx < ARRAY_SIZE(excnames)) { diff --git a/target/arm/helper.h b/target/arm/helper.h index 248569b0cd..aee8f0019b 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -6,8 +6,8 @@ DEF_HELPER_3(add_saturate, i32, env, i32, i32) DEF_HELPER_3(sub_saturate, i32, env, i32, i32) DEF_HELPER_3(add_usaturate, i32, env, i32, i32) DEF_HELPER_3(sub_usaturate, i32, env, i32, i32) -DEF_HELPER_FLAGS_2(sdiv, TCG_CALL_NO_RWG_SE, s32, s32, s32) -DEF_HELPER_FLAGS_2(udiv, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_RWG, s32, env, s32, s32) +DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_RWG, i32, env, i32, i32) DEF_HELPER_FLAGS_1(rbit, TCG_CALL_NO_RWG_SE, i32, i32) #define PAS_OP(pfx) \ diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c index 20761c9487..47903b3dc3 100644 --- a/target/arm/m_helper.c +++ b/target/arm/m_helper.c @@ -2252,6 +2252,10 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_UNALIGNED_MASK; break; + case EXCP_DIVBYZERO: + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_DIVBYZERO_MASK; + break; case EXCP_SWI: /* The PC already points to the next instruction. */ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC, env->v7m.secure); diff --git a/target/arm/translate.c b/target/arm/translate.c index 804a53279b..115aa768b6 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -7992,9 +7992,9 @@ static bool op_div(DisasContext *s, arg_rrr *a, bool u) t1 = load_reg(s, a->rn); t2 = load_reg(s, a->rm); if (u) { - gen_helper_udiv(t1, t1, t2); + gen_helper_udiv(t1, cpu_env, t1, t2); } else { - gen_helper_sdiv(t1, t1, t2); + gen_helper_sdiv(t1, cpu_env, t1, t2); } tcg_temp_free_i32(t2); store_reg(s, a->rd, t1); From dfa0d9b80ed36c3e3a92346c35e7e7b1e4afc49d Mon Sep 17 00:00:00 2001 From: Hamza Mahfooz Date: Tue, 27 Jul 2021 19:52:01 -0400 Subject: [PATCH 043/493] target/arm: kvm: use RCU_READ_LOCK_GUARD() in kvm_arch_fixup_msi_route() As per commit 5626f8c6d468 ("rcu: Add automatically released rcu_read_lock variants"), RCU_READ_LOCK_GUARD() should be used instead of rcu_read_{un}lock(). Signed-off-by: Hamza Mahfooz Reviewed-by: Paolo Bonzini Message-id: 20210727235201.11491-1-someguy@effective-light.com Signed-off-by: Peter Maydell --- target/arm/kvm.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index d8381ba224..5d55de1a49 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -998,7 +998,6 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, hwaddr xlat, len, doorbell_gpa; MemoryRegionSection mrs; MemoryRegion *mr; - int ret = 1; if (as == &address_space_memory) { return 0; @@ -1006,15 +1005,19 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, /* MSI doorbell address is translated by an IOMMU */ - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); + mr = address_space_translate(as, address, &xlat, &len, true, MEMTXATTRS_UNSPECIFIED); + if (!mr) { - goto unlock; + return 1; } + mrs = memory_region_find(mr, xlat, 1); + if (!mrs.mr) { - goto unlock; + return 1; } doorbell_gpa = mrs.offset_within_address_space; @@ -1025,11 +1028,7 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, trace_kvm_arm_fixup_msi_route(address, doorbell_gpa); - ret = 0; - -unlock: - rcu_read_unlock(); - return ret; + return 0; } int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, From d60af909d5131300596d4f6f052b2d0f44e76560 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Fri, 6 Aug 2021 16:47:00 +0200 Subject: [PATCH 044/493] hw/char/pl011: add support for sending break Break events are currently only handled by chardev/char-serial.c, so we just ignore errors, which results in no behaviour change for other chardevs. Signed-off-by: Jan Luebbe Message-id: 20210806144700.3751979-1-jlu@pengutronix.de Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/char/pl011.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/char/pl011.c b/hw/char/pl011.c index dc85527a5f..6e2d7f7509 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -26,6 +26,7 @@ #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" #include "chardev/char-fe.h" +#include "chardev/char-serial.h" #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" @@ -231,6 +232,11 @@ static void pl011_write(void *opaque, hwaddr offset, s->read_count = 0; s->read_pos = 0; } + if ((s->lcr ^ value) & 0x1) { + int break_enable = value & 0x1; + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK, + &break_enable); + } s->lcr = value; pl011_set_read_trigger(s); break; From ff31cca71ef257f8da2d9bc647ddd53f080ce580 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 10 Aug 2021 09:03:18 -0700 Subject: [PATCH 045/493] fsl-imx6ul: Instantiate SAI1/2/3 and ASRC as unimplemented devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instantiate SAI1/2/3 and ASRC as unimplemented devices to avoid random Linux kernel crashes, such as Unhandled fault: external abort on non-linefetch (0x808) at 0xd1580010 pgd = (ptrval) [d1580010] *pgd=8231b811, *pte=02034653, *ppte=02034453 Internal error: : 808 [#1] SMP ARM ... [] (regmap_mmio_write32le) from [] (regmap_mmio_write+0x3c/0x54) [] (regmap_mmio_write) from [] (_regmap_write+0x4c/0x1f0) [] (_regmap_write) from [] (_regmap_update_bits+0xe4/0xec) [] (_regmap_update_bits) from [] (regmap_update_bits_base+0x50/0x74) [] (regmap_update_bits_base) from [] (fsl_asrc_runtime_resume+0x1e4/0x21c) [] (fsl_asrc_runtime_resume) from [] (__rpm_callback+0x3c/0x108) [] (__rpm_callback) from [] (rpm_callback+0x60/0x64) [] (rpm_callback) from [] (rpm_resume+0x5cc/0x808) [] (rpm_resume) from [] (__pm_runtime_resume+0x60/0xa0) [] (__pm_runtime_resume) from [] (fsl_asrc_probe+0x2a8/0x708) [] (fsl_asrc_probe) from [] (platform_probe+0x58/0xb8) [] (platform_probe) from [] (really_probe.part.0+0x9c/0x334) [] (really_probe.part.0) from [] (__driver_probe_device+0xa0/0x138) [] (__driver_probe_device) from [] (driver_probe_device+0x30/0xc8) [] (driver_probe_device) from [] (__driver_attach+0x90/0x130) [] (__driver_attach) from [] (bus_for_each_dev+0x78/0xb8) [] (bus_for_each_dev) from [] (bus_add_driver+0xf0/0x1d8) [] (bus_add_driver) from [] (driver_register+0x88/0x118) [] (driver_register) from [] (do_one_initcall+0x7c/0x3a4) [] (do_one_initcall) from [] (kernel_init_freeable+0x198/0x22c) [] (kernel_init_freeable) from [] (kernel_init+0x10/0x128) [] (kernel_init) from [] (ret_from_fork+0x14/0x38) or Unhandled fault: external abort on non-linefetch (0x808) at 0xd19b0000 pgd = (ptrval) [d19b0000] *pgd=82711811, *pte=308a0653, *ppte=308a0453 Internal error: : 808 [#1] SMP ARM ... [] (regmap_mmio_write32le) from [] (regmap_mmio_write+0x3c/0x54) [] (regmap_mmio_write) from [] (_regmap_write+0x4c/0x1f0) [] (_regmap_write) from [] (regmap_write+0x3c/0x60) [] (regmap_write) from [] (fsl_sai_runtime_resume+0x9c/0x1ec) [] (fsl_sai_runtime_resume) from [] (__rpm_callback+0x3c/0x108) [] (__rpm_callback) from [] (rpm_callback+0x60/0x64) [] (rpm_callback) from [] (rpm_resume+0x5cc/0x808) [] (rpm_resume) from [] (__pm_runtime_resume+0x60/0xa0) [] (__pm_runtime_resume) from [] (fsl_sai_probe+0x2b8/0x65c) [] (fsl_sai_probe) from [] (platform_probe+0x58/0xb8) [] (platform_probe) from [] (really_probe.part.0+0x9c/0x334) [] (really_probe.part.0) from [] (__driver_probe_device+0xa0/0x138) [] (__driver_probe_device) from [] (driver_probe_device+0x30/0xc8) [] (driver_probe_device) from [] (__driver_attach+0x90/0x130) [] (__driver_attach) from [] (bus_for_each_dev+0x78/0xb8) [] (bus_for_each_dev) from [] (bus_add_driver+0xf0/0x1d8) [] (bus_add_driver) from [] (driver_register+0x88/0x118) [] (driver_register) from [] (do_one_initcall+0x7c/0x3a4) [] (do_one_initcall) from [] (kernel_init_freeable+0x198/0x22c) [] (kernel_init_freeable) from [] (kernel_init+0x10/0x128) [] (kernel_init) from [] (ret_from_fork+0x14/0x38) Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Guenter Roeck Message-id: 20210810160318.87376-1-linux@roeck-us.net Signed-off-by: Peter Maydell --- hw/arm/fsl-imx6ul.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index e0128d7316..1d1a708dd9 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -534,6 +534,13 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) */ create_unimplemented_device("sdma", FSL_IMX6UL_SDMA_ADDR, 0x4000); + /* + * SAI (Audio SSI (Synchronous Serial Interface)) + */ + create_unimplemented_device("sai1", FSL_IMX6UL_SAI1_ADDR, 0x4000); + create_unimplemented_device("sai2", FSL_IMX6UL_SAI2_ADDR, 0x4000); + create_unimplemented_device("sai3", FSL_IMX6UL_SAI3_ADDR, 0x4000); + /* * PWM */ @@ -542,6 +549,11 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) create_unimplemented_device("pwm3", FSL_IMX6UL_PWM3_ADDR, 0x4000); create_unimplemented_device("pwm4", FSL_IMX6UL_PWM4_ADDR, 0x4000); + /* + * Audio ASRC (asynchronous sample rate converter) + */ + create_unimplemented_device("asrc", FSL_IMX6UL_ASRC_ADDR, 0x4000); + /* * CAN */ From 77844cc51aa0714d54ae6f5a12279ce0e7f5ef55 Mon Sep 17 00:00:00 2001 From: "Wen, Jianxian" Date: Wed, 18 Aug 2021 10:17:00 +0000 Subject: [PATCH 046/493] hw/dma/pl330: Add memory region to replace default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add property memory region which can connect with IOMMU region to support SMMU translate. Signed-off-by: Jianxian Wen Reviewed-by: Philippe Mathieu-Daudé Message-id: 4C23C17B8E87E74E906A25A3254A03F4FA1FEC31@SHASXM03.verisilicon.com Signed-off-by: Peter Maydell --- hw/arm/exynos4210.c | 3 +++ hw/arm/xilinx_zynq.c | 3 +++ hw/dma/pl330.c | 26 ++++++++++++++++++++++---- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 5c7a51bbad..0299e81f85 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -173,6 +173,9 @@ static DeviceState *pl330_create(uint32_t base, qemu_or_irq *orgate, int i; dev = qdev_new("pl330"); + object_property_set_link(OBJECT(dev), "memory", + OBJECT(get_system_memory()), + &error_fatal); qdev_prop_set_uint8(dev, "num_events", nevents); qdev_prop_set_uint8(dev, "num_chnls", 8); qdev_prop_set_uint8(dev, "num_periph_req", nreq); diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 245af81bbb..69c333e91b 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -312,6 +312,9 @@ static void zynq_init(MachineState *machine) sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-IRQ_OFFSET]); dev = qdev_new("pl330"); + object_property_set_link(OBJECT(dev), "memory", + OBJECT(address_space_mem), + &error_fatal); qdev_prop_set_uint8(dev, "num_chnls", 8); qdev_prop_set_uint8(dev, "num_periph_req", 4); qdev_prop_set_uint8(dev, "num_events", 16); diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index 944ba296b0..0cb46191c1 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -269,6 +269,9 @@ struct PL330State { uint8_t num_faulting; uint8_t periph_busy[PL330_PERIPH_NUM]; + /* Memory region that DMA operation access */ + MemoryRegion *mem_mr; + AddressSpace *mem_as; }; #define TYPE_PL330 "pl330" @@ -1108,7 +1111,7 @@ static inline const PL330InsnDesc *pl330_fetch_insn(PL330Chan *ch) uint8_t opcode; int i; - dma_memory_read(&address_space_memory, ch->pc, &opcode, 1); + dma_memory_read(ch->parent->mem_as, ch->pc, &opcode, 1); for (i = 0; insn_desc[i].size; i++) { if ((opcode & insn_desc[i].opmask) == insn_desc[i].opcode) { return &insn_desc[i]; @@ -1122,7 +1125,7 @@ static inline void pl330_exec_insn(PL330Chan *ch, const PL330InsnDesc *insn) uint8_t buf[PL330_INSN_MAXSIZE]; assert(insn->size <= PL330_INSN_MAXSIZE); - dma_memory_read(&address_space_memory, ch->pc, buf, insn->size); + dma_memory_read(ch->parent->mem_as, ch->pc, buf, insn->size); insn->exec(ch, buf[0], &buf[1], insn->size - 1); } @@ -1186,7 +1189,7 @@ static int pl330_exec_cycle(PL330Chan *channel) if (q != NULL && q->len <= pl330_fifo_num_free(&s->fifo)) { int len = q->len - (q->addr & (q->len - 1)); - dma_memory_read(&address_space_memory, q->addr, buf, len); + dma_memory_read(s->mem_as, q->addr, buf, len); trace_pl330_exec_cycle(q->addr, len); if (trace_event_get_state_backends(TRACE_PL330_HEXDUMP)) { pl330_hexdump(buf, len); @@ -1217,7 +1220,7 @@ static int pl330_exec_cycle(PL330Chan *channel) fifo_res = pl330_fifo_get(&s->fifo, buf, len, q->tag); } if (fifo_res == PL330_FIFO_OK || q->z) { - dma_memory_write(&address_space_memory, q->addr, buf, len); + dma_memory_write(s->mem_as, q->addr, buf, len); trace_pl330_exec_cycle(q->addr, len); if (trace_event_get_state_backends(TRACE_PL330_HEXDUMP)) { pl330_hexdump(buf, len); @@ -1562,6 +1565,18 @@ static void pl330_realize(DeviceState *dev, Error **errp) "dma", PL330_IOMEM_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + if (!s->mem_mr) { + error_setg(errp, "'memory' link is not set"); + return; + } else if (s->mem_mr == get_system_memory()) { + /* Avoid creating new AS for system memory. */ + s->mem_as = &address_space_memory; + } else { + s->mem_as = g_new0(AddressSpace, 1); + address_space_init(s->mem_as, s->mem_mr, + memory_region_name(s->mem_mr)); + } + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pl330_exec_cycle_timer, s); s->cfg[0] = (s->mgr_ns_at_rst ? 0x4 : 0) | @@ -1656,6 +1671,9 @@ static Property pl330_properties[] = { DEFINE_PROP_UINT8("rd_q_dep", PL330State, rd_q_dep, 16), DEFINE_PROP_UINT16("data_buffer_dep", PL330State, data_buffer_dep, 256), + DEFINE_PROP_LINK("memory", PL330State, mem_mr, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), }; From 80d60a6d1efebcf35ff96e2d0a51373b0383bc10 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 5 Aug 2021 22:31:19 -0400 Subject: [PATCH 047/493] sbsa-ref: Rename SBSA_GWDT enum value The SBSA_GWDT enum value conflicts with the SBSA_GWDT() QOM type checking helper, preventing us from using a OBJECT_DEFINE* or DEFINE_INSTANCE_CHECKER macro for the SBSA_GWDT() wrapper. If I understand the SBSA 6.0 specification correctly, the signal being connected to IRQ 16 is the WS0 output signal from the Generic Watchdog. Rename the enum value to SBSA_GWDT_WS0 to be more explicit and avoid the name conflict. Signed-off-by: Eduardo Habkost Message-id: 20210806023119.431680-1-ehabkost@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/sbsa-ref.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index c1629df603..509c5f09b4 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -65,7 +65,7 @@ enum { SBSA_GIC_DIST, SBSA_GIC_REDIST, SBSA_SECURE_EC, - SBSA_GWDT, + SBSA_GWDT_WS0, SBSA_GWDT_REFRESH, SBSA_GWDT_CONTROL, SBSA_SMMU, @@ -140,7 +140,7 @@ static const int sbsa_ref_irqmap[] = { [SBSA_AHCI] = 10, [SBSA_EHCI] = 11, [SBSA_SMMU] = 12, /* ... to 15 */ - [SBSA_GWDT] = 16, + [SBSA_GWDT_WS0] = 16, }; static const char * const valid_cpus[] = { @@ -481,7 +481,7 @@ static void create_wdt(const SBSAMachineState *sms) hwaddr cbase = sbsa_ref_memmap[SBSA_GWDT_CONTROL].base; DeviceState *dev = qdev_new(TYPE_WDT_SBSA); SysBusDevice *s = SYS_BUS_DEVICE(dev); - int irq = sbsa_ref_irqmap[SBSA_GWDT]; + int irq = sbsa_ref_irqmap[SBSA_GWDT_WS0]; sysbus_realize_and_unref(s, &error_fatal); sysbus_mmio_map(s, 0, rbase); From 6f287c700c5fad924585dee1308477aa9e73ae50 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 10 Aug 2021 10:56:07 -0700 Subject: [PATCH 048/493] fsl-imx7: Instantiate SAI1/2/3 as unimplemented devices Instantiate SAI1/2/3 as unimplemented devices to avoid Linux kernel crashes such as the following. Unhandled fault: external abort on non-linefetch (0x808) at 0xd19b0000 pgd = (ptrval) [d19b0000] *pgd=82711811, *pte=308a0653, *ppte=308a0453 Internal error: : 808 [#1] SMP ARM Modules linked in: CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.14.0-rc5 #1 ... [] (regmap_mmio_write32le) from [] (regmap_mmio_write+0x3c/0x54) [] (regmap_mmio_write) from [] (_regmap_write+0x4c/0x1f0) [] (_regmap_write) from [] (regmap_write+0x3c/0x60) [] (regmap_write) from [] (fsl_sai_runtime_resume+0x9c/0x1ec) [] (fsl_sai_runtime_resume) from [] (__rpm_callback+0x3c/0x108) [] (__rpm_callback) from [] (rpm_callback+0x60/0x64) [] (rpm_callback) from [] (rpm_resume+0x5cc/0x808) [] (rpm_resume) from [] (__pm_runtime_resume+0x60/0xa0) [] (__pm_runtime_resume) from [] (fsl_sai_probe+0x2b8/0x65c) [] (fsl_sai_probe) from [] (platform_probe+0x58/0xb8) [] (platform_probe) from [] (really_probe.part.0+0x9c/0x334) [] (really_probe.part.0) from [] (__driver_probe_device+0xa0/0x138) [] (__driver_probe_device) from [] (driver_probe_device+0x30/0xc8) [] (driver_probe_device) from [] (__driver_attach+0x90/0x130) [] (__driver_attach) from [] (bus_for_each_dev+0x78/0xb8) [] (bus_for_each_dev) from [] (bus_add_driver+0xf0/0x1d8) [] (bus_add_driver) from [] (driver_register+0x88/0x118) [] (driver_register) from [] (do_one_initcall+0x7c/0x3a4) [] (do_one_initcall) from [] (kernel_init_freeable+0x198/0x22c) [] (kernel_init_freeable) from [] (kernel_init+0x10/0x128) [] (kernel_init) from [] (ret_from_fork+0x14/0x38) Signed-off-by: Guenter Roeck Message-id: 20210810175607.538090-1-linux@roeck-us.net Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/fsl-imx7.c | 7 +++++++ include/hw/arm/fsl-imx7.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 2ff2cab924..149885f2b8 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -467,6 +467,13 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) create_unimplemented_device("can1", FSL_IMX7_CAN1_ADDR, FSL_IMX7_CANn_SIZE); create_unimplemented_device("can2", FSL_IMX7_CAN2_ADDR, FSL_IMX7_CANn_SIZE); + /* + * SAI (Audio SSI (Synchronous Serial Interface)) + */ + create_unimplemented_device("sai1", FSL_IMX7_SAI1_ADDR, FSL_IMX7_SAIn_SIZE); + create_unimplemented_device("sai2", FSL_IMX7_SAI2_ADDR, FSL_IMX7_SAIn_SIZE); + create_unimplemented_device("sai2", FSL_IMX7_SAI3_ADDR, FSL_IMX7_SAIn_SIZE); + /* * OCOTP */ diff --git a/include/hw/arm/fsl-imx7.h b/include/hw/arm/fsl-imx7.h index f5d527a490..1c5fa6fd67 100644 --- a/include/hw/arm/fsl-imx7.h +++ b/include/hw/arm/fsl-imx7.h @@ -174,6 +174,11 @@ enum FslIMX7MemoryMap { FSL_IMX7_UART6_ADDR = 0x30A80000, FSL_IMX7_UART7_ADDR = 0x30A90000, + FSL_IMX7_SAI1_ADDR = 0x308A0000, + FSL_IMX7_SAI2_ADDR = 0x308B0000, + FSL_IMX7_SAI3_ADDR = 0x308C0000, + FSL_IMX7_SAIn_SIZE = 0x10000, + FSL_IMX7_ENET1_ADDR = 0x30BE0000, FSL_IMX7_ENET2_ADDR = 0x30BF0000, From 24b1a6aa43615be22c7ee66bd68ec5675f6a6a9a Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Tue, 10 Aug 2021 18:04:36 +0200 Subject: [PATCH 049/493] docs: Document how to use gdb with unix sockets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With gdb 9.0 and better it is possible to connect to a gdbstub over unix sockets, which is better than a TCP socket connection in some situations. The QEMU command line to set this up is non-obvious; document it. Signed-off-by: Sebastian Meyer Message-id: 162867284829.27377.4784930719350564918-0@git.sr.ht [PMM: Tweaked commit message; adjusted wording in a couple of places; fixed rST formatting issue; moved section up out of the 'advanced debugging options' subsection] Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- docs/system/gdb.rst | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/system/gdb.rst b/docs/system/gdb.rst index 144d083df3..bdb42dae2f 100644 --- a/docs/system/gdb.rst +++ b/docs/system/gdb.rst @@ -15,7 +15,8 @@ The ``-s`` option will make QEMU listen for an incoming connection from gdb on TCP port 1234, and ``-S`` will make QEMU not start the guest until you tell it to from gdb. (If you want to specify which TCP port to use or to use something other than TCP for the gdbstub -connection, use the ``-gdb dev`` option instead of ``-s``.) +connection, use the ``-gdb dev`` option instead of ``-s``. See +`Using unix sockets`_ for an example.) .. parsed-literal:: @@ -100,6 +101,29 @@ not just those in the cluster you are currently working on:: (gdb) set schedule-multiple on +Using unix sockets +================== + +An alternate method for connecting gdb to the QEMU gdbstub is to use +a unix socket (if supported by your operating system). This is useful when +running several tests in parallel, or if you do not have a known free TCP +port (e.g. when running automated tests). + +First create a chardev with the appropriate options, then +instruct the gdbserver to use that device: + +.. parsed-literal:: + + |qemu_system| -chardev socket,path=/tmp/gdb-socket,server=on,wait=off,id=gdb0 -gdb chardev:gdb0 -S ... + +Start gdb as before, but this time connect using the path to +the socket:: + + (gdb) target remote /tmp/gdb-socket + +Note that to use a unix socket for the connection you will need +gdb version 9.0 or newer. + Advanced debugging options ========================== From 41f421e0b5821775ec9bd0cb55e534d0e8db7754 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 18 Aug 2021 13:29:08 +0200 Subject: [PATCH 050/493] docs/about/removed-features: Move some CLI options to the right location Some of the removed CLI options have been added to the wrong section in the "Removed features" chapter - they've been put into the "Related binaries" section instead. Move them now into the correct "System emulator command line arguments" section. Message-Id: <20210818112908.102205-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- docs/about/removed-features.rst | 124 ++++++++++++++++---------------- 1 file changed, 61 insertions(+), 63 deletions(-) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 8feeead449..08f9e625ce 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -146,12 +146,73 @@ Replaced by ``-rtc base=date``. The "tls-creds" option should be used instead to point to a "tls-creds-x509" object created using "-object". +``-mem-path`` fallback to RAM (removed in 5.0) +'''''''''''''''''''''''''''''''''''''''''''''' + +If guest RAM allocation from file pointed by ``mem-path`` failed, +QEMU was falling back to allocating from RAM, which might have resulted +in unpredictable behavior since the backing file specified by the user +as ignored. Currently, users are responsible for making sure the backing storage +specified with ``-mem-path`` can actually provide the guest RAM configured with +``-m`` and QEMU fails to start up if RAM allocation is unsuccessful. + ``-net ...,name=...`` (removed in 5.1) '''''''''''''''''''''''''''''''''''''' The ``name`` parameter of the ``-net`` option was a synonym for the ``id`` parameter, which should now be used instead. +``-numa node,mem=...`` (removed in 5.1) +''''''''''''''''''''''''''''''''''''''' + +The parameter ``mem`` of ``-numa node`` was used to assign a part of guest RAM +to a NUMA node. But when using it, it's impossible to manage a specified RAM +chunk on the host side (like bind it to a host node, setting bind policy, ...), +so the guest ends up with the fake NUMA configuration with suboptiomal +performance. +However since 2014 there is an alternative way to assign RAM to a NUMA node +using parameter ``memdev``, which does the same as ``mem`` and adds +means to actually manage node RAM on the host side. Use parameter ``memdev`` +with *memory-backend-ram* backend as replacement for parameter ``mem`` +to achieve the same fake NUMA effect or a properly configured +*memory-backend-file* backend to actually benefit from NUMA configuration. +New machine versions (since 5.1) will not accept the option but it will still +work with old machine types. User can check the QAPI schema to see if the legacy +option is supported by looking at MachineInfo::numa-mem-supported property. + +``-numa`` node (without memory specified) (removed in 5.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Splitting RAM by default between NUMA nodes had the same issues as ``mem`` +parameter with the difference that the role of the user plays QEMU using +implicit generic or board specific splitting rule. +Use ``memdev`` with *memory-backend-ram* backend or ``mem`` (if +it's supported by used machine type) to define mapping explicitly instead. +Users of existing VMs, wishing to preserve the same RAM distribution, should +configure it explicitly using ``-numa node,memdev`` options. Current RAM +distribution can be retrieved using HMP command ``info numa`` and if separate +memory devices (pc|nv-dimm) are present use ``info memory-device`` and subtract +device memory from output of ``info numa``. + +``-smp`` (invalid topologies) (removed in 5.2) +'''''''''''''''''''''''''''''''''''''''''''''' + +CPU topology properties should describe whole machine topology including +possible CPUs. + +However, historically it was possible to start QEMU with an incorrect topology +where *n* <= *sockets* * *cores* * *threads* < *maxcpus*, +which could lead to an incorrect topology enumeration by the guest. +Support for invalid topologies is removed, the user must ensure +topologies described with -smp include all possible cpus, i.e. +*sockets* * *cores* * *threads* = *maxcpus*. + +``-machine enforce-config-section=on|off`` (removed in 5.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``enforce-config-section`` property was replaced by the +``-global migration.send-configuration={on|off}`` option. + ``-no-kvm`` (removed in 5.2) '''''''''''''''''''''''''''' @@ -590,69 +651,6 @@ enforce that any failure to open the backing image (including if the backing file is missing or an incorrect format was specified) is an error when ``-u`` is not used. -Command line options --------------------- - -``-smp`` (invalid topologies) (removed in 5.2) -'''''''''''''''''''''''''''''''''''''''''''''' - -CPU topology properties should describe whole machine topology including -possible CPUs. - -However, historically it was possible to start QEMU with an incorrect topology -where *n* <= *sockets* * *cores* * *threads* < *maxcpus*, -which could lead to an incorrect topology enumeration by the guest. -Support for invalid topologies is removed, the user must ensure -topologies described with -smp include all possible cpus, i.e. -*sockets* * *cores* * *threads* = *maxcpus*. - -``-numa`` node (without memory specified) (removed in 5.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Splitting RAM by default between NUMA nodes had the same issues as ``mem`` -parameter with the difference that the role of the user plays QEMU using -implicit generic or board specific splitting rule. -Use ``memdev`` with *memory-backend-ram* backend or ``mem`` (if -it's supported by used machine type) to define mapping explicitly instead. -Users of existing VMs, wishing to preserve the same RAM distribution, should -configure it explicitly using ``-numa node,memdev`` options. Current RAM -distribution can be retrieved using HMP command ``info numa`` and if separate -memory devices (pc|nv-dimm) are present use ``info memory-device`` and subtract -device memory from output of ``info numa``. - -``-numa node,mem=``\ *size* (removed in 5.1) -'''''''''''''''''''''''''''''''''''''''''''' - -The parameter ``mem`` of ``-numa node`` was used to assign a part of -guest RAM to a NUMA node. But when using it, it's impossible to manage a specified -RAM chunk on the host side (like bind it to a host node, setting bind policy, ...), -so the guest ends up with the fake NUMA configuration with suboptiomal performance. -However since 2014 there is an alternative way to assign RAM to a NUMA node -using parameter ``memdev``, which does the same as ``mem`` and adds -means to actually manage node RAM on the host side. Use parameter ``memdev`` -with *memory-backend-ram* backend as replacement for parameter ``mem`` -to achieve the same fake NUMA effect or a properly configured -*memory-backend-file* backend to actually benefit from NUMA configuration. -New machine versions (since 5.1) will not accept the option but it will still -work with old machine types. User can check the QAPI schema to see if the legacy -option is supported by looking at MachineInfo::numa-mem-supported property. - -``-mem-path`` fallback to RAM (removed in 5.0) -'''''''''''''''''''''''''''''''''''''''''''''' - -If guest RAM allocation from file pointed by ``mem-path`` failed, -QEMU was falling back to allocating from RAM, which might have resulted -in unpredictable behavior since the backing file specified by the user -as ignored. Currently, users are responsible for making sure the backing storage -specified with ``-mem-path`` can actually provide the guest RAM configured with -``-m`` and QEMU fails to start up if RAM allocation is unsuccessful. - -``-machine enforce-config-section=on|off`` (removed in 5.2) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -The ``enforce-config-section`` property was replaced by the -``-global migration.send-configuration={on|off}`` option. - qemu-img amend to adjust backing file (removed in 6.1) '''''''''''''''''''''''''''''''''''''''''''''''''''''' From 1ff4f90add806b6cb6ef1563e6e4ace1fae14548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 12 Aug 2021 19:04:02 +0100 Subject: [PATCH 051/493] docs: split the CI docs into two files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This splits the CI docs into one file talking about job setup and usage and another file describing provisioning of custom runners. Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Willian Rampazzo Message-Id: <20210812180403.4129067-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- docs/devel/ci-jobs.rst | 40 ++++++++++ docs/devel/ci-runners.rst | 117 ++++++++++++++++++++++++++++ docs/devel/ci.rst | 159 +------------------------------------- 3 files changed, 159 insertions(+), 157 deletions(-) create mode 100644 docs/devel/ci-jobs.rst create mode 100644 docs/devel/ci-runners.rst diff --git a/docs/devel/ci-jobs.rst b/docs/devel/ci-jobs.rst new file mode 100644 index 0000000000..9cd9819786 --- /dev/null +++ b/docs/devel/ci-jobs.rst @@ -0,0 +1,40 @@ +Custom CI/CD variables +====================== + +QEMU CI pipelines can be tuned by setting some CI environment variables. + +Set variable globally in the user's CI namespace +------------------------------------------------ + +Variables can be set globally in the user's CI namespace setting. + +For further information about how to set these variables, please refer to:: + + https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-a-project + +Set variable manually when pushing a branch or tag to the user's repository +--------------------------------------------------------------------------- + +Variables can be set manually when pushing a branch or tag, using +git-push command line arguments. + +Example setting the QEMU_CI_EXAMPLE_VAR variable: + +.. code:: + + git push -o ci.variable="QEMU_CI_EXAMPLE_VAR=value" myrepo mybranch + +For further information about how to set these variables, please refer to:: + + https://docs.gitlab.com/ee/user/project/push_options.html#push-options-for-gitlab-cicd + +Here is a list of the most used variables: + +QEMU_CI_AVOCADO_TESTING +~~~~~~~~~~~~~~~~~~~~~~~ +By default, tests using the Avocado framework are not run automatically in +the pipelines (because multiple artifacts have to be downloaded, and if +these artifacts are not already cached, downloading them make the jobs +reach the timeout limit). Set this variable to have the tests using the +Avocado framework run automatically. + diff --git a/docs/devel/ci-runners.rst b/docs/devel/ci-runners.rst new file mode 100644 index 0000000000..7817001fb2 --- /dev/null +++ b/docs/devel/ci-runners.rst @@ -0,0 +1,117 @@ +Jobs on Custom Runners +====================== + +Besides the jobs run under the various CI systems listed before, there +are a number additional jobs that will run before an actual merge. +These use the same GitLab CI's service/framework already used for all +other GitLab based CI jobs, but rely on additional systems, not the +ones provided by GitLab as "shared runners". + +The architecture of GitLab's CI service allows different machines to +be set up with GitLab's "agent", called gitlab-runner, which will take +care of running jobs created by events such as a push to a branch. +Here, the combination of a machine, properly configured with GitLab's +gitlab-runner, is called a "custom runner". + +The GitLab CI jobs definition for the custom runners are located under:: + + .gitlab-ci.d/custom-runners.yml + +Custom runners entail custom machines. To see a list of the machines +currently deployed in the QEMU GitLab CI and their maintainers, please +refer to the QEMU `wiki `__. + +Machine Setup Howto +------------------- + +For all Linux based systems, the setup can be mostly automated by the +execution of two Ansible playbooks. Create an ``inventory`` file +under ``scripts/ci/setup``, such as this:: + + fully.qualified.domain + other.machine.hostname + +You may need to set some variables in the inventory file itself. One +very common need is to tell Ansible to use a Python 3 interpreter on +those hosts. This would look like:: + + fully.qualified.domain ansible_python_interpreter=/usr/bin/python3 + other.machine.hostname ansible_python_interpreter=/usr/bin/python3 + +Build environment +~~~~~~~~~~~~~~~~~ + +The ``scripts/ci/setup/build-environment.yml`` Ansible playbook will +set up machines with the environment needed to perform builds and run +QEMU tests. This playbook consists on the installation of various +required packages (and a general package update while at it). It +currently covers a number of different Linux distributions, but it can +be expanded to cover other systems. + +The minimum required version of Ansible successfully tested in this +playbook is 2.8.0 (a version check is embedded within the playbook +itself). To run the playbook, execute:: + + cd scripts/ci/setup + ansible-playbook -i inventory build-environment.yml + +Please note that most of the tasks in the playbook require superuser +privileges, such as those from the ``root`` account or those obtained +by ``sudo``. If necessary, please refer to ``ansible-playbook`` +options such as ``--become``, ``--become-method``, ``--become-user`` +and ``--ask-become-pass``. + +gitlab-runner setup and registration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The gitlab-runner agent needs to be installed on each machine that +will run jobs. The association between a machine and a GitLab project +happens with a registration token. To find the registration token for +your repository/project, navigate on GitLab's web UI to: + + * Settings (the gears-like icon at the bottom of the left hand side + vertical toolbar), then + * CI/CD, then + * Runners, and click on the "Expand" button, then + * Under "Set up a specific Runner manually", look for the value under + "And this registration token:" + +Copy the ``scripts/ci/setup/vars.yml.template`` file to +``scripts/ci/setup/vars.yml``. Then, set the +``gitlab_runner_registration_token`` variable to the value obtained +earlier. + +To run the playbook, execute:: + + cd scripts/ci/setup + ansible-playbook -i inventory gitlab-runner.yml + +Following the registration, it's necessary to configure the runner tags, +and optionally other configurations on the GitLab UI. Navigate to: + + * Settings (the gears like icon), then + * CI/CD, then + * Runners, and click on the "Expand" button, then + * "Runners activated for this project", then + * Click on the "Edit" icon (next to the "Lock" Icon) + +Tags are very important as they are used to route specific jobs to +specific types of runners, so it's a good idea to double check that +the automatically created tags are consistent with the OS and +architecture. For instance, an Ubuntu 20.04 aarch64 system should +have tags set as:: + + ubuntu_20.04,aarch64 + +Because the job definition at ``.gitlab-ci.d/custom-runners.yml`` +would contain:: + + ubuntu-20.04-aarch64-all: + tags: + - ubuntu_20.04 + - aarch64 + +It's also recommended to: + + * increase the "Maximum job timeout" to something like ``2h`` + * give it a better Description diff --git a/docs/devel/ci.rst b/docs/devel/ci.rst index 205572510c..a6a650968b 100644 --- a/docs/devel/ci.rst +++ b/docs/devel/ci.rst @@ -8,160 +8,5 @@ found at:: https://wiki.qemu.org/Testing/CI -Custom CI/CD variables -====================== - -QEMU CI pipelines can be tuned by setting some CI environment variables. - -Set variable globally in the user's CI namespace ------------------------------------------------- - -Variables can be set globally in the user's CI namespace setting. - -For further information about how to set these variables, please refer to:: - - https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-a-project - -Set variable manually when pushing a branch or tag to the user's repository ---------------------------------------------------------------------------- - -Variables can be set manually when pushing a branch or tag, using -git-push command line arguments. - -Example setting the QEMU_CI_EXAMPLE_VAR variable: - -.. code:: - - git push -o ci.variable="QEMU_CI_EXAMPLE_VAR=value" myrepo mybranch - -For further information about how to set these variables, please refer to:: - - https://docs.gitlab.com/ee/user/project/push_options.html#push-options-for-gitlab-cicd - -Here is a list of the most used variables: - -QEMU_CI_AVOCADO_TESTING -~~~~~~~~~~~~~~~~~~~~~~~ -By default, tests using the Avocado framework are not run automatically in -the pipelines (because multiple artifacts have to be downloaded, and if -these artifacts are not already cached, downloading them make the jobs -reach the timeout limit). Set this variable to have the tests using the -Avocado framework run automatically. - -Jobs on Custom Runners -====================== - -Besides the jobs run under the various CI systems listed before, there -are a number additional jobs that will run before an actual merge. -These use the same GitLab CI's service/framework already used for all -other GitLab based CI jobs, but rely on additional systems, not the -ones provided by GitLab as "shared runners". - -The architecture of GitLab's CI service allows different machines to -be set up with GitLab's "agent", called gitlab-runner, which will take -care of running jobs created by events such as a push to a branch. -Here, the combination of a machine, properly configured with GitLab's -gitlab-runner, is called a "custom runner". - -The GitLab CI jobs definition for the custom runners are located under:: - - .gitlab-ci.d/custom-runners.yml - -Custom runners entail custom machines. To see a list of the machines -currently deployed in the QEMU GitLab CI and their maintainers, please -refer to the QEMU `wiki `__. - -Machine Setup Howto -------------------- - -For all Linux based systems, the setup can be mostly automated by the -execution of two Ansible playbooks. Create an ``inventory`` file -under ``scripts/ci/setup``, such as this:: - - fully.qualified.domain - other.machine.hostname - -You may need to set some variables in the inventory file itself. One -very common need is to tell Ansible to use a Python 3 interpreter on -those hosts. This would look like:: - - fully.qualified.domain ansible_python_interpreter=/usr/bin/python3 - other.machine.hostname ansible_python_interpreter=/usr/bin/python3 - -Build environment -~~~~~~~~~~~~~~~~~ - -The ``scripts/ci/setup/build-environment.yml`` Ansible playbook will -set up machines with the environment needed to perform builds and run -QEMU tests. This playbook consists on the installation of various -required packages (and a general package update while at it). It -currently covers a number of different Linux distributions, but it can -be expanded to cover other systems. - -The minimum required version of Ansible successfully tested in this -playbook is 2.8.0 (a version check is embedded within the playbook -itself). To run the playbook, execute:: - - cd scripts/ci/setup - ansible-playbook -i inventory build-environment.yml - -Please note that most of the tasks in the playbook require superuser -privileges, such as those from the ``root`` account or those obtained -by ``sudo``. If necessary, please refer to ``ansible-playbook`` -options such as ``--become``, ``--become-method``, ``--become-user`` -and ``--ask-become-pass``. - -gitlab-runner setup and registration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The gitlab-runner agent needs to be installed on each machine that -will run jobs. The association between a machine and a GitLab project -happens with a registration token. To find the registration token for -your repository/project, navigate on GitLab's web UI to: - - * Settings (the gears-like icon at the bottom of the left hand side - vertical toolbar), then - * CI/CD, then - * Runners, and click on the "Expand" button, then - * Under "Set up a specific Runner manually", look for the value under - "And this registration token:" - -Copy the ``scripts/ci/setup/vars.yml.template`` file to -``scripts/ci/setup/vars.yml``. Then, set the -``gitlab_runner_registration_token`` variable to the value obtained -earlier. - -To run the playbook, execute:: - - cd scripts/ci/setup - ansible-playbook -i inventory gitlab-runner.yml - -Following the registration, it's necessary to configure the runner tags, -and optionally other configurations on the GitLab UI. Navigate to: - - * Settings (the gears like icon), then - * CI/CD, then - * Runners, and click on the "Expand" button, then - * "Runners activated for this project", then - * Click on the "Edit" icon (next to the "Lock" Icon) - -Tags are very important as they are used to route specific jobs to -specific types of runners, so it's a good idea to double check that -the automatically created tags are consistent with the OS and -architecture. For instance, an Ubuntu 20.04 aarch64 system should -have tags set as:: - - ubuntu_20.04,aarch64 - -Because the job definition at ``.gitlab-ci.d/custom-runners.yml`` -would contain:: - - ubuntu-20.04-aarch64-all: - tags: - - ubuntu_20.04 - - aarch64 - -It's also recommended to: - - * increase the "Maximum job timeout" to something like ``2h`` - * give it a better Description +.. include:: ci-jobs.rst +.. include:: ci-runners.rst From 2e8801988282bf3a45b495efa8438509ff68056d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 12 Aug 2021 11:24:27 +0100 Subject: [PATCH 052/493] docs: make sphinx-build be quiet by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sphinx-build is fairly verbose spitting out pages of output to the console, which causes errors from other build commands to be scrolled off the top of the terminal. This can leave the mistaken impression that the build passed, when in fact there was a failure. Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20210812102427.4036399-1-berrange@redhat.com> Signed-off-by: Thomas Huth --- docs/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/meson.build b/docs/meson.build index 300b134329..cffe1ecf1d 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -9,7 +9,7 @@ endif # Check if tools are available to build documentation. build_docs = false if sphinx_build.found() - SPHINX_ARGS = ['env', 'CONFDIR=' + qemu_confdir, sphinx_build] + SPHINX_ARGS = ['env', 'CONFDIR=' + qemu_confdir, sphinx_build, '-q'] # If we're making warnings fatal, apply this to Sphinx runs as well if get_option('werror') SPHINX_ARGS += [ '-W' ] From c8b69a2a92bebed915af69bb685816e715956ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 31 Jul 2021 00:27:17 +0200 Subject: [PATCH 053/493] target/mips: Remove JR opcode unused arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JR opcode (Jump Register) only takes 1 argument, $rs. JALR (Jump And Link Register) takes 3: $rs, $rd and $hint. Commit 6af0bf9c7c3 added their processing into decode_opc() as: case 0x08 ... 0x09: /* Jumps */ gen_compute_branch(ctx, op1 | EXT_SPECIAL, rs, rd, sa); having both opcodes handled in the same function: gen_compute_branch. Per JR encoding, both $rd and $hint ('sa') are decoded as zero. Later this code got extracted to decode_opc_special(), commit 7a387fffce5 used definitions instead of magic values: case OPC_JR ... OPC_JALR: gen_compute_branch(ctx, op1, rs, rd, sa); Finally commit 0aefa33318b moved OPC_JR out of decode_opc_special, to a new 'decode_opc_special_legacy' function: @@ -15851,6 +15851,9 @@ static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx) + case OPC_JR: + gen_compute_branch(ctx, op1, 4, rs, rd, sa); + break; @@ -15933,7 +15936,7 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) - case OPC_JR ... OPC_JALR: + case OPC_JALR: gen_compute_branch(ctx, op1, 4, rs, rd, sa); break; Since JR is now handled individually, it is pointless to decode and pass it unused arguments. Replace them by simple zero value to avoid confusion with this opcode. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210730225507.2642827-1-f4bug@amsat.org> Reviewed-by: Richard Henderson --- target/mips/tcg/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 5b03545f09..bf71724f3f 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -14203,7 +14203,7 @@ static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx) break; #endif case OPC_JR: - gen_compute_branch(ctx, op1, 4, rs, rd, sa, 4); + gen_compute_branch(ctx, op1, 4, rs, 0, 0, 4); break; case OPC_SPIM: #ifdef MIPS_STRICT_STANDARD From 2e176eaf9c54a23ea2f37ccbeef2045f422b0e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 Jul 2021 16:02:57 +0200 Subject: [PATCH 054/493] target/mips: Simplify PREF opcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit check_insn() checks for any bit in the set, and INSN_R5900 is just another bit added to the set. No need to special-case it. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210801234202.3167676-2-f4bug@amsat.org> Reviewed-by: Richard Henderson --- target/mips/tcg/translate.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index bf71724f3f..6b95cca052 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -15739,12 +15739,8 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) /* Treat as NOP. */ break; case OPC_PREF: - if (ctx->insn_flags & INSN_R5900) { - /* Treat as NOP. */ - } else { - check_insn(ctx, ISA_MIPS4 | ISA_MIPS_R1); - /* Treat as NOP. */ - } + check_insn(ctx, ISA_MIPS4 | ISA_MIPS_R1 | INSN_R5900); + /* Treat as NOP. */ break; /* Floating point (COP1). */ From 4919f69c658be36c97d63660bd41377b06ea6822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 31 Jul 2021 15:23:47 +0200 Subject: [PATCH 055/493] target/mips: Decode vendor extensions before MIPS ISAs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit ffc672aa977 ("target/mips/tx79: Move MFHI1 / MFLO1 opcodes to decodetree") we misplaced the decoder call. Move it to the correct place. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210801234202.3167676-3-f4bug@amsat.org> Reviewed-by: Richard Henderson --- target/mips/tcg/translate.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 6b95cca052..490add3fc1 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -16094,6 +16094,11 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) /* Transition to the auto-generated decoder. */ + /* Vendor specific extensions */ + if (cpu_supports_isa(env, INSN_R5900) && decode_ext_txx9(ctx, ctx->opcode)) { + return; + } + /* ISA extensions */ if (ase_msa_available(env) && decode_ase_msa(ctx, ctx->opcode)) { return; @@ -16103,9 +16108,6 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) if (cpu_supports_isa(env, ISA_MIPS_R6) && decode_isa_rel6(ctx, ctx->opcode)) { return; } - if (cpu_supports_isa(env, INSN_R5900) && decode_ext_txx9(ctx, ctx->opcode)) { - return; - } if (decode_opc_legacy(env, ctx)) { return; From 12f79f11731194d7cb61c4a0d9b2de5416a773de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 1 Aug 2021 20:29:29 +0200 Subject: [PATCH 056/493] target/mips: Merge 32-bit/64-bit Release6 decodetree definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need to maintain 2 sets of decodetree definitions. Merge them into a single file. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210801234202.3167676-4-f4bug@amsat.org> Reviewed-by: Richard Henderson --- target/mips/tcg/meson.build | 3 +-- target/mips/tcg/mips64r6.decode | 27 ------------------- .../mips/tcg/{mips32r6.decode => rel6.decode} | 13 +++++++++ target/mips/tcg/rel6_translate.c | 16 ++++------- 4 files changed, 19 insertions(+), 40 deletions(-) delete mode 100644 target/mips/tcg/mips64r6.decode rename target/mips/tcg/{mips32r6.decode => rel6.decode} (69%) diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build index bf4001e574..70fa3dd57d 100644 --- a/target/mips/tcg/meson.build +++ b/target/mips/tcg/meson.build @@ -1,6 +1,5 @@ gen = [ - decodetree.process('mips32r6.decode', extra_args: '--static-decode=decode_mips32r6'), - decodetree.process('mips64r6.decode', extra_args: '--static-decode=decode_mips64r6'), + decodetree.process('rel6.decode', extra_args: ['--decode=decode_isa_rel6']), decodetree.process('msa.decode', extra_args: '--decode=decode_ase_msa'), decodetree.process('tx79.decode', extra_args: '--static-decode=decode_tx79'), ] diff --git a/target/mips/tcg/mips64r6.decode b/target/mips/tcg/mips64r6.decode deleted file mode 100644 index b58d8009cc..0000000000 --- a/target/mips/tcg/mips64r6.decode +++ /dev/null @@ -1,27 +0,0 @@ -# MIPS64 Release 6 instruction set -# -# Copyright (C) 2020 Philippe Mathieu-Daudé -# -# SPDX-License-Identifier: LGPL-2.1-or-later -# -# Reference: -# MIPS Architecture for Programmers Volume II-A -# The MIPS64 Instruction Set Reference Manual, Revision 6.06 -# (Document Number: MD00087-2B-MIPS64BIS-AFP-6.06) -# - -&rtype rs rt rd sa !extern - -&REMOVED !extern - -@lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &rtype - -DLSA 000000 ..... ..... ..... 000 .. 010101 @lsa - -REMOVED 011010 ----- ----- ---------------- # LDL -REMOVED 011011 ----- ----- ---------------- # LDR -REMOVED 101100 ----- ----- ---------------- # SDL -REMOVED 101101 ----- ----- ---------------- # SDR - -REMOVED 110100 ----- ----- ---------------- # LLD -REMOVED 111100 ----- ----- ---------------- # SCD diff --git a/target/mips/tcg/mips32r6.decode b/target/mips/tcg/rel6.decode similarity index 69% rename from target/mips/tcg/mips32r6.decode rename to target/mips/tcg/rel6.decode index 837c991edc..ed069c5166 100644 --- a/target/mips/tcg/mips32r6.decode +++ b/target/mips/tcg/rel6.decode @@ -5,21 +5,29 @@ # SPDX-License-Identifier: LGPL-2.1-or-later # # Reference: +# # MIPS Architecture for Programmers Volume II-A # The MIPS32 Instruction Set Reference Manual, Revision 6.06 # (Document Number: MD00086-2B-MIPS32BIS-AFP-06.06) # +# MIPS Architecture for Programmers Volume II-A +# The MIPS64 Instruction Set Reference Manual, Revision 6.06 +# (Document Number: MD00087-2B-MIPS64BIS-AFP-6.06) &rtype rs rt rd sa @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &rtype LSA 000000 ..... ..... ..... 000 .. 000101 @lsa +DLSA 000000 ..... ..... ..... 000 .. 010101 @lsa REMOVED 010011 ----- ----- ----- ----- ------ # COP1X (COP3) REMOVED 011100 ----- ----- ----- ----- ------ # SPECIAL2 +REMOVED 011010 ----- ----- ---------------- # LDL +REMOVED 011011 ----- ----- ---------------- # LDR + REMOVED 011111 ----- ----- ---------- 011001 # LWLE REMOVED 011111 ----- ----- ---------- 011010 # LWRE REMOVED 011111 ----- ----- ---------- 100001 # SWLE @@ -28,9 +36,14 @@ REMOVED 011111 ----- ----- ---------- 100010 # SWRE REMOVED 100010 ----- ----- ---------------- # LWL REMOVED 100110 ----- ----- ---------------- # LWR REMOVED 101010 ----- ----- ---------------- # SWL +REMOVED 101100 ----- ----- ---------------- # SDL +REMOVED 101101 ----- ----- ---------------- # SDR REMOVED 101110 ----- ----- ---------------- # SWR REMOVED 101111 ----- ----- ---------------- # CACHE + REMOVED 110000 ----- ----- ---------------- # LL REMOVED 110011 ----- ----- ---------------- # PREF +REMOVED 110100 ----- ----- ---------------- # LLD REMOVED 111000 ----- ----- ---------------- # SC +REMOVED 111100 ----- ----- ---------------- # SCD diff --git a/target/mips/tcg/rel6_translate.c b/target/mips/tcg/rel6_translate.c index 0354370927..ae2e023a81 100644 --- a/target/mips/tcg/rel6_translate.c +++ b/target/mips/tcg/rel6_translate.c @@ -13,9 +13,8 @@ #include "exec/helper-gen.h" #include "translate.h" -/* Include the auto-generated decoder. */ -#include "decode-mips32r6.c.inc" -#include "decode-mips64r6.c.inc" +/* Include the auto-generated decoders. */ +#include "decode-rel6.c.inc" bool trans_REMOVED(DisasContext *ctx, arg_REMOVED *a) { @@ -31,13 +30,8 @@ static bool trans_LSA(DisasContext *ctx, arg_rtype *a) static bool trans_DLSA(DisasContext *ctx, arg_rtype *a) { + if (TARGET_LONG_BITS != 64) { + return false; + } return gen_dlsa(ctx, a->rd, a->rt, a->rs, a->sa); } - -bool decode_isa_rel6(DisasContext *ctx, uint32_t insn) -{ - if (TARGET_LONG_BITS == 64 && decode_mips64r6(ctx, insn)) { - return true; - } - return decode_mips32r6(ctx, insn); -} From 34fe9fa3683a7ff54315c62960eba6eeac2a36ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 27 Jul 2021 21:13:49 +0200 Subject: [PATCH 057/493] target/mips: Rename 'rtype' as 'r' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We'll soon have more opcode and decoded arguments, and 'rtype' is not very helpful. Naming it simply 'r' ease reviewing the .decode files when we have many opcodes. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210801234202.3167676-5-f4bug@amsat.org> Reviewed-by: Richard Henderson --- target/mips/tcg/msa.decode | 4 +-- target/mips/tcg/msa_translate.c | 4 +-- target/mips/tcg/rel6.decode | 4 +-- target/mips/tcg/rel6_translate.c | 4 +-- target/mips/tcg/tx79.decode | 14 ++++---- target/mips/tcg/tx79_translate.c | 62 ++++++++++++++++---------------- 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index bf132e36b9..74d99f6862 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -11,11 +11,11 @@ # - The MIPS64 SIMD Architecture Module, Revision 1.12 # (Document Number: MD00868-1D-MSA64-AFP-01.12) -&rtype rs rt rd sa +&r rs rt rd sa &msa_bz df wt s16 -@lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &rtype +@lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r @bz ...... ... .. wt:5 s16:16 &msa_bz df=3 @bz_df ...... ... df:2 wt:5 s16:16 &msa_bz diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index eed2eca6c9..8170a8df26 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -2261,12 +2261,12 @@ static bool trans_MSA(DisasContext *ctx, arg_MSA *a) return true; } -static bool trans_LSA(DisasContext *ctx, arg_rtype *a) +static bool trans_LSA(DisasContext *ctx, arg_r *a) { return gen_lsa(ctx, a->rd, a->rt, a->rs, a->sa); } -static bool trans_DLSA(DisasContext *ctx, arg_rtype *a) +static bool trans_DLSA(DisasContext *ctx, arg_r *a) { if (TARGET_LONG_BITS != 64) { return false; diff --git a/target/mips/tcg/rel6.decode b/target/mips/tcg/rel6.decode index ed069c5166..d6989cf56e 100644 --- a/target/mips/tcg/rel6.decode +++ b/target/mips/tcg/rel6.decode @@ -14,9 +14,9 @@ # The MIPS64 Instruction Set Reference Manual, Revision 6.06 # (Document Number: MD00087-2B-MIPS64BIS-AFP-6.06) -&rtype rs rt rd sa +&r rs rt rd sa -@lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &rtype +@lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r LSA 000000 ..... ..... ..... 000 .. 000101 @lsa DLSA 000000 ..... ..... ..... 000 .. 010101 @lsa diff --git a/target/mips/tcg/rel6_translate.c b/target/mips/tcg/rel6_translate.c index ae2e023a81..d631851258 100644 --- a/target/mips/tcg/rel6_translate.c +++ b/target/mips/tcg/rel6_translate.c @@ -23,12 +23,12 @@ bool trans_REMOVED(DisasContext *ctx, arg_REMOVED *a) return true; } -static bool trans_LSA(DisasContext *ctx, arg_rtype *a) +static bool trans_LSA(DisasContext *ctx, arg_r *a) { return gen_lsa(ctx, a->rd, a->rt, a->rs, a->sa); } -static bool trans_DLSA(DisasContext *ctx, arg_rtype *a) +static bool trans_DLSA(DisasContext *ctx, arg_r *a) { if (TARGET_LONG_BITS != 64) { return false; diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index 03a25a5096..57d87a2076 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -11,20 +11,20 @@ # when creating helpers common to those for the individual # instruction patterns. -&rtype rs rt rd sa +&r rs rt rd sa -&itype base rt offset +&i base rt offset ########################################################################### # Named instruction formats. These are generally used to # reduce the amount of duplication between instruction patterns. -@rs_rt_rd ...... rs:5 rt:5 rd:5 ..... ...... &rtype sa=0 -@rt_rd ...... ..... rt:5 rd:5 ..... ...... &rtype rs=0 sa=0 -@rs ...... rs:5 ..... .......... ...... &rtype rt=0 rd=0 sa=0 -@rd ...... .......... rd:5 ..... ...... &rtype rs=0 rt=0 sa=0 +@rs_rt_rd ...... rs:5 rt:5 rd:5 ..... ...... &r sa=0 +@rt_rd ...... ..... rt:5 rd:5 ..... ...... &r sa=0 rs=0 +@rs ...... rs:5 ..... .......... ...... &r sa=0 rt=0 rd=0 +@rd ...... .......... rd:5 ..... ...... &r sa=0 rs=0 rt=0 -@ldst ...... base:5 rt:5 offset:16 &itype +@ldst ...... base:5 rt:5 offset:16 &i ########################################################################### diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index 395d6afa1f..6d51fe17c1 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -64,28 +64,28 @@ bool decode_ext_tx79(DisasContext *ctx, uint32_t insn) * MTLO1 rs Move To LO1 Register */ -static bool trans_MFHI1(DisasContext *ctx, arg_rtype *a) +static bool trans_MFHI1(DisasContext *ctx, arg_r *a) { gen_store_gpr(cpu_HI[1], a->rd); return true; } -static bool trans_MFLO1(DisasContext *ctx, arg_rtype *a) +static bool trans_MFLO1(DisasContext *ctx, arg_r *a) { gen_store_gpr(cpu_LO[1], a->rd); return true; } -static bool trans_MTHI1(DisasContext *ctx, arg_rtype *a) +static bool trans_MTHI1(DisasContext *ctx, arg_r *a) { gen_load_gpr(cpu_HI[1], a->rs); return true; } -static bool trans_MTLO1(DisasContext *ctx, arg_rtype *a) +static bool trans_MTLO1(DisasContext *ctx, arg_r *a) { gen_load_gpr(cpu_LO[1], a->rs); @@ -116,7 +116,7 @@ static bool trans_MTLO1(DisasContext *ctx, arg_rtype *a) * PSUBUW rd, rs, rt Parallel Subtract with Unsigned saturation Word */ -static bool trans_parallel_arith(DisasContext *ctx, arg_rtype *a, +static bool trans_parallel_arith(DisasContext *ctx, arg_r *a, void (*gen_logic_i64)(TCGv_i64, TCGv_i64, TCGv_i64)) { TCGv_i64 ax, bx; @@ -146,19 +146,19 @@ static bool trans_parallel_arith(DisasContext *ctx, arg_rtype *a, } /* Parallel Subtract Byte */ -static bool trans_PSUBB(DisasContext *ctx, arg_rtype *a) +static bool trans_PSUBB(DisasContext *ctx, arg_r *a) { return trans_parallel_arith(ctx, a, tcg_gen_vec_sub8_i64); } /* Parallel Subtract Halfword */ -static bool trans_PSUBH(DisasContext *ctx, arg_rtype *a) +static bool trans_PSUBH(DisasContext *ctx, arg_r *a) { return trans_parallel_arith(ctx, a, tcg_gen_vec_sub16_i64); } /* Parallel Subtract Word */ -static bool trans_PSUBW(DisasContext *ctx, arg_rtype *a) +static bool trans_PSUBW(DisasContext *ctx, arg_r *a) { return trans_parallel_arith(ctx, a, tcg_gen_vec_sub32_i64); } @@ -189,25 +189,25 @@ static bool trans_PSUBW(DisasContext *ctx, arg_rtype *a) */ /* Parallel And */ -static bool trans_PAND(DisasContext *ctx, arg_rtype *a) +static bool trans_PAND(DisasContext *ctx, arg_r *a) { return trans_parallel_arith(ctx, a, tcg_gen_and_i64); } /* Parallel Or */ -static bool trans_POR(DisasContext *ctx, arg_rtype *a) +static bool trans_POR(DisasContext *ctx, arg_r *a) { return trans_parallel_arith(ctx, a, tcg_gen_or_i64); } /* Parallel Exclusive Or */ -static bool trans_PXOR(DisasContext *ctx, arg_rtype *a) +static bool trans_PXOR(DisasContext *ctx, arg_r *a) { return trans_parallel_arith(ctx, a, tcg_gen_xor_i64); } /* Parallel Not Or */ -static bool trans_PNOR(DisasContext *ctx, arg_rtype *a) +static bool trans_PNOR(DisasContext *ctx, arg_r *a) { return trans_parallel_arith(ctx, a, tcg_gen_nor_i64); } @@ -237,7 +237,7 @@ static bool trans_PNOR(DisasContext *ctx, arg_rtype *a) * PCEQW rd, rs, rt Parallel Compare for Equal Word */ -static bool trans_parallel_compare(DisasContext *ctx, arg_rtype *a, +static bool trans_parallel_compare(DisasContext *ctx, arg_r *a, TCGCond cond, unsigned wlen) { TCGv_i64 c0, c1, ax, bx, t0, t1, t2; @@ -286,37 +286,37 @@ static bool trans_parallel_compare(DisasContext *ctx, arg_rtype *a, } /* Parallel Compare for Greater Than Byte */ -static bool trans_PCGTB(DisasContext *ctx, arg_rtype *a) +static bool trans_PCGTB(DisasContext *ctx, arg_r *a) { return trans_parallel_compare(ctx, a, TCG_COND_GE, 8); } /* Parallel Compare for Equal Byte */ -static bool trans_PCEQB(DisasContext *ctx, arg_rtype *a) +static bool trans_PCEQB(DisasContext *ctx, arg_r *a) { return trans_parallel_compare(ctx, a, TCG_COND_EQ, 8); } /* Parallel Compare for Greater Than Halfword */ -static bool trans_PCGTH(DisasContext *ctx, arg_rtype *a) +static bool trans_PCGTH(DisasContext *ctx, arg_r *a) { return trans_parallel_compare(ctx, a, TCG_COND_GE, 16); } /* Parallel Compare for Equal Halfword */ -static bool trans_PCEQH(DisasContext *ctx, arg_rtype *a) +static bool trans_PCEQH(DisasContext *ctx, arg_r *a) { return trans_parallel_compare(ctx, a, TCG_COND_EQ, 16); } /* Parallel Compare for Greater Than Word */ -static bool trans_PCGTW(DisasContext *ctx, arg_rtype *a) +static bool trans_PCGTW(DisasContext *ctx, arg_r *a) { return trans_parallel_compare(ctx, a, TCG_COND_GE, 32); } /* Parallel Compare for Equal Word */ -static bool trans_PCEQW(DisasContext *ctx, arg_rtype *a) +static bool trans_PCEQW(DisasContext *ctx, arg_r *a) { return trans_parallel_compare(ctx, a, TCG_COND_EQ, 32); } @@ -334,7 +334,7 @@ static bool trans_PCEQW(DisasContext *ctx, arg_rtype *a) * SQ rt, offset(base) Store Quadword */ -static bool trans_LQ(DisasContext *ctx, arg_itype *a) +static bool trans_LQ(DisasContext *ctx, arg_i *a) { TCGv_i64 t0; TCGv addr; @@ -369,7 +369,7 @@ static bool trans_LQ(DisasContext *ctx, arg_itype *a) return true; } -static bool trans_SQ(DisasContext *ctx, arg_itype *a) +static bool trans_SQ(DisasContext *ctx, arg_i *a) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv addr = tcg_temp_new(); @@ -437,7 +437,7 @@ static bool trans_SQ(DisasContext *ctx, arg_itype *a) */ /* Parallel Pack to Word */ -static bool trans_PPACW(DisasContext *ctx, arg_rtype *a) +static bool trans_PPACW(DisasContext *ctx, arg_r *a) { TCGv_i64 a0, b0, t0; @@ -473,7 +473,7 @@ static void gen_pextw(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 a, TCGv_i64 b) tcg_gen_deposit_i64(dh, a, b, 0, 32); } -static bool trans_PEXTLx(DisasContext *ctx, arg_rtype *a, unsigned wlen) +static bool trans_PEXTLx(DisasContext *ctx, arg_r *a, unsigned wlen) { TCGv_i64 ax, bx; @@ -514,19 +514,19 @@ static bool trans_PEXTLx(DisasContext *ctx, arg_rtype *a, unsigned wlen) } /* Parallel Extend Lower from Byte */ -static bool trans_PEXTLB(DisasContext *ctx, arg_rtype *a) +static bool trans_PEXTLB(DisasContext *ctx, arg_r *a) { return trans_PEXTLx(ctx, a, 8); } /* Parallel Extend Lower from Halfword */ -static bool trans_PEXTLH(DisasContext *ctx, arg_rtype *a) +static bool trans_PEXTLH(DisasContext *ctx, arg_r *a) { return trans_PEXTLx(ctx, a, 16); } /* Parallel Extend Lower from Word */ -static bool trans_PEXTLW(DisasContext *ctx, arg_rtype *a) +static bool trans_PEXTLW(DisasContext *ctx, arg_r *a) { TCGv_i64 ax, bx; @@ -549,7 +549,7 @@ static bool trans_PEXTLW(DisasContext *ctx, arg_rtype *a) } /* Parallel Extend Upper from Word */ -static bool trans_PEXTUW(DisasContext *ctx, arg_rtype *a) +static bool trans_PEXTUW(DisasContext *ctx, arg_r *a) { TCGv_i64 ax, bx; @@ -593,7 +593,7 @@ static bool trans_PEXTUW(DisasContext *ctx, arg_rtype *a) */ /* Parallel Copy Halfword */ -static bool trans_PCPYH(DisasContext *s, arg_rtype *a) +static bool trans_PCPYH(DisasContext *s, arg_r *a) { if (a->rd == 0) { /* nop */ @@ -615,7 +615,7 @@ static bool trans_PCPYH(DisasContext *s, arg_rtype *a) } /* Parallel Copy Lower Doubleword */ -static bool trans_PCPYLD(DisasContext *s, arg_rtype *a) +static bool trans_PCPYLD(DisasContext *s, arg_r *a) { if (a->rd == 0) { /* nop */ @@ -638,7 +638,7 @@ static bool trans_PCPYLD(DisasContext *s, arg_rtype *a) } /* Parallel Copy Upper Doubleword */ -static bool trans_PCPYUD(DisasContext *s, arg_rtype *a) +static bool trans_PCPYUD(DisasContext *s, arg_r *a) { if (a->rd == 0) { /* nop */ @@ -657,7 +657,7 @@ static bool trans_PCPYUD(DisasContext *s, arg_rtype *a) } /* Parallel Rotate 3 Words Left */ -static bool trans_PROT3W(DisasContext *ctx, arg_rtype *a) +static bool trans_PROT3W(DisasContext *ctx, arg_r *a) { TCGv_i64 ax; From fb3164e412dac312cc472968d049ee1a074dd7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 7 Aug 2021 09:36:49 +0200 Subject: [PATCH 058/493] target/mips: Introduce generic TRANS() macro for decodetree helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plain copy/paste of the TRANS() macro introduced in the PPC commit f2aabda8ac9 ("target/ppc: Move D/DS/X-form integer loads to decodetree") to the MIPS target. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210808173018.90960-2-f4bug@amsat.org> --- target/mips/tcg/translate.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index c25fad597d..791e3e2c7e 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -202,4 +202,12 @@ bool decode_ext_txx9(DisasContext *ctx, uint32_t insn); bool decode_ext_tx79(DisasContext *ctx, uint32_t insn); #endif +/* + * Helpers for implementing sets of trans_* functions. + * Defer the implementation of NAME to FUNC, with optional extra arguments. + */ +#define TRANS(NAME, FUNC, ...) \ + static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ + { return FUNC(ctx, a, __VA_ARGS__); } + #endif From 07565cbf4abb81efa6dc4742c243bdc4b3444398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Nov 2020 16:39:20 +0100 Subject: [PATCH 059/493] target/mips: Extract NEC Vr54xx helper definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the NEC Vr54xx helper definitions to 'vendor-vr54xx_helper.h'. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20201120210844.2625602-15-f4bug@amsat.org> --- target/mips/helper.h | 18 +++--------------- target/mips/tcg/vr54xx_helper.h.inc | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 target/mips/tcg/vr54xx_helper.h.inc diff --git a/target/mips/helper.h b/target/mips/helper.h index a9c6c7d1a3..de32d82e98 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -16,21 +16,6 @@ DEF_HELPER_3(lld, tl, env, tl, int) #endif #endif -DEF_HELPER_3(muls, tl, env, tl, tl) -DEF_HELPER_3(mulsu, tl, env, tl, tl) -DEF_HELPER_3(macc, tl, env, tl, tl) -DEF_HELPER_3(maccu, tl, env, tl, tl) -DEF_HELPER_3(msac, tl, env, tl, tl) -DEF_HELPER_3(msacu, tl, env, tl, tl) -DEF_HELPER_3(mulhi, tl, env, tl, tl) -DEF_HELPER_3(mulhiu, tl, env, tl, tl) -DEF_HELPER_3(mulshi, tl, env, tl, tl) -DEF_HELPER_3(mulshiu, tl, env, tl, tl) -DEF_HELPER_3(macchi, tl, env, tl, tl) -DEF_HELPER_3(macchiu, tl, env, tl, tl) -DEF_HELPER_3(msachi, tl, env, tl, tl) -DEF_HELPER_3(msachiu, tl, env, tl, tl) - DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) #ifdef TARGET_MIPS64 DEF_HELPER_FLAGS_1(dbitswap, TCG_CALL_NO_RWG_SE, tl, tl) @@ -609,3 +594,6 @@ DEF_HELPER_FLAGS_2(rddsp, 0, tl, tl, env) #endif /* !CONFIG_USER_ONLY */ #include "tcg/msa_helper.h.inc" + +/* Vendor extensions */ +#include "tcg/vr54xx_helper.h.inc" diff --git a/target/mips/tcg/vr54xx_helper.h.inc b/target/mips/tcg/vr54xx_helper.h.inc new file mode 100644 index 0000000000..50b1f5b818 --- /dev/null +++ b/target/mips/tcg/vr54xx_helper.h.inc @@ -0,0 +1,24 @@ +/* + * MIPS NEC Vr54xx instruction emulation helpers for QEMU. + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * Copyright (c) 2006 Marius Groeger (FPU operations) + * Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support) + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +DEF_HELPER_3(muls, tl, env, tl, tl) +DEF_HELPER_3(mulsu, tl, env, tl, tl) +DEF_HELPER_3(macc, tl, env, tl, tl) +DEF_HELPER_3(maccu, tl, env, tl, tl) +DEF_HELPER_3(msac, tl, env, tl, tl) +DEF_HELPER_3(msacu, tl, env, tl, tl) +DEF_HELPER_3(mulhi, tl, env, tl, tl) +DEF_HELPER_3(mulhiu, tl, env, tl, tl) +DEF_HELPER_3(mulshi, tl, env, tl, tl) +DEF_HELPER_3(mulshiu, tl, env, tl, tl) +DEF_HELPER_3(macchi, tl, env, tl, tl) +DEF_HELPER_3(macchiu, tl, env, tl, tl) +DEF_HELPER_3(msachi, tl, env, tl, tl) +DEF_HELPER_3(msachiu, tl, env, tl, tl) From 6629f79f53ea9a1caa3f3c842fa3d02488116486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Nov 2020 16:41:05 +0100 Subject: [PATCH 060/493] target/mips: Extract NEC Vr54xx helpers to vr54xx_helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract NEC Vr54xx helpers from op_helper.c to a new file: 'vr54xx_helper.c'. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20201120210844.2625602-14-f4bug@amsat.org> --- target/mips/tcg/meson.build | 1 + target/mips/tcg/op_helper.c | 118 -------------------------- target/mips/tcg/vr54xx_helper.c | 142 ++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 118 deletions(-) create mode 100644 target/mips/tcg/vr54xx_helper.c diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build index 70fa3dd57d..ff618a159b 100644 --- a/target/mips/tcg/meson.build +++ b/target/mips/tcg/meson.build @@ -18,6 +18,7 @@ mips_ss.add(files( 'translate.c', 'translate_addr_const.c', 'txx9_translate.c', + 'vr54xx_helper.c', )) mips_ss.add(when: 'TARGET_MIPS64', if_true: files( 'tx79_translate.c', diff --git a/target/mips/tcg/op_helper.c b/target/mips/tcg/op_helper.c index fafbf1faca..ef3dafcbb3 100644 --- a/target/mips/tcg/op_helper.c +++ b/target/mips/tcg/op_helper.c @@ -26,124 +26,6 @@ #include "exec/memop.h" #include "fpu_helper.h" -/* 64 bits arithmetic for 32 bits hosts */ -static inline uint64_t get_HILO(CPUMIPSState *env) -{ - return ((uint64_t)(env->active_tc.HI[0]) << 32) | - (uint32_t)env->active_tc.LO[0]; -} - -static inline target_ulong set_HIT0_LO(CPUMIPSState *env, uint64_t HILO) -{ - env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); - return env->active_tc.HI[0] = (int32_t)(HILO >> 32); -} - -static inline target_ulong set_HI_LOT0(CPUMIPSState *env, uint64_t HILO) -{ - target_ulong tmp = env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); - env->active_tc.HI[0] = (int32_t)(HILO >> 32); - return tmp; -} - -/* Multiplication variants of the vr54xx. */ -target_ulong helper_muls(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HI_LOT0(env, 0 - ((int64_t)(int32_t)arg1 * - (int64_t)(int32_t)arg2)); -} - -target_ulong helper_mulsu(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HI_LOT0(env, 0 - (uint64_t)(uint32_t)arg1 * - (uint64_t)(uint32_t)arg2); -} - -target_ulong helper_macc(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HI_LOT0(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * - (int64_t)(int32_t)arg2); -} - -target_ulong helper_macchi(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HIT0_LO(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * - (int64_t)(int32_t)arg2); -} - -target_ulong helper_maccu(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HI_LOT0(env, (uint64_t)get_HILO(env) + - (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); -} - -target_ulong helper_macchiu(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HIT0_LO(env, (uint64_t)get_HILO(env) + - (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); -} - -target_ulong helper_msac(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HI_LOT0(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * - (int64_t)(int32_t)arg2); -} - -target_ulong helper_msachi(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HIT0_LO(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * - (int64_t)(int32_t)arg2); -} - -target_ulong helper_msacu(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HI_LOT0(env, (uint64_t)get_HILO(env) - - (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); -} - -target_ulong helper_msachiu(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HIT0_LO(env, (uint64_t)get_HILO(env) - - (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); -} - -target_ulong helper_mulhi(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HIT0_LO(env, (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2); -} - -target_ulong helper_mulhiu(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HIT0_LO(env, (uint64_t)(uint32_t)arg1 * - (uint64_t)(uint32_t)arg2); -} - -target_ulong helper_mulshi(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HIT0_LO(env, 0 - (int64_t)(int32_t)arg1 * - (int64_t)(int32_t)arg2); -} - -target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1, - target_ulong arg2) -{ - return set_HIT0_LO(env, 0 - (uint64_t)(uint32_t)arg1 * - (uint64_t)(uint32_t)arg2); -} - static inline target_ulong bitswap(target_ulong v) { v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) | diff --git a/target/mips/tcg/vr54xx_helper.c b/target/mips/tcg/vr54xx_helper.c new file mode 100644 index 0000000000..2255bd1116 --- /dev/null +++ b/target/mips/tcg/vr54xx_helper.c @@ -0,0 +1,142 @@ +/* + * MIPS VR5432 emulation helpers + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * 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 . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" + +/* 64 bits arithmetic for 32 bits hosts */ +static inline uint64_t get_HILO(CPUMIPSState *env) +{ + return ((uint64_t)(env->active_tc.HI[0]) << 32) | + (uint32_t)env->active_tc.LO[0]; +} + +static inline target_ulong set_HIT0_LO(CPUMIPSState *env, uint64_t HILO) +{ + env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); + return env->active_tc.HI[0] = (int32_t)(HILO >> 32); +} + +static inline target_ulong set_HI_LOT0(CPUMIPSState *env, uint64_t HILO) +{ + target_ulong tmp = env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); + env->active_tc.HI[0] = (int32_t)(HILO >> 32); + return tmp; +} + +/* Multiplication variants of the vr54xx. */ +target_ulong helper_muls(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, 0 - ((int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2)); +} + +target_ulong helper_mulsu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, 0 - (uint64_t)(uint32_t)arg1 * + (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_macc(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2); +} + +target_ulong helper_macchi(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2); +} + +target_ulong helper_maccu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, (uint64_t)get_HILO(env) + (uint64_t)(uint32_t)arg1 * + (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_macchiu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (uint64_t)get_HILO(env) + (uint64_t)(uint32_t)arg1 * + (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_msac(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2); +} + +target_ulong helper_msachi(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2); +} + +target_ulong helper_msacu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, (uint64_t)get_HILO(env) - (uint64_t)(uint32_t)arg1 * + (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_msachiu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (uint64_t)get_HILO(env) - (uint64_t)(uint32_t)arg1 * + (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_mulhi(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2); +} + +target_ulong helper_mulhiu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (uint64_t)(uint32_t)arg1 * + (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_mulshi(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, 0 - (int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2); +} + +target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, 0 - (uint64_t)(uint32_t)arg1 * + (uint64_t)(uint32_t)arg2); +} From 9d0053923904215d95e8e5ab14b38bb792a0a68f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Jul 2021 13:18:48 +0200 Subject: [PATCH 061/493] target/mips: Introduce decodetree structure for NEC Vr54xx extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The decoder is called but doesn't decode anything. This will ease reviewing the next commit. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210801235926.3178085-3-f4bug@amsat.org> Reviewed-by: Richard Henderson --- target/mips/tcg/meson.build | 2 ++ target/mips/tcg/translate.c | 3 +++ target/mips/tcg/translate.h | 1 + target/mips/tcg/vr54xx.decode | 8 ++++++++ target/mips/tcg/vr54xx_translate.c | 19 +++++++++++++++++++ 5 files changed, 33 insertions(+) create mode 100644 target/mips/tcg/vr54xx.decode create mode 100644 target/mips/tcg/vr54xx_translate.c diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build index ff618a159b..8f6f7508b6 100644 --- a/target/mips/tcg/meson.build +++ b/target/mips/tcg/meson.build @@ -2,6 +2,7 @@ gen = [ decodetree.process('rel6.decode', extra_args: ['--decode=decode_isa_rel6']), decodetree.process('msa.decode', extra_args: '--decode=decode_ase_msa'), decodetree.process('tx79.decode', extra_args: '--static-decode=decode_tx79'), + decodetree.process('vr54xx.decode', extra_args: '--decode=decode_ext_vr54xx'), ] mips_ss.add(gen) @@ -19,6 +20,7 @@ mips_ss.add(files( 'translate_addr_const.c', 'txx9_translate.c', 'vr54xx_helper.c', + 'vr54xx_translate.c', )) mips_ss.add(when: 'TARGET_MIPS64', if_true: files( 'tx79_translate.c', diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 490add3fc1..3436363993 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -16098,6 +16098,9 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) if (cpu_supports_isa(env, INSN_R5900) && decode_ext_txx9(ctx, ctx->opcode)) { return; } + if (cpu_supports_isa(env, INSN_VR54XX) && decode_ext_vr54xx(ctx, ctx->opcode)) { + return; + } /* ISA extensions */ if (ase_msa_available(env) && decode_ase_msa(ctx, ctx->opcode)) { diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index 791e3e2c7e..bb0a6b8d74 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -201,6 +201,7 @@ bool decode_ext_txx9(DisasContext *ctx, uint32_t insn); #if defined(TARGET_MIPS64) bool decode_ext_tx79(DisasContext *ctx, uint32_t insn); #endif +bool decode_ext_vr54xx(DisasContext *ctx, uint32_t insn); /* * Helpers for implementing sets of trans_* functions. diff --git a/target/mips/tcg/vr54xx.decode b/target/mips/tcg/vr54xx.decode new file mode 100644 index 0000000000..f6b3e42c99 --- /dev/null +++ b/target/mips/tcg/vr54xx.decode @@ -0,0 +1,8 @@ +# MIPS VR5432 instruction set extensions +# +# Copyright (C) 2021 Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Reference: VR5432 Microprocessor User’s Manual +# (Document Number U13751EU5V0UM00) diff --git a/target/mips/tcg/vr54xx_translate.c b/target/mips/tcg/vr54xx_translate.c new file mode 100644 index 0000000000..13e58fdd8d --- /dev/null +++ b/target/mips/tcg/vr54xx_translate.c @@ -0,0 +1,19 @@ +/* + * VR5432 extensions translation routines + * + * Reference: VR5432 Microprocessor User’s Manual + * (Document Number U13751EU5V0UM00) + * + * Copyright (c) 2021 Philippe Mathieu-Daudé + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "tcg/tcg-op.h" +#include "exec/helper-gen.h" +#include "translate.h" +#include "internal.h" + +/* Include the auto-generated decoder. */ +#include "decode-vr54xx.c.inc" From 5fa38eedbd0b3fad052a11c5efc8031a20ed9b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Jul 2021 13:20:42 +0200 Subject: [PATCH 062/493] target/mips: Convert Vr54xx MACC* opcodes to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the following Integer Multiply-Accumulate opcodes: * MACC Multiply, accumulate, and move LO * MACCHI Multiply, accumulate, and move HI * MACCHIU Unsigned multiply, accumulate, and move HI * MACCU Unsigned multiply, accumulate, and move LO Since all opcodes are generated using the same pattern, we add the gen_helper_mult_acc_t typedef and MULT_ACC() macro to remove boilerplate code. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210808173018.90960-6-f4bug@amsat.org> --- target/mips/tcg/translate.c | 16 --------------- target/mips/tcg/vr54xx.decode | 9 ++++++++ target/mips/tcg/vr54xx_translate.c | 33 ++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 3436363993..fd8ffadf06 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -300,16 +300,12 @@ enum { enum { OPC_VR54XX_MULS = (0x03 << 6) | OPC_MULT, OPC_VR54XX_MULSU = (0x03 << 6) | OPC_MULTU, - OPC_VR54XX_MACC = (0x05 << 6) | OPC_MULT, - OPC_VR54XX_MACCU = (0x05 << 6) | OPC_MULTU, OPC_VR54XX_MSAC = (0x07 << 6) | OPC_MULT, OPC_VR54XX_MSACU = (0x07 << 6) | OPC_MULTU, OPC_VR54XX_MULHI = (0x09 << 6) | OPC_MULT, OPC_VR54XX_MULHIU = (0x09 << 6) | OPC_MULTU, OPC_VR54XX_MULSHI = (0x0B << 6) | OPC_MULT, OPC_VR54XX_MULSHIU = (0x0B << 6) | OPC_MULTU, - OPC_VR54XX_MACCHI = (0x0D << 6) | OPC_MULT, - OPC_VR54XX_MACCHIU = (0x0D << 6) | OPC_MULTU, OPC_VR54XX_MSACHI = (0x0F << 6) | OPC_MULT, OPC_VR54XX_MSACHIU = (0x0F << 6) | OPC_MULTU, }; @@ -3780,12 +3776,6 @@ static void gen_mul_vr54xx(DisasContext *ctx, uint32_t opc, case OPC_VR54XX_MULSU: gen_helper_mulsu(t0, cpu_env, t0, t1); break; - case OPC_VR54XX_MACC: - gen_helper_macc(t0, cpu_env, t0, t1); - break; - case OPC_VR54XX_MACCU: - gen_helper_maccu(t0, cpu_env, t0, t1); - break; case OPC_VR54XX_MSAC: gen_helper_msac(t0, cpu_env, t0, t1); break; @@ -3804,12 +3794,6 @@ static void gen_mul_vr54xx(DisasContext *ctx, uint32_t opc, case OPC_VR54XX_MULSHIU: gen_helper_mulshiu(t0, cpu_env, t0, t1); break; - case OPC_VR54XX_MACCHI: - gen_helper_macchi(t0, cpu_env, t0, t1); - break; - case OPC_VR54XX_MACCHIU: - gen_helper_macchiu(t0, cpu_env, t0, t1); - break; case OPC_VR54XX_MSACHI: gen_helper_msachi(t0, cpu_env, t0, t1); break; diff --git a/target/mips/tcg/vr54xx.decode b/target/mips/tcg/vr54xx.decode index f6b3e42c99..73778f101a 100644 --- a/target/mips/tcg/vr54xx.decode +++ b/target/mips/tcg/vr54xx.decode @@ -6,3 +6,12 @@ # # Reference: VR5432 Microprocessor User’s Manual # (Document Number U13751EU5V0UM00) + +&r rs rt rd + +@rs_rt_rd ...... rs:5 rt:5 rd:5 ..... ...... &r + +MACC 000000 ..... ..... ..... 00101011000 @rs_rt_rd +MACCU 000000 ..... ..... ..... 00101011001 @rs_rt_rd +MACCHI 000000 ..... ..... ..... 01101011000 @rs_rt_rd +MACCHIU 000000 ..... ..... ..... 01101011001 @rs_rt_rd diff --git a/target/mips/tcg/vr54xx_translate.c b/target/mips/tcg/vr54xx_translate.c index 13e58fdd8d..0e2d460c98 100644 --- a/target/mips/tcg/vr54xx_translate.c +++ b/target/mips/tcg/vr54xx_translate.c @@ -17,3 +17,36 @@ /* Include the auto-generated decoder. */ #include "decode-vr54xx.c.inc" + +/* + * Integer Multiply-Accumulate Instructions + * + * MACC Multiply, accumulate, and move LO + * MACCHI Multiply, accumulate, and move HI + * MACCHIU Unsigned multiply, accumulate, and move HI + * MACCU Unsigned multiply, accumulate, and move LO + */ + +static bool trans_mult_acc(DisasContext *ctx, arg_r *a, + void (*gen_helper_mult_acc)(TCGv, TCGv_ptr, TCGv, TCGv)) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_load_gpr(t0, a->rs); + gen_load_gpr(t1, a->rt); + + gen_helper_mult_acc(t0, cpu_env, t0, t1); + + gen_store_gpr(t0, a->rd); + + tcg_temp_free(t0); + tcg_temp_free(t1); + + return false; +} + +TRANS(MACC, trans_mult_acc, gen_helper_macc); +TRANS(MACCHI, trans_mult_acc, gen_helper_macchi); +TRANS(MACCHIU, trans_mult_acc, gen_helper_macchiu); +TRANS(MACCU, trans_mult_acc, gen_helper_maccu); From a5e2932068f1b09fa3be3b79a88f935b6e9620c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Jul 2021 13:25:53 +0200 Subject: [PATCH 063/493] target/mips: Convert Vr54xx MUL* opcodes to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the following Integer Multiply-Accumulate opcodes: * MULHI Multiply and move HI * MULHIU Unsigned multiply and move HI * MULS Multiply, negate, and move LO * MULSHI Multiply, negate, and move HI * MULSHIU Unsigned multiply, negate, and move HI * MULSU Unsigned multiply, negate, and move LO Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210808173018.90960-7-f4bug@amsat.org> --- target/mips/tcg/translate.c | 24 ------------------------ target/mips/tcg/vr54xx.decode | 6 ++++++ target/mips/tcg/vr54xx_translate.c | 12 ++++++++++++ 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index fd8ffadf06..4b7f2d9ae8 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -298,14 +298,8 @@ enum { #define MASK_MUL_VR54XX(op) (MASK_SPECIAL(op) | (op & (0x1F << 6))) enum { - OPC_VR54XX_MULS = (0x03 << 6) | OPC_MULT, - OPC_VR54XX_MULSU = (0x03 << 6) | OPC_MULTU, OPC_VR54XX_MSAC = (0x07 << 6) | OPC_MULT, OPC_VR54XX_MSACU = (0x07 << 6) | OPC_MULTU, - OPC_VR54XX_MULHI = (0x09 << 6) | OPC_MULT, - OPC_VR54XX_MULHIU = (0x09 << 6) | OPC_MULTU, - OPC_VR54XX_MULSHI = (0x0B << 6) | OPC_MULT, - OPC_VR54XX_MULSHIU = (0x0B << 6) | OPC_MULTU, OPC_VR54XX_MSACHI = (0x0F << 6) | OPC_MULT, OPC_VR54XX_MSACHIU = (0x0F << 6) | OPC_MULTU, }; @@ -3770,30 +3764,12 @@ static void gen_mul_vr54xx(DisasContext *ctx, uint32_t opc, gen_load_gpr(t1, rt); switch (opc) { - case OPC_VR54XX_MULS: - gen_helper_muls(t0, cpu_env, t0, t1); - break; - case OPC_VR54XX_MULSU: - gen_helper_mulsu(t0, cpu_env, t0, t1); - break; case OPC_VR54XX_MSAC: gen_helper_msac(t0, cpu_env, t0, t1); break; case OPC_VR54XX_MSACU: gen_helper_msacu(t0, cpu_env, t0, t1); break; - case OPC_VR54XX_MULHI: - gen_helper_mulhi(t0, cpu_env, t0, t1); - break; - case OPC_VR54XX_MULHIU: - gen_helper_mulhiu(t0, cpu_env, t0, t1); - break; - case OPC_VR54XX_MULSHI: - gen_helper_mulshi(t0, cpu_env, t0, t1); - break; - case OPC_VR54XX_MULSHIU: - gen_helper_mulshiu(t0, cpu_env, t0, t1); - break; case OPC_VR54XX_MSACHI: gen_helper_msachi(t0, cpu_env, t0, t1); break; diff --git a/target/mips/tcg/vr54xx.decode b/target/mips/tcg/vr54xx.decode index 73778f101a..79bb5175ea 100644 --- a/target/mips/tcg/vr54xx.decode +++ b/target/mips/tcg/vr54xx.decode @@ -11,7 +11,13 @@ @rs_rt_rd ...... rs:5 rt:5 rd:5 ..... ...... &r +MULS 000000 ..... ..... ..... 00011011000 @rs_rt_rd +MULSU 000000 ..... ..... ..... 00011011001 @rs_rt_rd MACC 000000 ..... ..... ..... 00101011000 @rs_rt_rd MACCU 000000 ..... ..... ..... 00101011001 @rs_rt_rd +MULHI 000000 ..... ..... ..... 01001011000 @rs_rt_rd +MULHIU 000000 ..... ..... ..... 01001011001 @rs_rt_rd +MULSHI 000000 ..... ..... ..... 01011011000 @rs_rt_rd +MULSHIU 000000 ..... ..... ..... 01011011001 @rs_rt_rd MACCHI 000000 ..... ..... ..... 01101011000 @rs_rt_rd MACCHIU 000000 ..... ..... ..... 01101011001 @rs_rt_rd diff --git a/target/mips/tcg/vr54xx_translate.c b/target/mips/tcg/vr54xx_translate.c index 0e2d460c98..9f35b2c7e5 100644 --- a/target/mips/tcg/vr54xx_translate.c +++ b/target/mips/tcg/vr54xx_translate.c @@ -25,6 +25,12 @@ * MACCHI Multiply, accumulate, and move HI * MACCHIU Unsigned multiply, accumulate, and move HI * MACCU Unsigned multiply, accumulate, and move LO + * MULHI Multiply and move HI + * MULHIU Unsigned multiply and move HI + * MULS Multiply, negate, and move LO + * MULSHI Multiply, negate, and move HI + * MULSHIU Unsigned multiply, negate, and move HI + * MULSU Unsigned multiply, negate, and move LO */ static bool trans_mult_acc(DisasContext *ctx, arg_r *a, @@ -50,3 +56,9 @@ TRANS(MACC, trans_mult_acc, gen_helper_macc); TRANS(MACCHI, trans_mult_acc, gen_helper_macchi); TRANS(MACCHIU, trans_mult_acc, gen_helper_macchiu); TRANS(MACCU, trans_mult_acc, gen_helper_maccu); +TRANS(MULHI, trans_mult_acc, gen_helper_mulhi); +TRANS(MULHIU, trans_mult_acc, gen_helper_mulhiu); +TRANS(MULS, trans_mult_acc, gen_helper_muls); +TRANS(MULSHI, trans_mult_acc, gen_helper_mulshi); +TRANS(MULSHIU, trans_mult_acc, gen_helper_mulshiu); +TRANS(MULSU, trans_mult_acc, gen_helper_mulsu); From bf7720024c62c9e2707b11aa7fe178be691b2f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Jul 2021 13:26:10 +0200 Subject: [PATCH 064/493] target/mips: Convert Vr54xx MSA* opcodes to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the following Integer Multiply-Accumulate opcodes: * MSAC Multiply, negate, accumulate, and move LO * MSACHI Multiply, negate, accumulate, and move HI * MSACHIU Unsigned multiply, negate, accumulate, and move HI * MSACU Unsigned multiply, negate, accumulate, and move LO Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210808173018.90960-8-f4bug@amsat.org> --- target/mips/tcg/translate.c | 55 ++---------------------------- target/mips/tcg/vr54xx.decode | 4 +++ target/mips/tcg/vr54xx_translate.c | 8 +++++ 3 files changed, 14 insertions(+), 53 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 4b7f2d9ae8..30780deb96 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -294,16 +294,6 @@ enum { R6_OPC_SDBBP = 0x0e | OPC_SPECIAL, }; -/* Multiplication variants of the vr54xx. */ -#define MASK_MUL_VR54XX(op) (MASK_SPECIAL(op) | (op & (0x1F << 6))) - -enum { - OPC_VR54XX_MSAC = (0x07 << 6) | OPC_MULT, - OPC_VR54XX_MSACU = (0x07 << 6) | OPC_MULTU, - OPC_VR54XX_MSACHI = (0x0F << 6) | OPC_MULT, - OPC_VR54XX_MSACHIU = (0x0F << 6) | OPC_MULTU, -}; - /* REGIMM (rt field) opcodes */ #define MASK_REGIMM(op) (MASK_OP_MAJOR(op) | (op & (0x1F << 16))) @@ -3754,40 +3744,6 @@ static void gen_mul_txx9(DisasContext *ctx, uint32_t opc, tcg_temp_free(t1); } -static void gen_mul_vr54xx(DisasContext *ctx, uint32_t opc, - int rd, int rs, int rt) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - - gen_load_gpr(t0, rs); - gen_load_gpr(t1, rt); - - switch (opc) { - case OPC_VR54XX_MSAC: - gen_helper_msac(t0, cpu_env, t0, t1); - break; - case OPC_VR54XX_MSACU: - gen_helper_msacu(t0, cpu_env, t0, t1); - break; - case OPC_VR54XX_MSACHI: - gen_helper_msachi(t0, cpu_env, t0, t1); - break; - case OPC_VR54XX_MSACHIU: - gen_helper_msachiu(t0, cpu_env, t0, t1); - break; - default: - MIPS_INVAL("mul vr54xx"); - gen_reserved_instruction(ctx); - goto out; - } - gen_store_gpr(t0, rd); - - out: - tcg_temp_free(t0); - tcg_temp_free(t1); -} - static void gen_cl(DisasContext *ctx, uint32_t opc, int rd, int rs) { @@ -14104,13 +14060,12 @@ static void decode_opc_special_tx79(CPUMIPSState *env, DisasContext *ctx) static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx) { - int rs, rt, rd, sa; + int rs, rt, rd; uint32_t op1; rs = (ctx->opcode >> 21) & 0x1f; rt = (ctx->opcode >> 16) & 0x1f; rd = (ctx->opcode >> 11) & 0x1f; - sa = (ctx->opcode >> 6) & 0x1f; op1 = MASK_SPECIAL(ctx->opcode); switch (op1) { @@ -14140,13 +14095,7 @@ static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx) break; case OPC_MULT: case OPC_MULTU: - if (sa) { - check_insn(ctx, INSN_VR54XX); - op1 = MASK_MUL_VR54XX(ctx->opcode); - gen_mul_vr54xx(ctx, op1, rd, rs, rt); - } else { - gen_muldiv(ctx, op1, rd & 3, rs, rt); - } + gen_muldiv(ctx, op1, rd & 3, rs, rt); break; case OPC_DIV: case OPC_DIVU: diff --git a/target/mips/tcg/vr54xx.decode b/target/mips/tcg/vr54xx.decode index 79bb5175ea..4fc708d80a 100644 --- a/target/mips/tcg/vr54xx.decode +++ b/target/mips/tcg/vr54xx.decode @@ -15,9 +15,13 @@ MULS 000000 ..... ..... ..... 00011011000 @rs_rt_rd MULSU 000000 ..... ..... ..... 00011011001 @rs_rt_rd MACC 000000 ..... ..... ..... 00101011000 @rs_rt_rd MACCU 000000 ..... ..... ..... 00101011001 @rs_rt_rd +MSAC 000000 ..... ..... ..... 00111011000 @rs_rt_rd +MSACU 000000 ..... ..... ..... 00111011001 @rs_rt_rd MULHI 000000 ..... ..... ..... 01001011000 @rs_rt_rd MULHIU 000000 ..... ..... ..... 01001011001 @rs_rt_rd MULSHI 000000 ..... ..... ..... 01011011000 @rs_rt_rd MULSHIU 000000 ..... ..... ..... 01011011001 @rs_rt_rd MACCHI 000000 ..... ..... ..... 01101011000 @rs_rt_rd MACCHIU 000000 ..... ..... ..... 01101011001 @rs_rt_rd +MSACHI 000000 ..... ..... ..... 01111011000 @rs_rt_rd +MSACHIU 000000 ..... ..... ..... 01111011001 @rs_rt_rd diff --git a/target/mips/tcg/vr54xx_translate.c b/target/mips/tcg/vr54xx_translate.c index 9f35b2c7e5..3e2c98f2c6 100644 --- a/target/mips/tcg/vr54xx_translate.c +++ b/target/mips/tcg/vr54xx_translate.c @@ -25,6 +25,10 @@ * MACCHI Multiply, accumulate, and move HI * MACCHIU Unsigned multiply, accumulate, and move HI * MACCU Unsigned multiply, accumulate, and move LO + * MSAC Multiply, negate, accumulate, and move LO + * MSACHI Multiply, negate, accumulate, and move HI + * MSACHIU Unsigned multiply, negate, accumulate, and move HI + * MSACU Unsigned multiply, negate, accumulate, and move LO * MULHI Multiply and move HI * MULHIU Unsigned multiply and move HI * MULS Multiply, negate, and move LO @@ -56,6 +60,10 @@ TRANS(MACC, trans_mult_acc, gen_helper_macc); TRANS(MACCHI, trans_mult_acc, gen_helper_macchi); TRANS(MACCHIU, trans_mult_acc, gen_helper_macchiu); TRANS(MACCU, trans_mult_acc, gen_helper_maccu); +TRANS(MSAC, trans_mult_acc, gen_helper_msac); +TRANS(MSACHI, trans_mult_acc, gen_helper_msachi); +TRANS(MSACHIU, trans_mult_acc, gen_helper_msachiu); +TRANS(MSACU, trans_mult_acc, gen_helper_msacu); TRANS(MULHI, trans_mult_acc, gen_helper_mulhi); TRANS(MULHIU, trans_mult_acc, gen_helper_mulhiu); TRANS(MULS, trans_mult_acc, gen_helper_muls); From 98d207cf9c232e6ac451b2aba24baa007956f578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Aug 2021 12:37:12 +0200 Subject: [PATCH 065/493] target/mips: Document Loongson-3A CPU definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the cores on which each Loongson-3A CPU is based (see commit af868995e1b, "target/mips: Add Loongson-3 CPU definition"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Huacai Chen Message-Id: <20210813110149.1432692-2-f4bug@amsat.org> --- target/mips/cpu-defs.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/mips/cpu-defs.c.inc b/target/mips/cpu-defs.c.inc index e03b2a998c..c6ab3af190 100644 --- a/target/mips/cpu-defs.c.inc +++ b/target/mips/cpu-defs.c.inc @@ -805,7 +805,7 @@ const mips_def_t mips_defs[] = .mmu_type = MMU_TYPE_R4000, }, { - .name = "Loongson-3A1000", + .name = "Loongson-3A1000", /* Loongson-3A R1, GS464-based */ .CP0_PRid = 0x6305, /* 64KB I-cache and d-cache. 4 way with 32 bit cache line size. */ .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | @@ -835,7 +835,7 @@ const mips_def_t mips_defs[] = .mmu_type = MMU_TYPE_R4000, }, { - .name = "Loongson-3A4000", /* GS464V-based */ + .name = "Loongson-3A4000", /* Loongson-3A R4, GS464V-based */ .CP0_PRid = 0x14C000, /* 64KB I-cache and d-cache. 4 way with 32 bit cache line size. */ .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | From 71ed30b7d4c7bc7d8069eba601d7384e378f3024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 13 Aug 2021 12:36:46 +0200 Subject: [PATCH 066/493] target/mips: Allow Loongson 3A1000 to use up to 48-bit VAddr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the manual '龙芯 GS264 处理器核用户手册' v1.0, chapter 1.1.5 SEGBITS: the 3A1000 (based on GS464 core) implements 48 virtual address bits in each 64-bit segment, not 40. Fixes: af868995e1b ("target/mips: Add Loongson-3 CPU definition") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Huacai Chen Message-Id: <20210813110149.1432692-3-f4bug@amsat.org> --- target/mips/cpu-defs.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/mips/cpu-defs.c.inc b/target/mips/cpu-defs.c.inc index c6ab3af190..cbc45fcb0e 100644 --- a/target/mips/cpu-defs.c.inc +++ b/target/mips/cpu-defs.c.inc @@ -828,7 +828,7 @@ const mips_def_t mips_defs[] = (0x1 << FCR0_D) | (0x1 << FCR0_S), .CP1_fcr31 = 0, .CP1_fcr31_rw_bitmask = 0xFF83FFFF, - .SEGBITS = 42, + .SEGBITS = 48, .PABITS = 48, .insn_flags = CPU_MIPS64R2 | INSN_LOONGSON3A | ASE_LMMI | ASE_LEXT, From c1feb46d12a7e7aeed804f2cca69f509c1bea8ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Aug 2021 02:04:04 +0200 Subject: [PATCH 067/493] target/mips: Remove duplicated check_cp1_enabled() calls in Loongson EXT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already call check_cp1_enabled() earlier in the "pre-conditions" checks for GSLWXC1 and GSLDXC1 in gen_loongson_lsdc2() prologue. Remove the duplicated calls. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Huacai Chen Message-Id: <20210816001031.1720432-1-f4bug@amsat.org> --- target/mips/tcg/translate.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 30780deb96..a58d50e40e 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -4693,7 +4693,6 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, break; #endif case OPC_GSLWXC1: - check_cp1_enabled(ctx); gen_base_offset_addr(ctx, t0, rs, offset); if (rd) { gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0); @@ -4706,7 +4705,6 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, break; #if defined(TARGET_MIPS64) case OPC_GSLDXC1: - check_cp1_enabled(ctx); gen_base_offset_addr(ctx, t0, rs, offset); if (rd) { gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0); From b24339bcd05b9d772f86c74732932bac98a15f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Aug 2021 15:45:45 +0200 Subject: [PATCH 068/493] target/mips: Remove gen_helper_0e3i() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gen_helper_0e3i() is unused since commit 895c2d04359 ("target-mips: switch to AREG0 free mode"), remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210816205107.2051495-2-f4bug@amsat.org> --- target/mips/tcg/translate.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index a58d50e40e..c0f8a04b47 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -1249,12 +1249,6 @@ TCGv_i64 fpu_f64[32]; tcg_temp_free_i32(helper_tmp); \ } while (0) -#define gen_helper_0e3i(name, arg1, arg2, arg3, arg4) do { \ - TCGv_i32 helper_tmp = tcg_const_i32(arg4); \ - gen_helper_##name(cpu_env, arg1, arg2, arg3, helper_tmp); \ - tcg_temp_free_i32(helper_tmp); \ - } while (0) - #define DISAS_STOP DISAS_TARGET_0 #define DISAS_EXIT DISAS_TARGET_1 From 53152abfc148ebfb7843ef3596f4be5bc8e5eff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Aug 2021 15:41:40 +0200 Subject: [PATCH 069/493] target/mips: Remove gen_helper_1e2i() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gen_helper_1e2i() is unused since commit 33a07fa2db6 ("target/mips: reimplement SC instruction emulation and use cmpxchg"), remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210816205107.2051495-3-f4bug@amsat.org> --- target/mips/tcg/translate.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index c0f8a04b47..4b689a54ab 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -1243,12 +1243,6 @@ TCGv_i64 fpu_f64[32]; tcg_temp_free_i32(helper_tmp); \ } while (0) -#define gen_helper_1e2i(name, ret, arg1, arg2, arg3) do { \ - TCGv_i32 helper_tmp = tcg_const_i32(arg3); \ - gen_helper_##name(ret, cpu_env, arg1, arg2, helper_tmp); \ - tcg_temp_free_i32(helper_tmp); \ - } while (0) - #define DISAS_STOP DISAS_TARGET_0 #define DISAS_EXIT DISAS_TARGET_1 From 78bdd3886577ce4f3e573685b155622e19bcca38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 15 Aug 2021 15:51:49 +0200 Subject: [PATCH 070/493] target/mips: Use tcg_constant_i32() in gen_helper_0e2i() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit $rt register is used read-only, so we can replace tcg_const_i32() temporary by tcg_constant_i32(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210816205107.2051495-4-f4bug@amsat.org> --- target/mips/tcg/translate.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 4b689a54ab..a6df9beb67 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -9072,12 +9072,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, break; case 3: /* XXX: For now we support only a single FPU context. */ - { - TCGv_i32 fs_tmp = tcg_const_i32(rd); - - gen_helper_0e2i(ctc1, t0, fs_tmp, rt); - tcg_temp_free_i32(fs_tmp); - } + gen_helper_0e2i(ctc1, t0, tcg_constant_i32(rd), rt); /* Stop translation as we may have changed hflags */ ctx->base.is_jmp = DISAS_STOP; break; @@ -9694,12 +9689,7 @@ static void gen_cp1(DisasContext *ctx, uint32_t opc, int rt, int fs) case OPC_CTC1: gen_load_gpr(t0, rt); save_cpu_state(ctx, 0); - { - TCGv_i32 fs_tmp = tcg_const_i32(fs); - - gen_helper_0e2i(ctc1, t0, fs_tmp, rt); - tcg_temp_free_i32(fs_tmp); - } + gen_helper_0e2i(ctc1, t0, tcg_constant_i32(fs), rt); /* Stop translation as we may have changed hflags */ ctx->base.is_jmp = DISAS_STOP; break; From 26fe92763aaf3b0d704fbdb32daf434147dc6ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 15 Aug 2021 15:07:40 +0200 Subject: [PATCH 071/493] target/mips: Simplify gen_helper() macros by using tcg_constant_i32() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all call sites the last argument is always used as a read-only value, so we can replace tcg_const_i32() temporary by tcg_constant_i32(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210816205107.2051495-5-f4bug@amsat.org> --- target/mips/tcg/translate.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index a6df9beb67..3417fc433f 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -1214,33 +1214,23 @@ TCGv_i64 fpu_f64[32]; #include "exec/gen-icount.h" #define gen_helper_0e0i(name, arg) do { \ - TCGv_i32 helper_tmp = tcg_const_i32(arg); \ - gen_helper_##name(cpu_env, helper_tmp); \ - tcg_temp_free_i32(helper_tmp); \ + gen_helper_##name(cpu_env, tcg_constant_i32(arg)); \ } while (0) #define gen_helper_0e1i(name, arg1, arg2) do { \ - TCGv_i32 helper_tmp = tcg_const_i32(arg2); \ - gen_helper_##name(cpu_env, arg1, helper_tmp); \ - tcg_temp_free_i32(helper_tmp); \ + gen_helper_##name(cpu_env, arg1, tcg_constant_i32(arg2)); \ } while (0) #define gen_helper_1e0i(name, ret, arg1) do { \ - TCGv_i32 helper_tmp = tcg_const_i32(arg1); \ - gen_helper_##name(ret, cpu_env, helper_tmp); \ - tcg_temp_free_i32(helper_tmp); \ + gen_helper_##name(ret, cpu_env, tcg_constant_i32(arg1)); \ } while (0) #define gen_helper_1e1i(name, ret, arg1, arg2) do { \ - TCGv_i32 helper_tmp = tcg_const_i32(arg2); \ - gen_helper_##name(ret, cpu_env, arg1, helper_tmp); \ - tcg_temp_free_i32(helper_tmp); \ + gen_helper_##name(ret, cpu_env, arg1, tcg_constant_i32(arg2));\ } while (0) #define gen_helper_0e2i(name, arg1, arg2, arg3) do { \ - TCGv_i32 helper_tmp = tcg_const_i32(arg3); \ - gen_helper_##name(cpu_env, arg1, arg2, helper_tmp); \ - tcg_temp_free_i32(helper_tmp); \ + gen_helper_##name(cpu_env, arg1, arg2, tcg_constant_i32(arg3));\ } while (0) #define DISAS_STOP DISAS_TARGET_0 From a1b4b060d756dbc2d221d340b7adbc3e694e06d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 15 Aug 2021 15:46:09 +0200 Subject: [PATCH 072/493] target/mips: Inline gen_helper_1e1i() call in op_ld_INSN() macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gen_helper_1e1i() is one-line long and is used in one place: simply inline it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210816205107.2051495-6-f4bug@amsat.org> --- target/mips/tcg/translate.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 3417fc433f..db7fc75d93 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -1225,10 +1225,6 @@ TCGv_i64 fpu_f64[32]; gen_helper_##name(ret, cpu_env, tcg_constant_i32(arg1)); \ } while (0) -#define gen_helper_1e1i(name, ret, arg1, arg2) do { \ - gen_helper_##name(ret, cpu_env, arg1, tcg_constant_i32(arg2));\ - } while (0) - #define gen_helper_0e2i(name, arg1, arg2, arg3) do { \ gen_helper_##name(cpu_env, arg1, arg2, tcg_constant_i32(arg3));\ } while (0) @@ -1991,7 +1987,7 @@ static inline void op_ld_##insn(TCGv ret, TCGv arg1, int mem_idx, \ static inline void op_ld_##insn(TCGv ret, TCGv arg1, int mem_idx, \ DisasContext *ctx) \ { \ - gen_helper_1e1i(insn, ret, arg1, mem_idx); \ + gen_helper_##insn(ret, cpu_env, arg1, tcg_constant_i32(mem_idx)); \ } #endif OP_LD_ATOMIC(ll, ld32s); From ae71abadd58037dd457f021f9be381e32f1815ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 15 Aug 2021 15:48:58 +0200 Subject: [PATCH 073/493] target/mips: Inline gen_helper_0e0i() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gen_helper_0e0i() is one-line long and is only used twice: simply inline it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210816205107.2051495-7-f4bug@amsat.org> --- target/mips/tcg/translate.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index db7fc75d93..c515a337eb 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -1213,10 +1213,6 @@ TCGv_i64 fpu_f64[32]; #include "exec/gen-icount.h" -#define gen_helper_0e0i(name, arg) do { \ - gen_helper_##name(cpu_env, tcg_constant_i32(arg)); \ - } while (0) - #define gen_helper_0e1i(name, arg1, arg2) do { \ gen_helper_##name(cpu_env, arg1, tcg_constant_i32(arg2)); \ } while (0) @@ -1378,7 +1374,7 @@ void generate_exception_err(DisasContext *ctx, int excp, int err) void generate_exception(DisasContext *ctx, int excp) { - gen_helper_0e0i(raise_exception, excp); + gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); } void generate_exception_end(DisasContext *ctx, int excp) @@ -14188,7 +14184,7 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) MIPS_INVAL("PMON / selsl"); gen_reserved_instruction(ctx); #else - gen_helper_0e0i(pmon, sa); + gen_helper_pmon(cpu_env, tcg_constant_i32(sa)); #endif break; case OPC_SYSCALL: From a8b18de7f5f8283633e5fc3378fbd6d0c9f4a1ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 15 Aug 2021 15:56:13 +0200 Subject: [PATCH 074/493] target/mips: Use tcg_constant_i32() in generate_exception_err() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit excp/err are temporaries input, so we can replace tcg_const_i32() calls by tcg_constant_i32() equivalent. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210816205107.2051495-8-f4bug@amsat.org> --- target/mips/tcg/translate.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index c515a337eb..93b72c994f 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -1363,12 +1363,9 @@ static inline void restore_cpu_state(CPUMIPSState *env, DisasContext *ctx) void generate_exception_err(DisasContext *ctx, int excp, int err) { - TCGv_i32 texcp = tcg_const_i32(excp); - TCGv_i32 terr = tcg_const_i32(err); save_cpu_state(ctx, 1); - gen_helper_raise_exception_err(cpu_env, texcp, terr); - tcg_temp_free_i32(terr); - tcg_temp_free_i32(texcp); + gen_helper_raise_exception_err(cpu_env, tcg_constant_i32(excp), + tcg_constant_i32(err)); ctx->base.is_jmp = DISAS_NORETURN; } From 761533fc9a8df2e7d276b7e1fc22cad7d0fd0352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 15 Aug 2021 16:02:41 +0200 Subject: [PATCH 075/493] target/mips: Define gen_helper() macros in translate.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To be able to split some code calling the gen_helper() macros out of the huge translate.c, we need to define them in the 'translate.h' local header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210816205107.2051495-9-f4bug@amsat.org> --- target/mips/tcg/translate.c | 12 ------------ target/mips/tcg/translate.h | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 93b72c994f..40cb1fc950 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -1213,18 +1213,6 @@ TCGv_i64 fpu_f64[32]; #include "exec/gen-icount.h" -#define gen_helper_0e1i(name, arg1, arg2) do { \ - gen_helper_##name(cpu_env, arg1, tcg_constant_i32(arg2)); \ - } while (0) - -#define gen_helper_1e0i(name, ret, arg1) do { \ - gen_helper_##name(ret, cpu_env, tcg_constant_i32(arg1)); \ - } while (0) - -#define gen_helper_0e2i(name, arg1, arg2, arg3) do { \ - gen_helper_##name(cpu_env, arg1, arg2, tcg_constant_i32(arg3));\ - } while (0) - #define DISAS_STOP DISAS_TARGET_0 #define DISAS_EXIT DISAS_TARGET_1 diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index bb0a6b8d74..eac01a8118 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -113,6 +113,18 @@ enum { OPC_BC1TANY4 = (0x01 << 16) | OPC_BC1ANY4, }; +#define gen_helper_0e1i(name, arg1, arg2) do { \ + gen_helper_##name(cpu_env, arg1, tcg_constant_i32(arg2)); \ + } while (0) + +#define gen_helper_1e0i(name, ret, arg1) do { \ + gen_helper_##name(ret, cpu_env, tcg_constant_i32(arg1)); \ + } while (0) + +#define gen_helper_0e2i(name, arg1, arg2, arg3) do { \ + gen_helper_##name(cpu_env, arg1, arg2, tcg_constant_i32(arg3));\ + } while (0) + void generate_exception(DisasContext *ctx, int excp); void generate_exception_err(DisasContext *ctx, int excp, int err); void generate_exception_end(DisasContext *ctx, int excp); From 5b3cc34c346e216c81f0750f586a016c33f2e08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 18 Aug 2021 12:10:53 +0200 Subject: [PATCH 076/493] target/mips: Call cpu_is_bigendian & inline GET_OFFSET in ld/st helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The target endianess information is stored in the BigEndian bit of the Config0 register in CP0. As a first step, inline the GET_OFFSET() macro, calling cpu_is_bigendian() to get the 'direction' of the offset. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210818215517.2560994-2-f4bug@amsat.org> --- target/mips/tcg/ldst_helper.c | 55 +++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/target/mips/tcg/ldst_helper.c b/target/mips/tcg/ldst_helper.c index d42812b8a6..8d1dfea676 100644 --- a/target/mips/tcg/ldst_helper.c +++ b/target/mips/tcg/ldst_helper.c @@ -52,31 +52,36 @@ HELPER_LD_ATOMIC(lld, ldq, 0x7, (target_ulong)) #endif /* !CONFIG_USER_ONLY */ +static inline bool cpu_is_bigendian(CPUMIPSState *env) +{ + return extract32(env->CP0_Config0, CP0C0_BE, 1); +} + #ifdef TARGET_WORDS_BIGENDIAN #define GET_LMASK(v) ((v) & 3) -#define GET_OFFSET(addr, offset) (addr + (offset)) #else #define GET_LMASK(v) (((v) & 3) ^ 3) -#define GET_OFFSET(addr, offset) (addr - (offset)) #endif void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { + int dir = cpu_is_bigendian(env) ? 1 : -1; + cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); if (GET_LMASK(arg2) <= 2) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16), + cpu_stb_mmuidx_ra(env, arg2 + 1 * dir, (uint8_t)(arg1 >> 16), mem_idx, GETPC()); } if (GET_LMASK(arg2) <= 1) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8), + cpu_stb_mmuidx_ra(env, arg2 + 2 * dir, (uint8_t)(arg1 >> 8), mem_idx, GETPC()); } if (GET_LMASK(arg2) == 0) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 3), (uint8_t)arg1, + cpu_stb_mmuidx_ra(env, arg2 + 3 * dir, (uint8_t)arg1, mem_idx, GETPC()); } } @@ -84,20 +89,22 @@ void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { + int dir = cpu_is_bigendian(env) ? 1 : -1; + cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); if (GET_LMASK(arg2) >= 1) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), + cpu_stb_mmuidx_ra(env, arg2 - 1 * dir, (uint8_t)(arg1 >> 8), mem_idx, GETPC()); } if (GET_LMASK(arg2) >= 2) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), + cpu_stb_mmuidx_ra(env, arg2 - 2 * dir, (uint8_t)(arg1 >> 16), mem_idx, GETPC()); } if (GET_LMASK(arg2) == 3) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), + cpu_stb_mmuidx_ra(env, arg2 - 3 * dir, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); } } @@ -116,40 +123,42 @@ void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { + int dir = cpu_is_bigendian(env) ? 1 : -1; + cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 56), mem_idx, GETPC()); if (GET_LMASK64(arg2) <= 6) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48), + cpu_stb_mmuidx_ra(env, arg2 + 1 * dir, (uint8_t)(arg1 >> 48), mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 5) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40), + cpu_stb_mmuidx_ra(env, arg2 + 2 * dir, (uint8_t)(arg1 >> 40), mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 4) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32), + cpu_stb_mmuidx_ra(env, arg2 + 3 * dir, (uint8_t)(arg1 >> 32), mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 3) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24), + cpu_stb_mmuidx_ra(env, arg2 + 4 * dir, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 2) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16), + cpu_stb_mmuidx_ra(env, arg2 + 5 * dir, (uint8_t)(arg1 >> 16), mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 1) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8), + cpu_stb_mmuidx_ra(env, arg2 + 6 * dir, (uint8_t)(arg1 >> 8), mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 0) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 7), (uint8_t)arg1, + cpu_stb_mmuidx_ra(env, arg2 + 7 * dir, (uint8_t)arg1, mem_idx, GETPC()); } } @@ -157,40 +166,42 @@ void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { + int dir = cpu_is_bigendian(env) ? 1 : -1; + cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); if (GET_LMASK64(arg2) >= 1) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), + cpu_stb_mmuidx_ra(env, arg2 - 1 * dir, (uint8_t)(arg1 >> 8), mem_idx, GETPC()); } if (GET_LMASK64(arg2) >= 2) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), + cpu_stb_mmuidx_ra(env, arg2 - 2 * dir, (uint8_t)(arg1 >> 16), mem_idx, GETPC()); } if (GET_LMASK64(arg2) >= 3) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), + cpu_stb_mmuidx_ra(env, arg2 - 3 * dir, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); } if (GET_LMASK64(arg2) >= 4) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32), + cpu_stb_mmuidx_ra(env, arg2 - 4 * dir, (uint8_t)(arg1 >> 32), mem_idx, GETPC()); } if (GET_LMASK64(arg2) >= 5) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40), + cpu_stb_mmuidx_ra(env, arg2 - 5 * dir, (uint8_t)(arg1 >> 40), mem_idx, GETPC()); } if (GET_LMASK64(arg2) >= 6) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48), + cpu_stb_mmuidx_ra(env, arg2 - 6 * dir, (uint8_t)(arg1 >> 48), mem_idx, GETPC()); } if (GET_LMASK64(arg2) == 7) { - cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56), + cpu_stb_mmuidx_ra(env, arg2 - 7 * dir, (uint8_t)(arg1 >> 56), mem_idx, GETPC()); } } From 4885b99a6e05d16f40af18a062a5cb45fa92ec27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 18 Aug 2021 12:11:30 +0200 Subject: [PATCH 077/493] target/mips: Replace GET_LMASK() macro by get_lmask(32) function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The target endianess information is stored in the BigEndian bit of the Config0 register in CP0. Replace the GET_LMASK() macro by an inlined get_lmask() function, passing CPUMIPSState and the word size as argument. We can remove one use of the TARGET_WORDS_BIGENDIAN definition. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210818215517.2560994-3-f4bug@amsat.org> --- target/mips/tcg/ldst_helper.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/target/mips/tcg/ldst_helper.c b/target/mips/tcg/ldst_helper.c index 8d1dfea676..054459703a 100644 --- a/target/mips/tcg/ldst_helper.c +++ b/target/mips/tcg/ldst_helper.c @@ -57,30 +57,39 @@ static inline bool cpu_is_bigendian(CPUMIPSState *env) return extract32(env->CP0_Config0, CP0C0_BE, 1); } -#ifdef TARGET_WORDS_BIGENDIAN -#define GET_LMASK(v) ((v) & 3) -#else -#define GET_LMASK(v) (((v) & 3) ^ 3) -#endif +static inline target_ulong get_lmask(CPUMIPSState *env, + target_ulong value, unsigned bits) +{ + unsigned mask = (bits / BITS_PER_BYTE) - 1; + + value &= mask; + + if (!cpu_is_bigendian(env)) { + value ^= mask; + } + + return value; +} void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { + target_ulong lmask = get_lmask(env, arg2, 32); int dir = cpu_is_bigendian(env) ? 1 : -1; cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); - if (GET_LMASK(arg2) <= 2) { + if (lmask <= 2) { cpu_stb_mmuidx_ra(env, arg2 + 1 * dir, (uint8_t)(arg1 >> 16), mem_idx, GETPC()); } - if (GET_LMASK(arg2) <= 1) { + if (lmask <= 1) { cpu_stb_mmuidx_ra(env, arg2 + 2 * dir, (uint8_t)(arg1 >> 8), mem_idx, GETPC()); } - if (GET_LMASK(arg2) == 0) { + if (lmask == 0) { cpu_stb_mmuidx_ra(env, arg2 + 3 * dir, (uint8_t)arg1, mem_idx, GETPC()); } @@ -89,21 +98,22 @@ void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { + target_ulong lmask = get_lmask(env, arg2, 32); int dir = cpu_is_bigendian(env) ? 1 : -1; cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); - if (GET_LMASK(arg2) >= 1) { + if (lmask >= 1) { cpu_stb_mmuidx_ra(env, arg2 - 1 * dir, (uint8_t)(arg1 >> 8), mem_idx, GETPC()); } - if (GET_LMASK(arg2) >= 2) { + if (lmask >= 2) { cpu_stb_mmuidx_ra(env, arg2 - 2 * dir, (uint8_t)(arg1 >> 16), mem_idx, GETPC()); } - if (GET_LMASK(arg2) == 3) { + if (lmask == 3) { cpu_stb_mmuidx_ra(env, arg2 - 3 * dir, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); } From 23a04dcdf6fa6b34933778b16ee8200027dc6e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 18 Aug 2021 12:11:41 +0200 Subject: [PATCH 078/493] target/mips: Replace GET_LMASK64() macro by get_lmask(64) function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The target endianess information is stored in the BigEndian bit of the Config0 register in CP0. Replace the GET_LMASK() macro by an inlined get_lmask() function, passing CPUMIPSState and the word size as argument. We can remove another use of the TARGET_WORDS_BIGENDIAN definition. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210818215517.2560994-4-f4bug@amsat.org> --- target/mips/tcg/ldst_helper.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/target/mips/tcg/ldst_helper.c b/target/mips/tcg/ldst_helper.c index 054459703a..d0bd0267b2 100644 --- a/target/mips/tcg/ldst_helper.c +++ b/target/mips/tcg/ldst_helper.c @@ -124,50 +124,46 @@ void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, * "half" load and stores. We must do the memory access inline, * or fault handling won't work. */ -#ifdef TARGET_WORDS_BIGENDIAN -#define GET_LMASK64(v) ((v) & 7) -#else -#define GET_LMASK64(v) (((v) & 7) ^ 7) -#endif void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { + target_ulong lmask = get_lmask(env, arg2, 64); int dir = cpu_is_bigendian(env) ? 1 : -1; cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 56), mem_idx, GETPC()); - if (GET_LMASK64(arg2) <= 6) { + if (lmask <= 6) { cpu_stb_mmuidx_ra(env, arg2 + 1 * dir, (uint8_t)(arg1 >> 48), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) <= 5) { + if (lmask <= 5) { cpu_stb_mmuidx_ra(env, arg2 + 2 * dir, (uint8_t)(arg1 >> 40), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) <= 4) { + if (lmask <= 4) { cpu_stb_mmuidx_ra(env, arg2 + 3 * dir, (uint8_t)(arg1 >> 32), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) <= 3) { + if (lmask <= 3) { cpu_stb_mmuidx_ra(env, arg2 + 4 * dir, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) <= 2) { + if (lmask <= 2) { cpu_stb_mmuidx_ra(env, arg2 + 5 * dir, (uint8_t)(arg1 >> 16), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) <= 1) { + if (lmask <= 1) { cpu_stb_mmuidx_ra(env, arg2 + 6 * dir, (uint8_t)(arg1 >> 8), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) <= 0) { + if (lmask <= 0) { cpu_stb_mmuidx_ra(env, arg2 + 7 * dir, (uint8_t)arg1, mem_idx, GETPC()); } @@ -176,41 +172,42 @@ void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { + target_ulong lmask = get_lmask(env, arg2, 64); int dir = cpu_is_bigendian(env) ? 1 : -1; cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); - if (GET_LMASK64(arg2) >= 1) { + if (lmask >= 1) { cpu_stb_mmuidx_ra(env, arg2 - 1 * dir, (uint8_t)(arg1 >> 8), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) >= 2) { + if (lmask >= 2) { cpu_stb_mmuidx_ra(env, arg2 - 2 * dir, (uint8_t)(arg1 >> 16), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) >= 3) { + if (lmask >= 3) { cpu_stb_mmuidx_ra(env, arg2 - 3 * dir, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) >= 4) { + if (lmask >= 4) { cpu_stb_mmuidx_ra(env, arg2 - 4 * dir, (uint8_t)(arg1 >> 32), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) >= 5) { + if (lmask >= 5) { cpu_stb_mmuidx_ra(env, arg2 - 5 * dir, (uint8_t)(arg1 >> 40), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) >= 6) { + if (lmask >= 6) { cpu_stb_mmuidx_ra(env, arg2 - 6 * dir, (uint8_t)(arg1 >> 48), mem_idx, GETPC()); } - if (GET_LMASK64(arg2) == 7) { + if (lmask == 7) { cpu_stb_mmuidx_ra(env, arg2 - 7 * dir, (uint8_t)(arg1 >> 56), mem_idx, GETPC()); } From 0cfd392d7b419cdbe5284b8227d95ab3daaea860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 18 Aug 2021 12:12:12 +0200 Subject: [PATCH 079/493] target/mips: Store CP0_Config0 in DisasContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most TCG helpers only have access to a DisasContext pointer, not CPUMIPSState. Store a copy of CPUMIPSState::CP0_Config0 in DisasContext so we can access it from TCG helpers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210818164321.2474534-5-f4bug@amsat.org> --- target/mips/tcg/translate.c | 1 + target/mips/tcg/translate.h | 1 + 2 files changed, 2 insertions(+) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 40cb1fc950..ac97f5f6f0 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -15979,6 +15979,7 @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; ctx->saved_pc = -1; ctx->insn_flags = env->insn_flags; + ctx->CP0_Config0 = env->CP0_Config0; ctx->CP0_Config1 = env->CP0_Config1; ctx->CP0_Config2 = env->CP0_Config2; ctx->CP0_Config3 = env->CP0_Config3; diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index eac01a8118..4b4fa2c207 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -18,6 +18,7 @@ typedef struct DisasContext { target_ulong page_start; uint32_t opcode; uint64_t insn_flags; + int32_t CP0_Config0; int32_t CP0_Config1; int32_t CP0_Config2; int32_t CP0_Config3; From bf78469cc8ddb117b6db4a353e59fb4664a96de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 18 Aug 2021 12:13:02 +0200 Subject: [PATCH 080/493] target/mips: Replace TARGET_WORDS_BIGENDIAN by cpu_is_bigendian() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the inlined cpu_is_bigendian() function in "translate.h". Replace the TARGET_WORDS_BIGENDIAN #ifdef'ry by calls to cpu_is_bigendian(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210818164321.2474534-6-f4bug@amsat.org> --- target/mips/tcg/nanomips_translate.c.inc | 20 +++---- target/mips/tcg/translate.c | 70 ++++++++++++------------ target/mips/tcg/translate.h | 5 ++ 3 files changed, 50 insertions(+), 45 deletions(-) diff --git a/target/mips/tcg/nanomips_translate.c.inc b/target/mips/tcg/nanomips_translate.c.inc index 09e64a6948..a66ae26796 100644 --- a/target/mips/tcg/nanomips_translate.c.inc +++ b/target/mips/tcg/nanomips_translate.c.inc @@ -999,11 +999,11 @@ static void gen_llwp(DisasContext *ctx, uint32_t base, int16_t offset, gen_base_offset_addr(ctx, taddr, base, offset); tcg_gen_qemu_ld64(tval, taddr, ctx->mem_idx); -#ifdef TARGET_WORDS_BIGENDIAN - tcg_gen_extr_i64_tl(tmp2, tmp1, tval); -#else - tcg_gen_extr_i64_tl(tmp1, tmp2, tval); -#endif + if (cpu_is_bigendian(ctx)) { + tcg_gen_extr_i64_tl(tmp2, tmp1, tval); + } else { + tcg_gen_extr_i64_tl(tmp1, tmp2, tval); + } gen_store_gpr(tmp1, reg1); tcg_temp_free(tmp1); gen_store_gpr(tmp2, reg2); @@ -1035,11 +1035,11 @@ static void gen_scwp(DisasContext *ctx, uint32_t base, int16_t offset, gen_load_gpr(tmp1, reg1); gen_load_gpr(tmp2, reg2); -#ifdef TARGET_WORDS_BIGENDIAN - tcg_gen_concat_tl_i64(tval, tmp2, tmp1); -#else - tcg_gen_concat_tl_i64(tval, tmp1, tmp2); -#endif + if (cpu_is_bigendian(ctx)) { + tcg_gen_concat_tl_i64(tval, tmp2, tmp1); + } else { + tcg_gen_concat_tl_i64(tval, tmp1, tmp2); + } tcg_gen_ld_i64(llval, cpu_env, offsetof(CPUMIPSState, llval_wp)); tcg_gen_atomic_cmpxchg_i64(val, taddr, llval, tval, diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index ac97f5f6f0..6f4a9a839c 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -2048,9 +2048,9 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, */ tcg_gen_qemu_ld_tl(t1, t0, mem_idx, MO_UB); tcg_gen_andi_tl(t1, t0, 7); -#ifndef TARGET_WORDS_BIGENDIAN - tcg_gen_xori_tl(t1, t1, 7); -#endif + if (!cpu_is_bigendian(ctx)) { + tcg_gen_xori_tl(t1, t1, 7); + } tcg_gen_shli_tl(t1, t1, 3); tcg_gen_andi_tl(t0, t0, ~7); tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEQ); @@ -2072,9 +2072,9 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, */ tcg_gen_qemu_ld_tl(t1, t0, mem_idx, MO_UB); tcg_gen_andi_tl(t1, t0, 7); -#ifdef TARGET_WORDS_BIGENDIAN - tcg_gen_xori_tl(t1, t1, 7); -#endif + if (cpu_is_bigendian(ctx)) { + tcg_gen_xori_tl(t1, t1, 7); + } tcg_gen_shli_tl(t1, t1, 3); tcg_gen_andi_tl(t0, t0, ~7); tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEQ); @@ -2153,9 +2153,9 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, */ tcg_gen_qemu_ld_tl(t1, t0, mem_idx, MO_UB); tcg_gen_andi_tl(t1, t0, 3); -#ifndef TARGET_WORDS_BIGENDIAN - tcg_gen_xori_tl(t1, t1, 3); -#endif + if (!cpu_is_bigendian(ctx)) { + tcg_gen_xori_tl(t1, t1, 3); + } tcg_gen_shli_tl(t1, t1, 3); tcg_gen_andi_tl(t0, t0, ~3); tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEUL); @@ -2181,9 +2181,9 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, */ tcg_gen_qemu_ld_tl(t1, t0, mem_idx, MO_UB); tcg_gen_andi_tl(t1, t0, 3); -#ifdef TARGET_WORDS_BIGENDIAN - tcg_gen_xori_tl(t1, t1, 3); -#endif + if (cpu_is_bigendian(ctx)) { + tcg_gen_xori_tl(t1, t1, 3); + } tcg_gen_shli_tl(t1, t1, 3); tcg_gen_andi_tl(t0, t0, ~3); tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEUL); @@ -4400,9 +4400,9 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, t1 = tcg_temp_new(); tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); tcg_gen_andi_tl(t1, t0, 3); -#ifndef TARGET_WORDS_BIGENDIAN - tcg_gen_xori_tl(t1, t1, 3); -#endif + if (!cpu_is_bigendian(ctx)) { + tcg_gen_xori_tl(t1, t1, 3); + } tcg_gen_shli_tl(t1, t1, 3); tcg_gen_andi_tl(t0, t0, ~3); tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL); @@ -4430,9 +4430,9 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, t1 = tcg_temp_new(); tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); tcg_gen_andi_tl(t1, t0, 3); -#ifdef TARGET_WORDS_BIGENDIAN - tcg_gen_xori_tl(t1, t1, 3); -#endif + if (cpu_is_bigendian(ctx)) { + tcg_gen_xori_tl(t1, t1, 3); + } tcg_gen_shli_tl(t1, t1, 3); tcg_gen_andi_tl(t0, t0, ~3); tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL); @@ -4462,9 +4462,9 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, t1 = tcg_temp_new(); tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); tcg_gen_andi_tl(t1, t0, 7); -#ifndef TARGET_WORDS_BIGENDIAN - tcg_gen_xori_tl(t1, t1, 7); -#endif + if (!cpu_is_bigendian(ctx)) { + tcg_gen_xori_tl(t1, t1, 7); + } tcg_gen_shli_tl(t1, t1, 3); tcg_gen_andi_tl(t0, t0, ~7); tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ); @@ -4484,9 +4484,9 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, t1 = tcg_temp_new(); tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); tcg_gen_andi_tl(t1, t0, 7); -#ifdef TARGET_WORDS_BIGENDIAN - tcg_gen_xori_tl(t1, t1, 7); -#endif + if (cpu_is_bigendian(ctx)) { + tcg_gen_xori_tl(t1, t1, 7); + } tcg_gen_shli_tl(t1, t1, 3); tcg_gen_andi_tl(t0, t0, ~7); tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ); @@ -11409,17 +11409,17 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_set_label(l1); tcg_gen_brcondi_tl(TCG_COND_NE, t0, 4, l2); tcg_temp_free(t0); -#ifdef TARGET_WORDS_BIGENDIAN - gen_load_fpr32(ctx, fp, fs); - gen_load_fpr32h(ctx, fph, ft); - gen_store_fpr32h(ctx, fp, fd); - gen_store_fpr32(ctx, fph, fd); -#else - gen_load_fpr32h(ctx, fph, fs); - gen_load_fpr32(ctx, fp, ft); - gen_store_fpr32(ctx, fph, fd); - gen_store_fpr32h(ctx, fp, fd); -#endif + if (cpu_is_bigendian(ctx)) { + gen_load_fpr32(ctx, fp, fs); + gen_load_fpr32h(ctx, fph, ft); + gen_store_fpr32h(ctx, fp, fd); + gen_store_fpr32(ctx, fph, fd); + } else { + gen_load_fpr32h(ctx, fph, fs); + gen_load_fpr32(ctx, fp, ft); + gen_store_fpr32(ctx, fph, fd); + gen_store_fpr32h(ctx, fp, fd); + } gen_set_label(l2); tcg_temp_free_i32(fp); tcg_temp_free_i32(fph); diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index 4b4fa2c207..6111493651 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -224,4 +224,9 @@ bool decode_ext_vr54xx(DisasContext *ctx, uint32_t insn); static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ { return FUNC(ctx, a, __VA_ARGS__); } +static inline bool cpu_is_bigendian(DisasContext *ctx) +{ + return extract32(ctx->CP0_Config0, CP0C0_BE, 1); +} + #endif From 56bb24e543f9ddd27c74178f3398b7a1dbb2742a Mon Sep 17 00:00:00 2001 From: Chenyi Qiang Date: Wed, 30 Jun 2021 09:20:53 +0800 Subject: [PATCH 081/493] target/i386: Remove split lock detect in Snowridge CPU model At present, there's no mechanism intelligent enough to virtualize split lock detection correctly. Remove it in Snowridge CPU model to avoid the feature exposure. Signed-off-by: Chenyi Qiang Message-Id: <20210630012053.10098-1-chenyi.qiang@intel.com> Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 34a7ce865b..aebf81d9c9 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3682,6 +3682,14 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ }, }, }, + { + .version = 4, + .note = "no split lock detect", + .props = (PropValue[]) { + { "split-lock-detect", "off" }, + { /* end of list */ }, + }, + }, { /* end of list */ }, }, }, From f429dbf8fc526a9cacf531176b28d0c65701475a Mon Sep 17 00:00:00 2001 From: Yang Zhong Date: Fri, 20 Aug 2021 13:46:11 +0800 Subject: [PATCH 082/493] i386/cpu: Remove AVX_VNNI feature from Cooperlake cpu model The AVX_VNNI feature is not in Cooperlake platform, remove it from cpu model. Signed-off-by: Yang Zhong Message-Id: <20210820054611.84303-1-yang.zhong@intel.com> Fixes: c1826ea6a052 ("i386/cpu: Expose AVX_VNNI instruction to guest") Cc: qemu-stable@nongnu.org Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index aebf81d9c9..97e250e876 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3102,7 +3102,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY | MSR_ARCH_CAP_MDS_NO | MSR_ARCH_CAP_PSCHANGE_MC_NO | MSR_ARCH_CAP_TAA_NO, .features[FEAT_7_1_EAX] = - CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_AVX512_BF16, + CPUID_7_1_EAX_AVX512_BF16, /* XSAVES is added in version 2 */ .features[FEAT_XSAVE] = CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | From b32abbb2f563ca26c0f87e848d46e1001568b7f6 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 6 Aug 2021 14:05:10 +0200 Subject: [PATCH 083/493] qapi: Fix crash on redefinition with a different condition QAPISchema._make_implicit_object_type() asserts that when an implicit object type is used multiple times, @ifcond is the same for all uses. It will be for legitimate uses, i.e. simple union branch wrapper types. A comment explains this. The assertion fails when a command or event is redefined with a different condition. The redefinition is an error, but it's flagged only later. Fixing the assertion would complicate matters further. Not worthwhile, drop it instead. We really need to get rid of simple unions. Tweak test case redefined-event to cover redefinition with a different condition. Signed-off-by: Markus Armbruster Message-Id: <20210806120510.2367124-1-armbru@redhat.com> Reviewed-by: Eric Blake --- scripts/qapi/schema.py | 22 +++++++++++----------- tests/qapi-schema/redefined-event.json | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index d1d27ff7ee..a4ce3972a4 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -997,18 +997,18 @@ class QAPISchema: name = 'q_obj_%s-%s' % (name, role) typ = self.lookup_entity(name, QAPISchemaObjectType) if typ: - # The implicit object type has multiple users. This can - # happen only for simple unions' implicit wrapper types. - # Its ifcond should be the disjunction of its user's - # ifconds. Not implemented. Instead, we always pass the - # wrapped type's ifcond, which is trivially the same for all - # users. It's also necessary for the wrapper to compile. - # But it's not tight: the disjunction need not imply it. We - # may end up compiling useless wrapper types. + # The implicit object type has multiple users. This is + # either a duplicate definition (which will be flagged + # later), or an implicit wrapper type used for multiple + # simple unions. In the latter case, ifcond should be the + # disjunction of its user's ifconds. Not implemented. + # Instead, we always pass the wrapped type's ifcond, which + # is trivially the same for all users. It's also + # necessary for the wrapper to compile. But it's not + # tight: the disjunction need not imply it. We may end up + # compiling useless wrapper types. # TODO kill simple unions or implement the disjunction - - # pylint: disable=protected-access - assert (ifcond or []) == typ._ifcond + pass else: self._def_entity(QAPISchemaObjectType( name, info, None, ifcond, None, None, members, None)) diff --git a/tests/qapi-schema/redefined-event.json b/tests/qapi-schema/redefined-event.json index 7717e91c18..09eff18412 100644 --- a/tests/qapi-schema/redefined-event.json +++ b/tests/qapi-schema/redefined-event.json @@ -1,3 +1,3 @@ # we reject duplicate events { 'event': 'EVENT_A', 'data': { 'myint': 'int' } } -{ 'event': 'EVENT_A', 'data': { 'myint': 'int' } } +{ 'event': 'EVENT_A', 'data': { 'myint': 'int' }, 'if': 'defined(FOO)' } From 3248c1aaf2dba296db98813303ccdda822708932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 4 Aug 2021 12:30:56 +0400 Subject: [PATCH 084/493] docs: update the documentation upfront about schema configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the documentation describing the changes in this series. Signed-off-by: Marc-André Lureau Reviewed-by: Stefan Hajnoczi Tested-by: John Snow Reviewed-by: Markus Armbruster Message-Id: <20210804083105.97531-2-marcandre.lureau@redhat.com> [Rebased with straightforward conflicts] Signed-off-by: Markus Armbruster --- docs/devel/qapi-code-gen.rst | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 26c62b0e7b..ced7a5ffe1 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -826,25 +826,31 @@ Configuring the schema Syntax:: COND = STRING - | [ STRING, ... ] + | { 'all: [ COND, ... ] } + | { 'any: [ COND, ... ] } + | { 'not': COND } All definitions take an optional 'if' member. Its value must be a -string or a list of strings. A string is shorthand for a list -containing just that string. The code generated for the definition -will then be guarded by #if STRING for each STRING in the COND list. +string, or an object with a single member 'all', 'any' or 'not'. + +The C code generated for the definition will then be guarded by an #if +preprocessing directive with an operand generated from that condition: + + * STRING will generate defined(STRING) + * { 'all': [COND, ...] } will generate (COND && ...) + * { 'any': [COND, ...] } will generate (COND || ...) + * { 'not': COND } will generate !COND Example: a conditional struct :: { 'struct': 'IfStruct', 'data': { 'foo': 'int' }, - 'if': ['defined(CONFIG_FOO)', 'defined(HAVE_BAR)'] } + 'if': { 'all': [ 'CONFIG_FOO', 'HAVE_BAR' ] } } gets its generated code guarded like this:: - #if defined(CONFIG_FOO) - #if defined(HAVE_BAR) + #if defined(CONFIG_FOO) && defined(HAVE_BAR) ... generated code ... - #endif /* defined(HAVE_BAR) */ - #endif /* defined(CONFIG_FOO) */ + #endif /* defined(HAVE_BAR) && defined(CONFIG_FOO) */ Individual members of complex types, commands arguments, and event-specific data can also be made conditional. This requires the @@ -855,7 +861,7 @@ member 'bar' :: { 'struct': 'IfStruct', 'data': { 'foo': 'int', - 'bar': { 'type': 'int', 'if': 'defined(IFCOND)'} } } + 'bar': { 'type': 'int', 'if': 'IFCOND'} } } A union's discriminator may not be conditional. @@ -867,7 +873,7 @@ value 'bar' :: { 'enum': 'IfEnum', 'data': [ 'foo', - { 'name' : 'bar', 'if': 'defined(IFCOND)' } ] } + { 'name' : 'bar', 'if': 'IFCOND' } ] } Likewise, features can be conditional. This requires the longhand form of FEATURE_. @@ -877,7 +883,7 @@ Example: a struct with conditional feature 'allow-negative-numbers' :: { 'struct': 'TestType', 'data': { 'number': 'int' }, 'features': [ { 'name': 'allow-negative-numbers', - 'if': 'defined(IFCOND)' } ] } + 'if': 'IFCOND' } ] } Please note that you are responsible to ensure that the C code will compile with an arbitrary combination of conditions, since the From f17539c80da3c0ebabbe75a04f5451995367ec91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 4 Aug 2021 12:30:57 +0400 Subject: [PATCH 085/493] qapi: wrap Sequence[str] in an object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical change, except for a new assertion in QAPISchemaEntity.ifcond(). Signed-off-by: Marc-André Lureau Message-Id: <20210804083105.97531-3-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [Rebased with obvious conflicts, commit message adjusted] Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 10 +++--- scripts/qapi/commands.py | 4 +-- scripts/qapi/events.py | 5 +-- scripts/qapi/gen.py | 14 ++++---- scripts/qapi/introspect.py | 30 ++++++++--------- scripts/qapi/schema.py | 60 +++++++++++++++++++++------------- scripts/qapi/types.py | 33 ++++++++++--------- scripts/qapi/visit.py | 23 ++++++------- tests/qapi-schema/test-qapi.py | 4 +-- 9 files changed, 100 insertions(+), 83 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 87c67ab23f..0eac3308b2 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -116,7 +116,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): the conditions are in literal-text and the commas are not. If with_if is False, we don't return the "(If: " and ")". """ - condlist = intersperse([nodes.literal('', c) for c in ifcond], + condlist = intersperse([nodes.literal('', c) for c in ifcond.ifcond], nodes.Text(', ')) if not with_if: return condlist @@ -139,7 +139,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): term.append(nodes.literal('', member.type.doc_type())) if member.optional: term.append(nodes.Text(' (optional)')) - if member.ifcond: + if member.ifcond.ifcond: term.extend(self._nodes_for_ifcond(member.ifcond)) return term @@ -154,7 +154,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): nodes.literal('', variants.tag_member.name), nodes.Text(' is '), nodes.literal('', '"%s"' % variant.name)] - if variant.ifcond: + if variant.ifcond.ifcond: term.extend(self._nodes_for_ifcond(variant.ifcond)) return term @@ -209,7 +209,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): dlnode = nodes.definition_list() for section in doc.args.values(): termtext = [nodes.literal('', section.member.name)] - if section.member.ifcond: + if section.member.ifcond.ifcond: termtext.extend(self._nodes_for_ifcond(section.member.ifcond)) # TODO drop fallbacks when undocumented members are outlawed if section.text: @@ -277,7 +277,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): def _nodes_for_if_section(self, ifcond): """Return list of doctree nodes for the "If" section""" nodelist = [] - if ifcond: + if ifcond.ifcond: snode = self._make_section('If') snode += nodes.paragraph( '', '', *self._nodes_for_ifcond(ifcond, with_if=False) diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 0e13d51054..3654825968 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -17,7 +17,6 @@ from typing import ( Dict, List, Optional, - Sequence, Set, ) @@ -31,6 +30,7 @@ from .gen import ( from .schema import ( QAPISchema, QAPISchemaFeature, + QAPISchemaIfCond, QAPISchemaObjectType, QAPISchemaType, ) @@ -301,7 +301,7 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) def visit_command(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], arg_type: Optional[QAPISchemaObjectType], ret_type: Optional[QAPISchemaType], diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index fee8c671e7..82475e84ec 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -12,7 +12,7 @@ This work is licensed under the terms of the GNU GPL, version 2. See the COPYING file in the top-level directory. """ -from typing import List, Optional, Sequence +from typing import List, Optional from .common import c_enum_const, c_name, mcgen from .gen import QAPISchemaModularCVisitor, build_params, ifcontext @@ -20,6 +20,7 @@ from .schema import ( QAPISchema, QAPISchemaEnumMember, QAPISchemaFeature, + QAPISchemaIfCond, QAPISchemaObjectType, ) from .source import QAPISourceInfo @@ -227,7 +228,7 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict); def visit_event(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], arg_type: Optional[QAPISchemaObjectType], boxed: bool) -> None: diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 1fa503bdbd..1c5b190276 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -18,7 +18,6 @@ from typing import ( Dict, Iterator, Optional, - Sequence, Tuple, ) @@ -32,6 +31,7 @@ from .common import ( mcgen, ) from .schema import ( + QAPISchemaIfCond, QAPISchemaModule, QAPISchemaObjectType, QAPISchemaVisitor, @@ -85,7 +85,7 @@ class QAPIGen: fp.write(text) -def _wrap_ifcond(ifcond: Sequence[str], before: str, after: str) -> str: +def _wrap_ifcond(ifcond: QAPISchemaIfCond, before: str, after: str) -> str: if before == after: return after # suppress empty #if ... #endif @@ -95,9 +95,9 @@ def _wrap_ifcond(ifcond: Sequence[str], before: str, after: str) -> str: if added[0] == '\n': out += '\n' added = added[1:] - out += gen_if(ifcond) + out += gen_if(ifcond.ifcond) out += added - out += gen_endif(ifcond) + out += gen_endif(ifcond.ifcond) return out @@ -127,9 +127,9 @@ def build_params(arg_type: Optional[QAPISchemaObjectType], class QAPIGenCCode(QAPIGen): def __init__(self, fname: str): super().__init__(fname) - self._start_if: Optional[Tuple[Sequence[str], str, str]] = None + self._start_if: Optional[Tuple[QAPISchemaIfCond, str, str]] = None - def start_if(self, ifcond: Sequence[str]) -> None: + def start_if(self, ifcond: QAPISchemaIfCond) -> None: assert self._start_if is None self._start_if = (ifcond, self._body, self._preamble) @@ -187,7 +187,7 @@ class QAPIGenH(QAPIGenC): @contextmanager -def ifcontext(ifcond: Sequence[str], *args: QAPIGenCCode) -> Iterator[None]: +def ifcontext(ifcond: QAPISchemaIfCond, *args: QAPIGenCCode) -> Iterator[None]: """ A with-statement context manager that wraps with `start_if()` / `end_if()`. diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 9a348ca2e5..db1ebbf53a 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -15,11 +15,9 @@ from typing import ( Any, Dict, Generic, - Iterable, List, Optional, Sequence, - Tuple, TypeVar, Union, ) @@ -38,6 +36,7 @@ from .schema import ( QAPISchemaEntity, QAPISchemaEnumMember, QAPISchemaFeature, + QAPISchemaIfCond, QAPISchemaObjectType, QAPISchemaObjectTypeMember, QAPISchemaType, @@ -91,11 +90,11 @@ class Annotated(Generic[_ValueT]): """ # TODO: Remove after Python 3.7 adds @dataclass: # pylint: disable=too-few-public-methods - def __init__(self, value: _ValueT, ifcond: Iterable[str], + def __init__(self, value: _ValueT, ifcond: QAPISchemaIfCond, comment: Optional[str] = None): self.value = value self.comment: Optional[str] = comment - self.ifcond: Tuple[str, ...] = tuple(ifcond) + self.ifcond = ifcond def _tree_to_qlit(obj: JSONValue, @@ -124,11 +123,11 @@ def _tree_to_qlit(obj: JSONValue, ret = '' if obj.comment: ret += indent(level) + f"/* {obj.comment} */\n" - if obj.ifcond: - ret += gen_if(obj.ifcond) + if obj.ifcond.ifcond: + ret += gen_if(obj.ifcond.ifcond) ret += _tree_to_qlit(obj.value, level) - if obj.ifcond: - ret += '\n' + gen_endif(obj.ifcond) + if obj.ifcond.ifcond: + ret += '\n' + gen_endif(obj.ifcond.ifcond) return ret ret = '' @@ -254,7 +253,7 @@ const QLitObject %(c_name)s = %(c_string)s; return [Annotated(f.name, f.ifcond) for f in features] def _gen_tree(self, name: str, mtype: str, obj: Dict[str, object], - ifcond: Sequence[str] = (), + ifcond: QAPISchemaIfCond = QAPISchemaIfCond(), features: Sequence[QAPISchemaFeature] = ()) -> None: """ Build and append a SchemaInfo object to self._trees. @@ -305,7 +304,7 @@ const QLitObject %(c_name)s = %(c_string)s; self._gen_tree(name, 'builtin', {'json-type': json_type}) def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], members: List[QAPISchemaEnumMember], prefix: Optional[str]) -> None: @@ -316,14 +315,14 @@ const QLitObject %(c_name)s = %(c_string)s; ) def visit_array_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, element_type: QAPISchemaType) -> None: element = self._use_type(element_type) self._gen_tree('[' + element + ']', 'array', {'element-type': element}, ifcond) def visit_object_type_flat(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], members: List[QAPISchemaObjectTypeMember], variants: Optional[QAPISchemaVariants]) -> None: @@ -336,7 +335,7 @@ const QLitObject %(c_name)s = %(c_string)s; self._gen_tree(name, 'object', obj, ifcond, features) def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], variants: QAPISchemaVariants) -> None: self._gen_tree( @@ -348,7 +347,7 @@ const QLitObject %(c_name)s = %(c_string)s; ) def visit_command(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], arg_type: Optional[QAPISchemaObjectType], ret_type: Optional[QAPISchemaType], gen: bool, @@ -367,7 +366,8 @@ const QLitObject %(c_name)s = %(c_string)s; self._gen_tree(name, 'command', obj, ifcond, features) def visit_event(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], features: List[QAPISchemaFeature], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], arg_type: Optional[QAPISchemaObjectType], boxed: bool) -> None: assert self._schema is not None diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index a4ce3972a4..e3beb24500 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -25,6 +25,11 @@ from .expr import check_exprs from .parser import QAPISchemaParser +class QAPISchemaIfCond: + def __init__(self, ifcond=None): + self.ifcond = ifcond or [] + + class QAPISchemaEntity: meta: Optional[str] = None @@ -42,7 +47,7 @@ class QAPISchemaEntity: # such place). self.info = info self.doc = doc - self._ifcond = ifcond or [] + self._ifcond = ifcond or QAPISchemaIfCond() self.features = features or [] self._checked = False @@ -593,7 +598,7 @@ class QAPISchemaVariants: self.info, "discriminator member '%s' of %s must not be optional" % (self._tag_name, base)) - if self.tag_member.ifcond: + if self.tag_member.ifcond.ifcond: raise QAPISemError( self.info, "discriminator member '%s' of %s must not be conditional" @@ -601,7 +606,7 @@ class QAPISchemaVariants: else: # simple union assert isinstance(self.tag_member.type, QAPISchemaEnumType) assert not self.tag_member.optional - assert self.tag_member.ifcond == [] + assert self.tag_member.ifcond.ifcond == [] if self._tag_name: # flat union # branches that are not explicitly covered get an empty type cases = {v.name for v in self.variants} @@ -646,7 +651,7 @@ class QAPISchemaMember: assert isinstance(name, str) self.name = name self.info = info - self.ifcond = ifcond or [] + self.ifcond = ifcond or QAPISchemaIfCond() self.defined_in = None def set_defined_in(self, name): @@ -968,11 +973,13 @@ class QAPISchema: def _make_features(self, features, info): if features is None: return [] - return [QAPISchemaFeature(f['name'], info, f.get('if')) + return [QAPISchemaFeature(f['name'], info, + QAPISchemaIfCond(f.get('if'))) for f in features] def _make_enum_members(self, values, info): - return [QAPISchemaEnumMember(v['name'], info, v.get('if')) + return [QAPISchemaEnumMember(v['name'], info, + QAPISchemaIfCond(v.get('if'))) for v in values] def _make_implicit_enum_type(self, name, info, ifcond, values): @@ -1018,7 +1025,7 @@ class QAPISchema: name = expr['enum'] data = expr['data'] prefix = expr.get('prefix') - ifcond = expr.get('if') + ifcond = QAPISchemaIfCond(expr.get('if')) features = self._make_features(expr.get('features'), info) self._def_entity(QAPISchemaEnumType( name, info, doc, ifcond, features, @@ -1036,7 +1043,8 @@ class QAPISchema: self._make_features(features, info)) def _make_members(self, data, info): - return [self._make_member(key, value['type'], value.get('if'), + return [self._make_member(key, value['type'], + QAPISchemaIfCond(value.get('if')), value.get('features'), info) for (key, value) in data.items()] @@ -1044,7 +1052,7 @@ class QAPISchema: name = expr['struct'] base = expr.get('base') data = expr['data'] - ifcond = expr.get('if') + ifcond = QAPISchemaIfCond(expr.get('if')) features = self._make_features(expr.get('features'), info) self._def_entity(QAPISchemaObjectType( name, info, doc, ifcond, features, base, @@ -1067,7 +1075,7 @@ class QAPISchema: name = expr['union'] data = expr['data'] base = expr.get('base') - ifcond = expr.get('if') + ifcond = QAPISchemaIfCond(expr.get('if')) features = self._make_features(expr.get('features'), info) tag_name = expr.get('discriminator') tag_member = None @@ -1076,15 +1084,19 @@ class QAPISchema: name, info, ifcond, 'base', self._make_members(base, info)) if tag_name: - variants = [self._make_variant(key, value['type'], - value.get('if'), info) - for (key, value) in data.items()] + variants = [ + self._make_variant(key, value['type'], + QAPISchemaIfCond(value.get('if')), + info) + for (key, value) in data.items()] members = [] else: - variants = [self._make_simple_variant(key, value['type'], - value.get('if'), info) - for (key, value) in data.items()] - enum = [{'name': v.name, 'if': v.ifcond} for v in variants] + variants = [ + self._make_simple_variant(key, value['type'], + QAPISchemaIfCond(value.get('if')), + info) + for (key, value) in data.items()] + enum = [{'name': v.name, 'if': v.ifcond.ifcond} for v in variants] typ = self._make_implicit_enum_type(name, info, ifcond, enum) tag_member = QAPISchemaObjectTypeMember('type', info, typ, False) members = [tag_member] @@ -1097,11 +1109,13 @@ class QAPISchema: def _def_alternate_type(self, expr, info, doc): name = expr['alternate'] data = expr['data'] - ifcond = expr.get('if') + ifcond = QAPISchemaIfCond(expr.get('if')) features = self._make_features(expr.get('features'), info) - variants = [self._make_variant(key, value['type'], value.get('if'), - info) - for (key, value) in data.items()] + variants = [ + self._make_variant(key, value['type'], + QAPISchemaIfCond(value.get('if')), + info) + for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) self._def_entity( QAPISchemaAlternateType(name, info, doc, ifcond, features, @@ -1118,7 +1132,7 @@ class QAPISchema: allow_oob = expr.get('allow-oob', False) allow_preconfig = expr.get('allow-preconfig', False) coroutine = expr.get('coroutine', False) - ifcond = expr.get('if') + ifcond = QAPISchemaIfCond(expr.get('if')) features = self._make_features(expr.get('features'), info) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( @@ -1137,7 +1151,7 @@ class QAPISchema: name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) - ifcond = expr.get('if') + ifcond = QAPISchemaIfCond(expr.get('if')) features = self._make_features(expr.get('features'), info) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 20d572a23a..3673cf0f49 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -13,7 +13,7 @@ This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. """ -from typing import List, Optional, Sequence +from typing import List, Optional from .common import ( c_enum_const, @@ -27,6 +27,7 @@ from .schema import ( QAPISchema, QAPISchemaEnumMember, QAPISchemaFeature, + QAPISchemaIfCond, QAPISchemaObjectType, QAPISchemaObjectTypeMember, QAPISchemaType, @@ -50,13 +51,13 @@ const QEnumLookup %(c_name)s_lookup = { ''', c_name=c_name(name)) for memb in members: - ret += gen_if(memb.ifcond) + ret += gen_if(memb.ifcond.ifcond) index = c_enum_const(name, memb.name, prefix) ret += mcgen(''' [%(index)s] = "%(name)s", ''', index=index, name=memb.name) - ret += gen_endif(memb.ifcond) + ret += gen_endif(memb.ifcond.ifcond) ret += mcgen(''' }, @@ -80,12 +81,12 @@ typedef enum %(c_name)s { c_name=c_name(name)) for memb in enum_members: - ret += gen_if(memb.ifcond) + ret += gen_if(memb.ifcond.ifcond) ret += mcgen(''' %(c_enum)s, ''', c_enum=c_enum_const(name, memb.name, prefix)) - ret += gen_endif(memb.ifcond) + ret += gen_endif(memb.ifcond.ifcond) ret += mcgen(''' } %(c_name)s; @@ -125,7 +126,7 @@ struct %(c_name)s { def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str: ret = '' for memb in members: - ret += gen_if(memb.ifcond) + ret += gen_if(memb.ifcond.ifcond) if memb.optional: ret += mcgen(''' bool has_%(c_name)s; @@ -135,11 +136,11 @@ def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str: %(c_type)s %(c_name)s; ''', c_type=memb.type.c_type(), c_name=c_name(memb.name)) - ret += gen_endif(memb.ifcond) + ret += gen_endif(memb.ifcond.ifcond) return ret -def gen_object(name: str, ifcond: Sequence[str], +def gen_object(name: str, ifcond: QAPISchemaIfCond, base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], variants: Optional[QAPISchemaVariants]) -> str: @@ -158,7 +159,7 @@ def gen_object(name: str, ifcond: Sequence[str], ret += mcgen(''' ''') - ret += gen_if(ifcond) + ret += gen_if(ifcond.ifcond) ret += mcgen(''' struct %(c_name)s { ''', @@ -192,7 +193,7 @@ struct %(c_name)s { ret += mcgen(''' }; ''') - ret += gen_endif(ifcond) + ret += gen_endif(ifcond.ifcond) return ret @@ -219,13 +220,13 @@ def gen_variants(variants: QAPISchemaVariants) -> str: for var in variants.variants: if var.type.name == 'q_empty': continue - ret += gen_if(var.ifcond) + ret += gen_if(var.ifcond.ifcond) ret += mcgen(''' %(c_type)s %(c_name)s; ''', c_type=var.type.c_unboxed_type(), c_name=c_name(var.name)) - ret += gen_endif(var.ifcond) + ret += gen_endif(var.ifcond.ifcond) ret += mcgen(''' } u; @@ -307,7 +308,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], members: List[QAPISchemaEnumMember], prefix: Optional[str]) -> None: @@ -318,7 +319,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): def visit_array_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, element_type: QAPISchemaType) -> None: with ifcontext(ifcond, self._genh, self._genc): self._genh.preamble_add(gen_fwd_object_or_array(name)) @@ -328,7 +329,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): def visit_object_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], @@ -351,7 +352,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], variants: QAPISchemaVariants) -> None: with ifcontext(ifcond, self._genh): diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 9e96f3c566..67721b2470 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -13,7 +13,7 @@ This work is licensed under the terms of the GNU GPL, version 2. See the COPYING file in the top-level directory. """ -from typing import List, Optional, Sequence +from typing import List, Optional from .common import ( c_enum_const, @@ -29,6 +29,7 @@ from .schema import ( QAPISchemaEnumMember, QAPISchemaEnumType, QAPISchemaFeature, + QAPISchemaIfCond, QAPISchemaObjectType, QAPISchemaObjectTypeMember, QAPISchemaType, @@ -78,7 +79,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) for memb in members: deprecated = 'deprecated' in [f.name for f in memb.features] - ret += gen_if(memb.ifcond) + ret += gen_if(memb.ifcond.ifcond) if memb.optional: ret += mcgen(''' if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) { @@ -111,7 +112,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ret += mcgen(''' } ''') - ret += gen_endif(memb.ifcond) + ret += gen_endif(memb.ifcond.ifcond) if variants: tag_member = variants.tag_member @@ -125,7 +126,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) for var in variants.variants: case_str = c_enum_const(tag_member.type.name, var.name, tag_member.type.prefix) - ret += gen_if(var.ifcond) + ret += gen_if(var.ifcond.ifcond) if var.type.name == 'q_empty': # valid variant and nothing to do ret += mcgen(''' @@ -141,7 +142,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) case=case_str, c_type=var.type.c_name(), c_name=c_name(var.name)) - ret += gen_endif(var.ifcond) + ret += gen_endif(var.ifcond.ifcond) ret += mcgen(''' default: abort(); @@ -227,7 +228,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, c_name=c_name(name)) for var in variants.variants: - ret += gen_if(var.ifcond) + ret += gen_if(var.ifcond.ifcond) ret += mcgen(''' case %(case)s: ''', @@ -253,7 +254,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, ret += mcgen(''' break; ''') - ret += gen_endif(var.ifcond) + ret += gen_endif(var.ifcond.ifcond) ret += mcgen(''' case QTYPE_NONE: @@ -352,7 +353,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], members: List[QAPISchemaEnumMember], prefix: Optional[str]) -> None: @@ -363,7 +364,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): def visit_array_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, element_type: QAPISchemaType) -> None: with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name)) @@ -372,7 +373,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): def visit_object_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], @@ -394,7 +395,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: Sequence[str], + ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], variants: QAPISchemaVariants) -> None: with ifcontext(ifcond, self._genh, self._genc): diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index f1c4deb9a5..7907b4ac3a 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -94,8 +94,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): @staticmethod def _print_if(ifcond, indent=4): - if ifcond: - print('%sif %s' % (' ' * indent, ifcond)) + if ifcond.ifcond: + print('%sif %s' % (' ' * indent, ifcond.ifcond)) @classmethod def _print_features(cls, features, indent=4): From 33aa3267bacc5a7af363c0ffa5f1bdabba54b989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 4 Aug 2021 12:30:58 +0400 Subject: [PATCH 086/493] qapi: add QAPISchemaIfCond.is_present() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20210804083105.97531-4-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 8 ++++---- scripts/qapi/introspect.py | 4 ++-- scripts/qapi/schema.py | 7 +++++-- tests/qapi-schema/test-qapi.py | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 0eac3308b2..511520f33f 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -139,7 +139,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): term.append(nodes.literal('', member.type.doc_type())) if member.optional: term.append(nodes.Text(' (optional)')) - if member.ifcond.ifcond: + if member.ifcond.is_present(): term.extend(self._nodes_for_ifcond(member.ifcond)) return term @@ -154,7 +154,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): nodes.literal('', variants.tag_member.name), nodes.Text(' is '), nodes.literal('', '"%s"' % variant.name)] - if variant.ifcond.ifcond: + if variant.ifcond.is_present(): term.extend(self._nodes_for_ifcond(variant.ifcond)) return term @@ -209,7 +209,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): dlnode = nodes.definition_list() for section in doc.args.values(): termtext = [nodes.literal('', section.member.name)] - if section.member.ifcond.ifcond: + if section.member.ifcond.is_present(): termtext.extend(self._nodes_for_ifcond(section.member.ifcond)) # TODO drop fallbacks when undocumented members are outlawed if section.text: @@ -277,7 +277,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): def _nodes_for_if_section(self, ifcond): """Return list of doctree nodes for the "If" section""" nodelist = [] - if ifcond.ifcond: + if ifcond.is_present(): snode = self._make_section('If') snode += nodes.paragraph( '', '', *self._nodes_for_ifcond(ifcond, with_if=False) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index db1ebbf53a..e23725e2f9 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -123,10 +123,10 @@ def _tree_to_qlit(obj: JSONValue, ret = '' if obj.comment: ret += indent(level) + f"/* {obj.comment} */\n" - if obj.ifcond.ifcond: + if obj.ifcond.is_present(): ret += gen_if(obj.ifcond.ifcond) ret += _tree_to_qlit(obj.value, level) - if obj.ifcond.ifcond: + if obj.ifcond.is_present(): ret += '\n' + gen_endif(obj.ifcond.ifcond) return ret diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index e3beb24500..86fcd6cbd5 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -29,6 +29,9 @@ class QAPISchemaIfCond: def __init__(self, ifcond=None): self.ifcond = ifcond or [] + def is_present(self): + return bool(self.ifcond) + class QAPISchemaEntity: meta: Optional[str] = None @@ -598,7 +601,7 @@ class QAPISchemaVariants: self.info, "discriminator member '%s' of %s must not be optional" % (self._tag_name, base)) - if self.tag_member.ifcond.ifcond: + if self.tag_member.ifcond.is_present(): raise QAPISemError( self.info, "discriminator member '%s' of %s must not be conditional" @@ -606,7 +609,7 @@ class QAPISchemaVariants: else: # simple union assert isinstance(self.tag_member.type, QAPISchemaEnumType) assert not self.tag_member.optional - assert self.tag_member.ifcond.ifcond == [] + assert not self.tag_member.ifcond.is_present() if self._tag_name: # flat union # branches that are not explicitly covered get an empty type cases = {v.name for v in self.variants} diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 7907b4ac3a..c92be2d086 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -94,7 +94,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): @staticmethod def _print_if(ifcond, indent=4): - if ifcond.ifcond: + if ifcond.is_present(): print('%sif %s' % (' ' * indent, ifcond.ifcond)) @classmethod From 6cc2e4817ff5b33d6f67e0a5f27dbd1112d1ecd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 4 Aug 2021 12:30:59 +0400 Subject: [PATCH 087/493] qapi: introduce QAPISchemaIfCond.cgen() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of building prepocessor conditions from a list of string, use the result generated from QAPISchemaIfCond.cgen() and hide the implementation details. Note: this patch introduces a minor regression, generating a redundant pair of parenthesis. This is mostly fixed in a later patch in this series ("qapi: replace if condition list with dict [..]") Signed-off-by: Marc-André Lureau Message-Id: <20210804083105.97531-5-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [Commit message tweaked] Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 35 ++++++++++++++++++++++------------- scripts/qapi/gen.py | 4 ++-- scripts/qapi/introspect.py | 4 ++-- scripts/qapi/schema.py | 5 ++++- scripts/qapi/types.py | 20 ++++++++++---------- scripts/qapi/visit.py | 12 ++++++------ 6 files changed, 46 insertions(+), 34 deletions(-) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 6ad1eeb61d..ba9fe14e4b 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -12,7 +12,12 @@ # See the COPYING file in the top-level directory. import re -from typing import Match, Optional, Sequence +from typing import ( + List, + Match, + Optional, + Union, +) #: Magic string that gets removed along with all space to its right. @@ -194,22 +199,26 @@ def guardend(name: str) -> str: name=c_fname(name).upper()) -def gen_if(ifcond: Sequence[str]) -> str: - ret = '' - for ifc in ifcond: - ret += mcgen(''' +def cgen_ifcond(ifcond: Union[str, List[str]]) -> str: + if not ifcond: + return '' + return '(' + ') && ('.join(ifcond) + ')' + + +def gen_if(cond: str) -> str: + if not cond: + return '' + return mcgen(''' #if %(cond)s -''', cond=ifc) - return ret +''', cond=cond) -def gen_endif(ifcond: Sequence[str]) -> str: - ret = '' - for ifc in reversed(ifcond): - ret += mcgen(''' +def gen_endif(cond: str) -> str: + if not cond: + return '' + return mcgen(''' #endif /* %(cond)s */ -''', cond=ifc) - return ret +''', cond=cond) def must_match(pattern: str, string: str) -> Match[str]: diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 1c5b190276..51a597a025 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -95,9 +95,9 @@ def _wrap_ifcond(ifcond: QAPISchemaIfCond, before: str, after: str) -> str: if added[0] == '\n': out += '\n' added = added[1:] - out += gen_if(ifcond.ifcond) + out += gen_if(ifcond.cgen()) out += added - out += gen_endif(ifcond.ifcond) + out += gen_endif(ifcond.cgen()) return out diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index e23725e2f9..bd4233ecee 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -124,10 +124,10 @@ def _tree_to_qlit(obj: JSONValue, if obj.comment: ret += indent(level) + f"/* {obj.comment} */\n" if obj.ifcond.is_present(): - ret += gen_if(obj.ifcond.ifcond) + ret += gen_if(obj.ifcond.cgen()) ret += _tree_to_qlit(obj.value, level) if obj.ifcond.is_present(): - ret += '\n' + gen_endif(obj.ifcond.ifcond) + ret += '\n' + gen_endif(obj.ifcond.cgen()) return ret ret = '' diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 86fcd6cbd5..4ea7e88846 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -19,7 +19,7 @@ import os import re from typing import Optional -from .common import POINTER_SUFFIX, c_name +from .common import POINTER_SUFFIX, c_name, cgen_ifcond from .error import QAPIError, QAPISemError, QAPISourceError from .expr import check_exprs from .parser import QAPISchemaParser @@ -29,6 +29,9 @@ class QAPISchemaIfCond: def __init__(self, ifcond=None): self.ifcond = ifcond or [] + def cgen(self): + return cgen_ifcond(self.ifcond) + def is_present(self): return bool(self.ifcond) diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 3673cf0f49..db9ff95bd1 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -51,13 +51,13 @@ const QEnumLookup %(c_name)s_lookup = { ''', c_name=c_name(name)) for memb in members: - ret += gen_if(memb.ifcond.ifcond) + ret += gen_if(memb.ifcond.cgen()) index = c_enum_const(name, memb.name, prefix) ret += mcgen(''' [%(index)s] = "%(name)s", ''', index=index, name=memb.name) - ret += gen_endif(memb.ifcond.ifcond) + ret += gen_endif(memb.ifcond.cgen()) ret += mcgen(''' }, @@ -81,12 +81,12 @@ typedef enum %(c_name)s { c_name=c_name(name)) for memb in enum_members: - ret += gen_if(memb.ifcond.ifcond) + ret += gen_if(memb.ifcond.cgen()) ret += mcgen(''' %(c_enum)s, ''', c_enum=c_enum_const(name, memb.name, prefix)) - ret += gen_endif(memb.ifcond.ifcond) + ret += gen_endif(memb.ifcond.cgen()) ret += mcgen(''' } %(c_name)s; @@ -126,7 +126,7 @@ struct %(c_name)s { def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str: ret = '' for memb in members: - ret += gen_if(memb.ifcond.ifcond) + ret += gen_if(memb.ifcond.cgen()) if memb.optional: ret += mcgen(''' bool has_%(c_name)s; @@ -136,7 +136,7 @@ def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str: %(c_type)s %(c_name)s; ''', c_type=memb.type.c_type(), c_name=c_name(memb.name)) - ret += gen_endif(memb.ifcond.ifcond) + ret += gen_endif(memb.ifcond.cgen()) return ret @@ -159,7 +159,7 @@ def gen_object(name: str, ifcond: QAPISchemaIfCond, ret += mcgen(''' ''') - ret += gen_if(ifcond.ifcond) + ret += gen_if(ifcond.cgen()) ret += mcgen(''' struct %(c_name)s { ''', @@ -193,7 +193,7 @@ struct %(c_name)s { ret += mcgen(''' }; ''') - ret += gen_endif(ifcond.ifcond) + ret += gen_endif(ifcond.cgen()) return ret @@ -220,13 +220,13 @@ def gen_variants(variants: QAPISchemaVariants) -> str: for var in variants.variants: if var.type.name == 'q_empty': continue - ret += gen_if(var.ifcond.ifcond) + ret += gen_if(var.ifcond.cgen()) ret += mcgen(''' %(c_type)s %(c_name)s; ''', c_type=var.type.c_unboxed_type(), c_name=c_name(var.name)) - ret += gen_endif(var.ifcond.ifcond) + ret += gen_endif(var.ifcond.cgen()) ret += mcgen(''' } u; diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 67721b2470..56ea516399 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -79,7 +79,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) for memb in members: deprecated = 'deprecated' in [f.name for f in memb.features] - ret += gen_if(memb.ifcond.ifcond) + ret += gen_if(memb.ifcond.cgen()) if memb.optional: ret += mcgen(''' if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) { @@ -112,7 +112,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ret += mcgen(''' } ''') - ret += gen_endif(memb.ifcond.ifcond) + ret += gen_endif(memb.ifcond.cgen()) if variants: tag_member = variants.tag_member @@ -126,7 +126,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) for var in variants.variants: case_str = c_enum_const(tag_member.type.name, var.name, tag_member.type.prefix) - ret += gen_if(var.ifcond.ifcond) + ret += gen_if(var.ifcond.cgen()) if var.type.name == 'q_empty': # valid variant and nothing to do ret += mcgen(''' @@ -142,7 +142,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) case=case_str, c_type=var.type.c_name(), c_name=c_name(var.name)) - ret += gen_endif(var.ifcond.ifcond) + ret += gen_endif(var.ifcond.cgen()) ret += mcgen(''' default: abort(); @@ -228,7 +228,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, c_name=c_name(name)) for var in variants.variants: - ret += gen_if(var.ifcond.ifcond) + ret += gen_if(var.ifcond.cgen()) ret += mcgen(''' case %(case)s: ''', @@ -254,7 +254,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, ret += mcgen(''' break; ''') - ret += gen_endif(var.ifcond.ifcond) + ret += gen_endif(var.ifcond.cgen()) ret += mcgen(''' case QTYPE_NONE: From d806f89f87152def5f422e946c84948831615236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 4 Aug 2021 12:31:00 +0400 Subject: [PATCH 088/493] qapidoc: introduce QAPISchemaIfCond.docgen() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of building the condition documentation from a list of string, use the result generated from QAPISchemaIfCond.docgen(). This changes the generated documentation from: - COND1, COND2... (where COND1, COND2 are Literal nodes, and ',' is Text) to: - COND1 and COND2 (the whole string as a Literal node) This will allow us to generate more complex conditions in the following patches, such as "(COND1 and COND2) or COND3". Adding back the differentiated formatting is left to the wish list. Signed-off-by: Marc-André Lureau Message-Id: <20210804083105.97531-6-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [TODO comment added] Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 14 ++++++++------ scripts/qapi/common.py | 7 +++++++ scripts/qapi/schema.py | 10 +++++++++- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 511520f33f..d791b59492 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -112,17 +112,19 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): def _nodes_for_ifcond(self, ifcond, with_if=True): """Return list of Text, literal nodes for the ifcond - Return a list which gives text like ' (If: cond1, cond2, cond3)', where - the conditions are in literal-text and the commas are not. + Return a list which gives text like ' (If: condition)'. If with_if is False, we don't return the "(If: " and ")". """ - condlist = intersperse([nodes.literal('', c) for c in ifcond.ifcond], - nodes.Text(', ')) + + doc = ifcond.docgen() + if not doc: + return [] + doc = nodes.literal('', doc) if not with_if: - return condlist + return [doc] nodelist = [nodes.Text(' ('), nodes.strong('', 'If: ')] - nodelist.extend(condlist) + nodelist.append(doc) nodelist.append(nodes.Text(')')) return nodelist diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index ba9fe14e4b..ddc54e4368 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -205,6 +205,13 @@ def cgen_ifcond(ifcond: Union[str, List[str]]) -> str: return '(' + ') && ('.join(ifcond) + ')' +def docgen_ifcond(ifcond: Union[str, List[str]]) -> str: + # TODO Doc generated for conditions needs polish + if not ifcond: + return '' + return ' and '.join(ifcond) + + def gen_if(cond: str) -> str: if not cond: return '' diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 4ea7e88846..a9345af7b7 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -19,7 +19,12 @@ import os import re from typing import Optional -from .common import POINTER_SUFFIX, c_name, cgen_ifcond +from .common import ( + POINTER_SUFFIX, + c_name, + cgen_ifcond, + docgen_ifcond, +) from .error import QAPIError, QAPISemError, QAPISourceError from .expr import check_exprs from .parser import QAPISchemaParser @@ -32,6 +37,9 @@ class QAPISchemaIfCond: def cgen(self): return cgen_ifcond(self.ifcond) + def docgen(self): + return docgen_ifcond(self.ifcond) + def is_present(self): return bool(self.ifcond) From 5d83b9a130690f879d5f33e991beabe69cb88bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 4 Aug 2021 12:31:01 +0400 Subject: [PATCH 089/493] qapi: replace if condition list with dict {'all': [...]} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the simple list sugar form with a recursive structure that will accept other operators in the following commits (all, any or not). Signed-off-by: Marc-André Lureau Message-Id: <20210804083105.97531-7-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [Accidental code motion undone. Degenerate :forms: comment dropped. Helper _check_if() moved. Error messages tweaked. ui.json updated. Accidental changes to qapi-schema-test.json dropped.] Signed-off-by: Markus Armbruster --- qapi/ui.json | 6 +- scripts/qapi/common.py | 23 +++++-- scripts/qapi/expr.py | 64 +++++++++++-------- scripts/qapi/schema.py | 2 +- tests/qapi-schema/bad-if-all.err | 2 + tests/qapi-schema/bad-if-all.json | 3 + tests/qapi-schema/bad-if-all.out | 0 tests/qapi-schema/bad-if-empty-list.json | 2 +- tests/qapi-schema/bad-if-key.err | 3 + tests/qapi-schema/bad-if-key.json | 3 + tests/qapi-schema/bad-if-key.out | 0 tests/qapi-schema/bad-if-keys.err | 2 + tests/qapi-schema/bad-if-keys.json | 3 + tests/qapi-schema/bad-if-keys.out | 0 tests/qapi-schema/bad-if-list.json | 2 +- tests/qapi-schema/bad-if.err | 2 +- tests/qapi-schema/bad-if.json | 2 +- tests/qapi-schema/doc-good.json | 3 +- tests/qapi-schema/doc-good.out | 13 ++-- tests/qapi-schema/doc-good.txt | 6 ++ tests/qapi-schema/enum-if-invalid.err | 3 +- tests/qapi-schema/features-if-invalid.err | 2 +- tests/qapi-schema/meson.build | 3 + tests/qapi-schema/qapi-schema-test.json | 20 +++--- tests/qapi-schema/qapi-schema-test.out | 58 ++++++++--------- .../qapi-schema/struct-member-if-invalid.err | 2 +- .../qapi-schema/union-branch-if-invalid.json | 2 +- 27 files changed, 143 insertions(+), 88 deletions(-) create mode 100644 tests/qapi-schema/bad-if-all.err create mode 100644 tests/qapi-schema/bad-if-all.json create mode 100644 tests/qapi-schema/bad-if-all.out create mode 100644 tests/qapi-schema/bad-if-key.err create mode 100644 tests/qapi-schema/bad-if-key.json create mode 100644 tests/qapi-schema/bad-if-key.out create mode 100644 tests/qapi-schema/bad-if-keys.err create mode 100644 tests/qapi-schema/bad-if-keys.json create mode 100644 tests/qapi-schema/bad-if-keys.out diff --git a/qapi/ui.json b/qapi/ui.json index fd9677d48e..aed2bec4ab 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1136,7 +1136,8 @@ { 'name': 'gtk', 'if': 'defined(CONFIG_GTK)' }, { 'name': 'sdl', 'if': 'defined(CONFIG_SDL)' }, { 'name': 'egl-headless', - 'if': 'defined(CONFIG_OPENGL) && defined(CONFIG_GBM)' }, + 'if': { 'all': [ 'defined(CONFIG_OPENGL)', + 'defined(CONFIG_GBM)' ] } }, { 'name': 'curses', 'if': 'defined(CONFIG_CURSES)' }, { 'name': 'cocoa', 'if': 'defined(CONFIG_COCOA)' }, { 'name': 'spice-app', 'if': 'defined(CONFIG_SPICE)'} ] } @@ -1167,7 +1168,8 @@ 'gtk': { 'type': 'DisplayGTK', 'if': 'defined(CONFIG_GTK)' }, 'curses': { 'type': 'DisplayCurses', 'if': 'defined(CONFIG_CURSES)' }, 'egl-headless': { 'type': 'DisplayEGLHeadless', - 'if': 'defined(CONFIG_OPENGL) && defined(CONFIG_GBM)' } + 'if': { 'all': [ 'defined(CONFIG_OPENGL)', + 'defined(CONFIG_GBM)' ] } } } } diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index ddc54e4368..3d7272a702 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -13,7 +13,8 @@ import re from typing import ( - List, + Any, + Dict, Match, Optional, Union, @@ -199,17 +200,29 @@ def guardend(name: str) -> str: name=c_fname(name).upper()) -def cgen_ifcond(ifcond: Union[str, List[str]]) -> str: +def cgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str: if not ifcond: return '' - return '(' + ') && ('.join(ifcond) + ')' + if isinstance(ifcond, str): + return ifcond + + oper, operands = next(iter(ifcond.items())) + oper = {'all': '&&'}[oper] + operands = [cgen_ifcond(o) for o in operands] + return '(' + (') ' + oper + ' (').join(operands) + ')' -def docgen_ifcond(ifcond: Union[str, List[str]]) -> str: +def docgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str: # TODO Doc generated for conditions needs polish if not ifcond: return '' - return ' and '.join(ifcond) + if isinstance(ifcond, str): + return ifcond + + oper, operands = next(iter(ifcond.items())) + oper = {'all': ' and '}[oper] + operands = [docgen_ifcond(o) for o in operands] + return '(' + oper.join(operands) + ')' def gen_if(cond: str) -> str: diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index cf98923fa6..d7a34655a7 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -259,14 +259,9 @@ def check_flags(expr: _JSONObject, info: QAPISourceInfo) -> None: def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None: """ - Normalize and validate the ``if`` member of an object. + Validate the ``if`` member of an object. - The ``if`` member may be either a ``str`` or a ``List[str]``. - A ``str`` value will be normalized to ``List[str]``. - - :forms: - :sugared: ``Union[str, List[str]]`` - :canonical: ``List[str]`` + The ``if`` member may be either a ``str`` or a dict. :param expr: The expression containing the ``if`` member to validate. :param info: QAPI schema source file information. @@ -275,31 +270,46 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None: :raise QAPISemError: When the "if" member fails validation, or when there are no non-empty conditions. - :return: None, ``expr`` is normalized in-place as needed. + :return: None """ + + def _check_if(cond: Union[str, object]) -> None: + if isinstance(cond, str): + if not cond.strip(): + raise QAPISemError( + info, + "'if' condition '%s' of %s makes no sense" + % (cond, source)) + return + + if not isinstance(cond, dict): + raise QAPISemError( + info, + "'if' condition of %s must be a string or an object" % source) + if len(cond) != 1: + raise QAPISemError( + info, + "'if' condition dict of %s must have one key: " + "'all'" % source) + check_keys(cond, info, "'if' condition", [], + ["all"]) + + oper, operands = next(iter(cond.items())) + if not operands: + raise QAPISemError( + info, "'if' condition [] of %s is useless" % source) + + if oper in ("all") and not isinstance(operands, list): + raise QAPISemError( + info, "'%s' condition of %s must be an array" % (oper, source)) + for operand in operands: + _check_if(operand) + ifcond = expr.get('if') if ifcond is None: return - if isinstance(ifcond, list): - if not ifcond: - raise QAPISemError( - info, "'if' condition [] of %s is useless" % source) - else: - # Normalize to a list - ifcond = expr['if'] = [ifcond] - - for elt in ifcond: - if not isinstance(elt, str): - raise QAPISemError( - info, - "'if' condition of %s must be a string or a list of strings" - % source) - if not elt.strip(): - raise QAPISemError( - info, - "'if' condition '%s' of %s makes no sense" - % (elt, source)) + _check_if(ifcond) def normalize_members(members: object) -> None: diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index a9345af7b7..229d24fce9 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -32,7 +32,7 @@ from .parser import QAPISchemaParser class QAPISchemaIfCond: def __init__(self, ifcond=None): - self.ifcond = ifcond or [] + self.ifcond = ifcond or {} def cgen(self): return cgen_ifcond(self.ifcond) diff --git a/tests/qapi-schema/bad-if-all.err b/tests/qapi-schema/bad-if-all.err new file mode 100644 index 0000000000..a04f6e7043 --- /dev/null +++ b/tests/qapi-schema/bad-if-all.err @@ -0,0 +1,2 @@ +bad-if-all.json: In struct 'TestIfStruct': +bad-if-all.json:2: 'all' condition of struct must be an array diff --git a/tests/qapi-schema/bad-if-all.json b/tests/qapi-schema/bad-if-all.json new file mode 100644 index 0000000000..44837d3981 --- /dev/null +++ b/tests/qapi-schema/bad-if-all.json @@ -0,0 +1,3 @@ +# check 'if all' is not a list +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': { 'all': 'ALL' } } diff --git a/tests/qapi-schema/bad-if-all.out b/tests/qapi-schema/bad-if-all.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if-empty-list.json b/tests/qapi-schema/bad-if-empty-list.json index 94f2eb8670..b62b5671df 100644 --- a/tests/qapi-schema/bad-if-empty-list.json +++ b/tests/qapi-schema/bad-if-empty-list.json @@ -1,3 +1,3 @@ # check empty 'if' list { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, - 'if': [] } + 'if': { 'all': [] } } diff --git a/tests/qapi-schema/bad-if-key.err b/tests/qapi-schema/bad-if-key.err new file mode 100644 index 0000000000..725d5abae5 --- /dev/null +++ b/tests/qapi-schema/bad-if-key.err @@ -0,0 +1,3 @@ +bad-if-key.json: In struct 'TestIfStruct': +bad-if-key.json:2: 'if' condition has unknown key 'value' +Valid keys are 'all'. diff --git a/tests/qapi-schema/bad-if-key.json b/tests/qapi-schema/bad-if-key.json new file mode 100644 index 0000000000..64c74c13f2 --- /dev/null +++ b/tests/qapi-schema/bad-if-key.json @@ -0,0 +1,3 @@ +# check unknown 'if' dict key +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': { 'value': 'defined(TEST_IF_STRUCT)' } } diff --git a/tests/qapi-schema/bad-if-key.out b/tests/qapi-schema/bad-if-key.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if-keys.err b/tests/qapi-schema/bad-if-keys.err new file mode 100644 index 0000000000..b072db9a6f --- /dev/null +++ b/tests/qapi-schema/bad-if-keys.err @@ -0,0 +1,2 @@ +bad-if-keys.json: In struct 'TestIfStruct': +bad-if-keys.json:2: 'if' condition dict of struct must have one key: 'all' diff --git a/tests/qapi-schema/bad-if-keys.json b/tests/qapi-schema/bad-if-keys.json new file mode 100644 index 0000000000..9e2f39ae21 --- /dev/null +++ b/tests/qapi-schema/bad-if-keys.json @@ -0,0 +1,3 @@ +# check multiple 'if' keys +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': { 'any': ['ANY'], 'all': ['ALL'] } } diff --git a/tests/qapi-schema/bad-if-keys.out b/tests/qapi-schema/bad-if-keys.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if-list.json b/tests/qapi-schema/bad-if-list.json index ea3d95bb6b..1fefef16a7 100644 --- a/tests/qapi-schema/bad-if-list.json +++ b/tests/qapi-schema/bad-if-list.json @@ -1,3 +1,3 @@ # check invalid 'if' content { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, - 'if': ['foo', ' '] } + 'if': { 'all': ['foo', ' '] } } diff --git a/tests/qapi-schema/bad-if.err b/tests/qapi-schema/bad-if.err index f83dee65da..ec373b213f 100644 --- a/tests/qapi-schema/bad-if.err +++ b/tests/qapi-schema/bad-if.err @@ -1,2 +1,2 @@ bad-if.json: In struct 'TestIfStruct': -bad-if.json:2: 'if' condition of struct must be a string or a list of strings +bad-if.json:2: 'if' condition of struct must be a string or an object diff --git a/tests/qapi-schema/bad-if.json b/tests/qapi-schema/bad-if.json index 3edd1a0bf2..fdc0c87bb3 100644 --- a/tests/qapi-schema/bad-if.json +++ b/tests/qapi-schema/bad-if.json @@ -1,3 +1,3 @@ # check invalid 'if' type { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, - 'if': { 'value': 'defined(TEST_IF_STRUCT)' } } + 'if': ['defined(TEST_IF_STRUCT)'] } diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 423ea23e07..25b1053e8a 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -70,7 +70,8 @@ # @base1: # the first member ## -{ 'struct': 'Base', 'data': { 'base1': 'Enum' } } +{ 'struct': 'Base', 'data': { 'base1': 'Enum' }, + 'if': { 'all': ['IFALL1', 'IFALL2'] } } ## # @Variant1: diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 8f54ceff2e..689d084f3a 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -12,15 +12,16 @@ enum QType module doc-good.json enum Enum member one - if ['defined(IFONE)'] + if defined(IFONE) member two - if ['defined(IFCOND)'] + if defined(IFCOND) feature enum-feat object Base member base1: Enum optional=False + if OrderedDict([('all', ['IFALL1', 'IFALL2'])]) object Variant1 member var1: str optional=False - if ['defined(IFSTR)'] + if defined(IFSTR) feature member-feat feature variant1-feat object Variant2 @@ -29,7 +30,7 @@ object Object tag base1 case one: Variant1 case two: Variant2 - if ['IFTWO'] + if IFTWO feature union-feat1 object q_obj_Variant1-wrapper member data: Variant1 optional=False @@ -38,13 +39,13 @@ object q_obj_Variant2-wrapper enum SugaredUnionKind member one member two - if ['IFTWO'] + if IFTWO object SugaredUnion member type: SugaredUnionKind optional=False tag type case one: q_obj_Variant1-wrapper case two: q_obj_Variant2-wrapper - if ['IFTWO'] + if IFTWO feature union-feat2 alternate Alternate tag type diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index 726727af74..4490108cb7 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -76,6 +76,12 @@ Members the first member +If +~~ + +"(IFALL1 and IFALL2)" + + "Variant1" (Object) ------------------- diff --git a/tests/qapi-schema/enum-if-invalid.err b/tests/qapi-schema/enum-if-invalid.err index 0556dc967b..df305cd79f 100644 --- a/tests/qapi-schema/enum-if-invalid.err +++ b/tests/qapi-schema/enum-if-invalid.err @@ -1,2 +1,3 @@ enum-if-invalid.json: In enum 'TestIfEnum': -enum-if-invalid.json:2: 'if' condition of 'data' member 'bar' must be a string or a list of strings +enum-if-invalid.json:2: 'if' condition has unknown key 'val' +Valid keys are 'all'. diff --git a/tests/qapi-schema/features-if-invalid.err b/tests/qapi-schema/features-if-invalid.err index f63b89535e..0ce7b6fcdf 100644 --- a/tests/qapi-schema/features-if-invalid.err +++ b/tests/qapi-schema/features-if-invalid.err @@ -1,2 +1,2 @@ features-if-invalid.json: In struct 'Stru': -features-if-invalid.json:2: 'if' condition of 'features' member 'f' must be a string or a list of strings +features-if-invalid.json:2: 'if' condition of 'features' member 'f' must be a string or an object diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index b8de58116a..4697c070bc 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -37,8 +37,11 @@ schemas = [ 'bad-data.json', 'bad-ident.json', 'bad-if.json', + 'bad-if-all.json', 'bad-if-empty.json', 'bad-if-empty-list.json', + 'bad-if-key.json', + 'bad-if-keys.json', 'bad-if-list.json', 'bad-type-bool.json', 'bad-type-dict.json', diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 84b9d41f15..e85a71c0f7 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -232,7 +232,7 @@ { 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct', 'bar': { 'type': 'str', 'if': 'defined(TEST_IF_UNION_BAR)'} }, - 'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' } + 'if': { 'all': ['defined(TEST_IF_UNION)', 'defined(TEST_IF_STRUCT)'] } } { 'command': 'test-if-union-cmd', 'data': { 'union-cmd-arg': 'TestIfUnion' }, @@ -241,25 +241,25 @@ { 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': { 'type': 'TestStruct', 'if': 'defined(TEST_IF_ALT_BAR)'} }, - 'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' } + 'if': { 'all': ['defined(TEST_IF_ALT)', 'defined(TEST_IF_STRUCT)'] } } { 'command': 'test-if-alternate-cmd', 'data': { 'alt-cmd-arg': 'TestIfAlternate' }, - 'if': 'defined(TEST_IF_ALT)' } + 'if': { 'all': ['defined(TEST_IF_ALT)'] } } { 'command': 'test-if-cmd', 'data': { 'foo': 'TestIfStruct', 'bar': { 'type': 'TestIfEnum', 'if': 'defined(TEST_IF_CMD_BAR)' } }, 'returns': 'UserDefThree', - 'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] } + 'if': { 'all': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] } } { 'command': 'test-cmd-return-def-three', 'returns': 'UserDefThree' } { 'event': 'TEST_IF_EVENT', 'data': { 'foo': 'TestIfStruct', 'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)' } }, - 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' } + 'if': { 'all': ['defined(TEST_IF_EVT)', 'defined(TEST_IF_STRUCT)'] } } # test 'features' @@ -288,8 +288,9 @@ { 'name': 'feature2', 'if': 'defined(TEST_IF_FEATURE_2)'} ] } { 'struct': 'CondFeatureStruct3', 'data': { 'foo': 'int' }, - 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', - 'defined(TEST_IF_COND_2)'] } ] } + 'features': [ { 'name': 'feature1', + 'if': { 'all': [ 'defined(TEST_IF_COND_1)', + 'defined(TEST_IF_COND_2)'] } } ] } { 'enum': 'FeatureEnum1', 'data': [ 'eins', 'zwei', 'drei' ], @@ -328,8 +329,9 @@ 'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'}, { 'name': 'feature2', 'if': 'defined(TEST_IF_FEATURE_2)'} ] } { 'command': 'test-command-cond-features3', - 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', - 'defined(TEST_IF_COND_2)'] } ] } + 'features': [ { 'name': 'feature1', + 'if': { 'all': [ 'defined(TEST_IF_COND_1)', + 'defined(TEST_IF_COND_2)'] } } ] } { 'event': 'TEST_EVENT_FEATURES0', 'data': 'FeatureStruct1' } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index e0b8a5f0b6..e74756cfd8 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -298,65 +298,65 @@ command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Unio object TestIfStruct member foo: int optional=False member bar: int optional=False - if ['defined(TEST_IF_STRUCT_BAR)'] - if ['defined(TEST_IF_STRUCT)'] + if defined(TEST_IF_STRUCT_BAR) + if defined(TEST_IF_STRUCT) enum TestIfEnum member foo member bar - if ['defined(TEST_IF_ENUM_BAR)'] - if ['defined(TEST_IF_ENUM)'] + if defined(TEST_IF_ENUM_BAR) + if defined(TEST_IF_ENUM) object q_obj_TestStruct-wrapper member data: TestStruct optional=False enum TestIfUnionKind member foo member bar - if ['defined(TEST_IF_UNION_BAR)'] - if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)'] + if defined(TEST_IF_UNION_BAR) + if OrderedDict([('all', ['defined(TEST_IF_UNION)', 'defined(TEST_IF_STRUCT)'])]) object TestIfUnion member type: TestIfUnionKind optional=False tag type case foo: q_obj_TestStruct-wrapper case bar: q_obj_str-wrapper - if ['defined(TEST_IF_UNION_BAR)'] - if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)'] + if defined(TEST_IF_UNION_BAR) + if OrderedDict([('all', ['defined(TEST_IF_UNION)', 'defined(TEST_IF_STRUCT)'])]) object q_obj_test-if-union-cmd-arg member union-cmd-arg: TestIfUnion optional=False - if ['defined(TEST_IF_UNION)'] + if defined(TEST_IF_UNION) command test-if-union-cmd q_obj_test-if-union-cmd-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False - if ['defined(TEST_IF_UNION)'] + if defined(TEST_IF_UNION) alternate TestIfAlternate tag type case foo: int case bar: TestStruct - if ['defined(TEST_IF_ALT_BAR)'] - if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)'] + if defined(TEST_IF_ALT_BAR) + if OrderedDict([('all', ['defined(TEST_IF_ALT)', 'defined(TEST_IF_STRUCT)'])]) object q_obj_test-if-alternate-cmd-arg member alt-cmd-arg: TestIfAlternate optional=False - if ['defined(TEST_IF_ALT)'] + if OrderedDict([('all', ['defined(TEST_IF_ALT)'])]) command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False - if ['defined(TEST_IF_ALT)'] + if OrderedDict([('all', ['defined(TEST_IF_ALT)'])]) object q_obj_test-if-cmd-arg member foo: TestIfStruct optional=False member bar: TestIfEnum optional=False - if ['defined(TEST_IF_CMD_BAR)'] - if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] + if defined(TEST_IF_CMD_BAR) + if OrderedDict([('all', ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'])]) command test-if-cmd q_obj_test-if-cmd-arg -> UserDefThree gen=True success_response=True boxed=False oob=False preconfig=False - if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] + if OrderedDict([('all', ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'])]) command test-cmd-return-def-three None -> UserDefThree gen=True success_response=True boxed=False oob=False preconfig=False array TestIfEnumList TestIfEnum - if ['defined(TEST_IF_ENUM)'] + if defined(TEST_IF_ENUM) object q_obj_TEST_IF_EVENT-arg member foo: TestIfStruct optional=False member bar: TestIfEnumList optional=False - if ['defined(TEST_IF_EVT_BAR)'] - if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'] + if defined(TEST_IF_EVT_BAR) + if OrderedDict([('all', ['defined(TEST_IF_EVT)', 'defined(TEST_IF_STRUCT)'])]) event TEST_IF_EVENT q_obj_TEST_IF_EVENT-arg boxed=False - if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'] + if OrderedDict([('all', ['defined(TEST_IF_EVT)', 'defined(TEST_IF_STRUCT)'])]) object FeatureStruct0 member foo: int optional=False object FeatureStruct1 @@ -379,17 +379,17 @@ object FeatureStruct4 object CondFeatureStruct1 member foo: int optional=False feature feature1 - if ['defined(TEST_IF_FEATURE_1)'] + if defined(TEST_IF_FEATURE_1) object CondFeatureStruct2 member foo: int optional=False feature feature1 - if ['defined(TEST_IF_FEATURE_1)'] + if defined(TEST_IF_FEATURE_1) feature feature2 - if ['defined(TEST_IF_FEATURE_2)'] + if defined(TEST_IF_FEATURE_2) object CondFeatureStruct3 member foo: int optional=False feature feature1 - if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] + if OrderedDict([('all', ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'])]) enum FeatureEnum1 member eins member zwei @@ -429,17 +429,17 @@ command test-command-features3 None -> None command test-command-cond-features1 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 - if ['defined(TEST_IF_FEATURE_1)'] + if defined(TEST_IF_FEATURE_1) command test-command-cond-features2 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 - if ['defined(TEST_IF_FEATURE_1)'] + if defined(TEST_IF_FEATURE_1) feature feature2 - if ['defined(TEST_IF_FEATURE_2)'] + if defined(TEST_IF_FEATURE_2) command test-command-cond-features3 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 - if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] + if OrderedDict([('all', ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'])]) event TEST_EVENT_FEATURES0 FeatureStruct1 boxed=False event TEST_EVENT_FEATURES1 None diff --git a/tests/qapi-schema/struct-member-if-invalid.err b/tests/qapi-schema/struct-member-if-invalid.err index 42e7fdae3c..5ee08afa41 100644 --- a/tests/qapi-schema/struct-member-if-invalid.err +++ b/tests/qapi-schema/struct-member-if-invalid.err @@ -1,2 +1,2 @@ struct-member-if-invalid.json: In struct 'Stru': -struct-member-if-invalid.json:2: 'if' condition of 'data' member 'member' must be a string or a list of strings +struct-member-if-invalid.json:2: 'if' condition of 'data' member 'member' must be a string or an object diff --git a/tests/qapi-schema/union-branch-if-invalid.json b/tests/qapi-schema/union-branch-if-invalid.json index 46d4239af6..c41633856f 100644 --- a/tests/qapi-schema/union-branch-if-invalid.json +++ b/tests/qapi-schema/union-branch-if-invalid.json @@ -3,4 +3,4 @@ { 'struct': 'Stru', 'data': { 'member': 'str' } } { 'union': 'Uni', 'base': { 'tag': 'Branches' }, 'discriminator': 'tag', - 'data': { 'branch1': { 'type': 'Stru', 'if': [''] } } } + 'data': { 'branch1': { 'type': 'Stru', 'if': { 'all': [''] } } } } From 3ad64edfad2fa404e866c01f6d427ed4fe4f4f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 4 Aug 2021 12:31:02 +0400 Subject: [PATCH 090/493] qapi: add 'any' condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Message-Id: <20210804083105.97531-8-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 4 ++-- scripts/qapi/expr.py | 6 +++--- tests/qapi-schema/bad-if-key.err | 2 +- tests/qapi-schema/bad-if-keys.err | 2 +- tests/qapi-schema/doc-good.json | 4 +++- tests/qapi-schema/doc-good.out | 2 +- tests/qapi-schema/doc-good.txt | 3 ++- tests/qapi-schema/enum-if-invalid.err | 2 +- tests/qapi-schema/qapi-schema-test.json | 8 +++++++- tests/qapi-schema/qapi-schema-test.out | 5 +++++ tests/unit/test-qmp-cmds.c | 1 + 11 files changed, 27 insertions(+), 12 deletions(-) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 3d7272a702..63a2e502fb 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -207,7 +207,7 @@ def cgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str: return ifcond oper, operands = next(iter(ifcond.items())) - oper = {'all': '&&'}[oper] + oper = {'all': '&&', 'any': '||'}[oper] operands = [cgen_ifcond(o) for o in operands] return '(' + (') ' + oper + ' (').join(operands) + ')' @@ -220,7 +220,7 @@ def docgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str: return ifcond oper, operands = next(iter(ifcond.items())) - oper = {'all': ' and '}[oper] + oper = {'all': ' and ', 'any': ' or '}[oper] operands = [docgen_ifcond(o) for o in operands] return '(' + oper.join(operands) + ')' diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index d7a34655a7..f3ce10fb3e 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -290,16 +290,16 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None: raise QAPISemError( info, "'if' condition dict of %s must have one key: " - "'all'" % source) + "'all' or 'any'" % source) check_keys(cond, info, "'if' condition", [], - ["all"]) + ["all", "any"]) oper, operands = next(iter(cond.items())) if not operands: raise QAPISemError( info, "'if' condition [] of %s is useless" % source) - if oper in ("all") and not isinstance(operands, list): + if oper in ("all", "any") and not isinstance(operands, list): raise QAPISemError( info, "'%s' condition of %s must be an array" % (oper, source)) for operand in operands: diff --git a/tests/qapi-schema/bad-if-key.err b/tests/qapi-schema/bad-if-key.err index 725d5abae5..7236f46e7a 100644 --- a/tests/qapi-schema/bad-if-key.err +++ b/tests/qapi-schema/bad-if-key.err @@ -1,3 +1,3 @@ bad-if-key.json: In struct 'TestIfStruct': bad-if-key.json:2: 'if' condition has unknown key 'value' -Valid keys are 'all'. +Valid keys are 'all', 'any'. diff --git a/tests/qapi-schema/bad-if-keys.err b/tests/qapi-schema/bad-if-keys.err index b072db9a6f..db6d019d77 100644 --- a/tests/qapi-schema/bad-if-keys.err +++ b/tests/qapi-schema/bad-if-keys.err @@ -1,2 +1,2 @@ bad-if-keys.json: In struct 'TestIfStruct': -bad-if-keys.json:2: 'if' condition dict of struct must have one key: 'all' +bad-if-keys.json:2: 'if' condition dict of struct must have one key: 'all' or 'any' diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 25b1053e8a..e253d89ee0 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -103,7 +103,9 @@ 'features': [ 'union-feat1' ], 'base': 'Base', 'discriminator': 'base1', - 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } + 'data': { 'one': 'Variant1', + 'two': { 'type': 'Variant2', + 'if': { 'any': ['IFONE', 'IFTWO'] } } } } ## # @SugaredUnion: diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 689d084f3a..c44c346ec8 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -30,7 +30,7 @@ object Object tag base1 case one: Variant1 case two: Variant2 - if IFTWO + if OrderedDict([('any', ['IFONE', 'IFTWO'])]) feature union-feat1 object q_obj_Variant1-wrapper member data: Variant1 optional=False diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index 4490108cb7..251e9b746c 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -120,7 +120,8 @@ Members The members of "Base" The members of "Variant1" when "base1" is ""one"" -The members of "Variant2" when "base1" is ""two"" (**If: **"IFTWO") +The members of "Variant2" when "base1" is ""two"" (**If: **"(IFONE or +IFTWO)") Features ~~~~~~~~ diff --git a/tests/qapi-schema/enum-if-invalid.err b/tests/qapi-schema/enum-if-invalid.err index df305cd79f..b96d94c48a 100644 --- a/tests/qapi-schema/enum-if-invalid.err +++ b/tests/qapi-schema/enum-if-invalid.err @@ -1,3 +1,3 @@ enum-if-invalid.json: In enum 'TestIfEnum': enum-if-invalid.json:2: 'if' condition has unknown key 'val' -Valid keys are 'all'. +Valid keys are 'all', 'any'. diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index e85a71c0f7..7737b32de8 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -291,6 +291,11 @@ 'features': [ { 'name': 'feature1', 'if': { 'all': [ 'defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] } } ] } +{ 'struct': 'CondFeatureStruct4', + 'data': { 'foo': 'int' }, + 'features': [ { 'name': 'feature1', + 'if': {'any': ['defined(TEST_IF_COND_1)', + 'defined(TEST_IF_COND_2)'] } } ] } { 'enum': 'FeatureEnum1', 'data': [ 'eins', 'zwei', 'drei' ], @@ -314,7 +319,8 @@ '*fs4': 'FeatureStruct4', '*cfs1': 'CondFeatureStruct1', '*cfs2': 'CondFeatureStruct2', - '*cfs3': 'CondFeatureStruct3' }, + '*cfs3': 'CondFeatureStruct3', + '*cfs4': 'CondFeatureStruct4' }, 'returns': 'FeatureStruct1', 'features': [] } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index e74756cfd8..2f067d57af 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -390,6 +390,10 @@ object CondFeatureStruct3 member foo: int optional=False feature feature1 if OrderedDict([('all', ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'])]) +object CondFeatureStruct4 + member foo: int optional=False + feature feature1 + if OrderedDict([('any', ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'])]) enum FeatureEnum1 member eins member zwei @@ -417,6 +421,7 @@ object q_obj_test-features0-arg member cfs1: CondFeatureStruct1 optional=True member cfs2: CondFeatureStruct2 optional=True member cfs3: CondFeatureStruct3 optional=True + member cfs4: CondFeatureStruct4 optional=True command test-features0 q_obj_test-features0-arg -> FeatureStruct1 gen=True success_response=True boxed=False oob=False preconfig=False command test-command-features1 None -> None diff --git a/tests/unit/test-qmp-cmds.c b/tests/unit/test-qmp-cmds.c index 1b0b7d99df..83efa39720 100644 --- a/tests/unit/test-qmp-cmds.c +++ b/tests/unit/test-qmp-cmds.c @@ -51,6 +51,7 @@ FeatureStruct1 *qmp_test_features0(bool has_fs0, FeatureStruct0 *fs0, bool has_cfs1, CondFeatureStruct1 *cfs1, bool has_cfs2, CondFeatureStruct2 *cfs2, bool has_cfs3, CondFeatureStruct3 *cfs3, + bool has_cfs4, CondFeatureStruct4 *cfs4, Error **errp) { return g_new0(FeatureStruct1, 1); From 8a156d89d15ec190ff519e7ecaaa0b85e1ff4a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 4 Aug 2021 12:31:03 +0400 Subject: [PATCH 091/493] qapi: Use 'if': { 'any': ... } where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Stefan Hajnoczi Tested-by: John Snow Reviewed-by: Markus Armbruster Message-Id: <20210804083105.97531-9-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- qapi/machine-target.json | 20 ++++++++++++++++---- qapi/misc-target.json | 12 +++++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/qapi/machine-target.json b/qapi/machine-target.json index e7811654b7..9b56b81bea 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -213,7 +213,9 @@ ## { 'struct': 'CpuModelExpansionInfo', 'data': { 'model': 'CpuModelInfo' }, - 'if': 'defined(TARGET_S390X) || defined(TARGET_I386) || defined(TARGET_ARM)' } + 'if': { 'any': [ 'defined(TARGET_S390X)', + 'defined(TARGET_I386)', + 'defined(TARGET_ARM)'] } } ## # @query-cpu-model-expansion: @@ -252,7 +254,9 @@ 'data': { 'type': 'CpuModelExpansionType', 'model': 'CpuModelInfo' }, 'returns': 'CpuModelExpansionInfo', - 'if': 'defined(TARGET_S390X) || defined(TARGET_I386) || defined(TARGET_ARM)' } + 'if': { 'any': [ 'defined(TARGET_S390X)', + 'defined(TARGET_I386)', + 'defined(TARGET_ARM)' ] } } ## # @CpuDefinitionInfo: @@ -316,7 +320,11 @@ 'typename': 'str', '*alias-of' : 'str', 'deprecated' : 'bool' }, - 'if': 'defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_I386) || defined(TARGET_S390X) || defined(TARGET_MIPS)' } + 'if': { 'any': [ 'defined(TARGET_PPC)', + 'defined(TARGET_ARM)', + 'defined(TARGET_I386)', + 'defined(TARGET_S390X)', + 'defined(TARGET_MIPS)' ] } } ## # @query-cpu-definitions: @@ -328,4 +336,8 @@ # Since: 1.2 ## { 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'], - 'if': 'defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_I386) || defined(TARGET_S390X) || defined(TARGET_MIPS)' } + 'if': { 'any': [ 'defined(TARGET_PPC)', + 'defined(TARGET_ARM)', + 'defined(TARGET_I386)', + 'defined(TARGET_S390X)', + 'defined(TARGET_MIPS)' ] } } diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 5573dcf8f0..9e2ea4a04a 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -23,7 +23,17 @@ ## { 'event': 'RTC_CHANGE', 'data': { 'offset': 'int' }, - 'if': 'defined(TARGET_ALPHA) || defined(TARGET_ARM) || defined(TARGET_HPPA) || defined(TARGET_I386) || defined(TARGET_MIPS) || defined(TARGET_MIPS64) || defined(TARGET_PPC) || defined(TARGET_PPC64) || defined(TARGET_S390X) || defined(TARGET_SH4) || defined(TARGET_SPARC)' } + 'if': { 'any': [ 'defined(TARGET_ALPHA)', + 'defined(TARGET_ARM)', + 'defined(TARGET_HPPA)', + 'defined(TARGET_I386)', + 'defined(TARGET_MIPS)', + 'defined(TARGET_MIPS64)', + 'defined(TARGET_PPC)', + 'defined(TARGET_PPC64)', + 'defined(TARGET_S390X)', + 'defined(TARGET_SH4)', + 'defined(TARGET_SPARC)' ] } } ## # @rtc-reset-reinjection: From 2b7d2145369f2ca55ded9045393bb860ee3f6745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 4 Aug 2021 12:31:04 +0400 Subject: [PATCH 092/493] qapi: add 'not' condition operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the sake of completeness, introduce the 'not' condition. Signed-off-by: Marc-André Lureau Message-Id: <20210804083105.97531-10-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [Long line broken in tests/qapi-schema/qapi-schema-test.json] Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 4 ++++ scripts/qapi/expr.py | 7 +++++-- tests/qapi-schema/bad-if-key.err | 2 +- tests/qapi-schema/bad-if-keys.err | 2 +- tests/qapi-schema/doc-good.json | 3 ++- tests/qapi-schema/doc-good.out | 1 + tests/qapi-schema/doc-good.txt | 6 ++++++ tests/qapi-schema/enum-if-invalid.err | 2 +- tests/qapi-schema/qapi-schema-test.json | 3 ++- tests/qapi-schema/qapi-schema-test.out | 4 ++-- 10 files changed, 25 insertions(+), 9 deletions(-) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 63a2e502fb..3fb2fbe7d4 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -207,6 +207,8 @@ def cgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str: return ifcond oper, operands = next(iter(ifcond.items())) + if oper == 'not': + return '!' + cgen_ifcond(operands) oper = {'all': '&&', 'any': '||'}[oper] operands = [cgen_ifcond(o) for o in operands] return '(' + (') ' + oper + ' (').join(operands) + ')' @@ -220,6 +222,8 @@ def docgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str: return ifcond oper, operands = next(iter(ifcond.items())) + if oper == 'not': + return '!' + docgen_ifcond(operands) oper = {'all': ' and ', 'any': ' or '}[oper] operands = [docgen_ifcond(o) for o in operands] return '(' + oper.join(operands) + ')' diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index f3ce10fb3e..120b31089f 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -290,15 +290,18 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None: raise QAPISemError( info, "'if' condition dict of %s must have one key: " - "'all' or 'any'" % source) + "'all', 'any' or 'not'" % source) check_keys(cond, info, "'if' condition", [], - ["all", "any"]) + ["all", "any", "not"]) oper, operands = next(iter(cond.items())) if not operands: raise QAPISemError( info, "'if' condition [] of %s is useless" % source) + if oper == "not": + _check_if(operands) + return if oper in ("all", "any") and not isinstance(operands, list): raise QAPISemError( info, "'%s' condition of %s must be an array" % (oper, source)) diff --git a/tests/qapi-schema/bad-if-key.err b/tests/qapi-schema/bad-if-key.err index 7236f46e7a..a69dc9ee86 100644 --- a/tests/qapi-schema/bad-if-key.err +++ b/tests/qapi-schema/bad-if-key.err @@ -1,3 +1,3 @@ bad-if-key.json: In struct 'TestIfStruct': bad-if-key.json:2: 'if' condition has unknown key 'value' -Valid keys are 'all', 'any'. +Valid keys are 'all', 'any', 'not'. diff --git a/tests/qapi-schema/bad-if-keys.err b/tests/qapi-schema/bad-if-keys.err index db6d019d77..aceb31dc6d 100644 --- a/tests/qapi-schema/bad-if-keys.err +++ b/tests/qapi-schema/bad-if-keys.err @@ -1,2 +1,2 @@ bad-if-keys.json: In struct 'TestIfStruct': -bad-if-keys.json:2: 'if' condition dict of struct must have one key: 'all' or 'any' +bad-if-keys.json:2: 'if' condition dict of struct must have one key: 'all', 'any' or 'not' diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index e253d89ee0..2a35c679a4 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -126,7 +126,8 @@ ## { 'alternate': 'Alternate', 'features': [ 'alt-feat' ], - 'data': { 'i': 'int', 'b': 'bool' } } + 'data': { 'i': 'int', 'b': 'bool' }, + 'if': { 'not': 'IFNOT' } } ## # == Another subsection diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index c44c346ec8..a8871e8f99 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -51,6 +51,7 @@ alternate Alternate tag type case i: int case b: bool + if OrderedDict([('not', 'IFNOT')]) feature alt-feat object q_obj_cmd-arg member arg1: int optional=False diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index 251e9b746c..03c98c4182 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -171,6 +171,12 @@ Features a feature +If +~~ + +"!IFNOT" + + Another subsection ================== diff --git a/tests/qapi-schema/enum-if-invalid.err b/tests/qapi-schema/enum-if-invalid.err index b96d94c48a..3bb84075a9 100644 --- a/tests/qapi-schema/enum-if-invalid.err +++ b/tests/qapi-schema/enum-if-invalid.err @@ -1,3 +1,3 @@ enum-if-invalid.json: In enum 'TestIfEnum': enum-if-invalid.json:2: 'if' condition has unknown key 'val' -Valid keys are 'all', 'any'. +Valid keys are 'all', 'any', 'not'. diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 7737b32de8..a700f2531b 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -245,7 +245,8 @@ { 'command': 'test-if-alternate-cmd', 'data': { 'alt-cmd-arg': 'TestIfAlternate' }, - 'if': { 'all': ['defined(TEST_IF_ALT)'] } } + 'if': { 'all': ['defined(TEST_IF_ALT)', + {'not': 'defined(TEST_IF_NOT_ALT)'}] } } { 'command': 'test-if-cmd', 'data': { diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 2f067d57af..53e12f3534 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -333,10 +333,10 @@ alternate TestIfAlternate if OrderedDict([('all', ['defined(TEST_IF_ALT)', 'defined(TEST_IF_STRUCT)'])]) object q_obj_test-if-alternate-cmd-arg member alt-cmd-arg: TestIfAlternate optional=False - if OrderedDict([('all', ['defined(TEST_IF_ALT)'])]) + if OrderedDict([('all', ['defined(TEST_IF_ALT)', OrderedDict([('not', 'defined(TEST_IF_NOT_ALT)')])])]) command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False - if OrderedDict([('all', ['defined(TEST_IF_ALT)'])]) + if OrderedDict([('all', ['defined(TEST_IF_ALT)', OrderedDict([('not', 'defined(TEST_IF_NOT_ALT)')])])]) object q_obj_test-if-cmd-arg member foo: TestIfStruct optional=False member bar: TestIfEnum optional=False From 8a9f1e1d9cc55f5eb0946cbf8fd1ef9a0e7d3dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 4 Aug 2021 12:31:05 +0400 Subject: [PATCH 093/493] qapi: make 'if' condition strings simple identifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the 'if' condition strings to be C-agnostic. It will accept '[A-Z][A-Z0-9_]*' identifiers. This allows to express configuration conditions in other languages (Rust or Python for ex) or other more suitable forms. Signed-off-by: Marc-André Lureau Reviewed-by: Stefan Hajnoczi Tested-by: John Snow Message-Id: <20210804083105.97531-11-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [Rebased with semantic conflict in redefined-event.json] Signed-off-by: Markus Armbruster --- qapi/block-core.json | 34 +++++----- qapi/block-export.json | 6 +- qapi/char.json | 12 ++-- qapi/machine-target.json | 40 +++++------ qapi/migration.json | 10 +-- qapi/misc-target.json | 50 +++++++------- qapi/qom.json | 10 +-- qapi/sockets.json | 6 +- qapi/tpm.json | 18 ++--- qapi/ui.json | 68 +++++++++---------- qga/qapi-schema.json | 8 +-- scripts/qapi/common.py | 2 +- scripts/qapi/expr.py | 4 +- .../alternate-branch-if-invalid.err | 2 +- tests/qapi-schema/bad-if-empty.err | 2 +- tests/qapi-schema/bad-if-list.err | 2 +- tests/qapi-schema/bad-if.json | 2 +- tests/qapi-schema/doc-good.json | 6 +- tests/qapi-schema/doc-good.out | 6 +- tests/qapi-schema/doc-good.txt | 6 +- tests/qapi-schema/features-missing-name.json | 2 +- tests/qapi-schema/qapi-schema-test.json | 54 +++++++-------- tests/qapi-schema/qapi-schema-test.out | 60 ++++++++-------- tests/qapi-schema/redefined-event.json | 2 +- tests/qapi-schema/union-branch-if-invalid.err | 2 +- 25 files changed, 206 insertions(+), 208 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 675d8265eb..06674c25c9 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -914,7 +914,7 @@ 'data': { 'file': 'BlockStatsSpecificFile', 'host_device': { 'type': 'BlockStatsSpecificFile', - 'if': 'defined(HAVE_HOST_BLOCK_DEVICE)' }, + 'if': 'HAVE_HOST_BLOCK_DEVICE' }, 'nvme': 'BlockStatsSpecificNvme' } } ## @@ -2796,7 +2796,7 @@ ## { 'enum': 'BlockdevAioOptions', 'data': [ 'threads', 'native', - { 'name': 'io_uring', 'if': 'defined(CONFIG_LINUX_IO_URING)' } ] } + { 'name': 'io_uring', 'if': 'CONFIG_LINUX_IO_URING' } ] } ## # @BlockdevCacheOptions: @@ -2832,12 +2832,12 @@ 'data': [ 'blkdebug', 'blklogwrites', 'blkreplay', 'blkverify', 'bochs', 'cloop', 'compress', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster', - {'name': 'host_cdrom', 'if': 'defined(HAVE_HOST_BLOCK_DEVICE)' }, - {'name': 'host_device', 'if': 'defined(HAVE_HOST_BLOCK_DEVICE)' }, + {'name': 'host_cdrom', 'if': 'HAVE_HOST_BLOCK_DEVICE' }, + {'name': 'host_device', 'if': 'HAVE_HOST_BLOCK_DEVICE' }, 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd', - { 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' }, + { 'name': 'replication', 'if': 'CONFIG_REPLICATION' }, 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] } ## @@ -2879,10 +2879,10 @@ '*locking': 'OnOffAuto', '*aio': 'BlockdevAioOptions', '*drop-cache': {'type': 'bool', - 'if': 'defined(CONFIG_LINUX)'}, + 'if': 'CONFIG_LINUX'}, '*x-check-cache-dropped': 'bool' }, 'features': [ { 'name': 'dynamic-auto-read-only', - 'if': 'defined(CONFIG_POSIX)' } ] } + 'if': 'CONFIG_POSIX' } ] } ## # @BlockdevOptionsNull: @@ -3774,7 +3774,7 @@ # Since: 2.9 ## { 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ], - 'if': 'defined(CONFIG_REPLICATION)' } + 'if': 'CONFIG_REPLICATION' } ## # @BlockdevOptionsReplication: @@ -3793,7 +3793,7 @@ 'base': 'BlockdevOptionsGenericFormat', 'data': { 'mode': 'ReplicationMode', '*top-id': 'str' }, - 'if': 'defined(CONFIG_REPLICATION)' } + 'if': 'CONFIG_REPLICATION' } ## # @NFSTransport: @@ -4108,9 +4108,9 @@ 'ftps': 'BlockdevOptionsCurlFtps', 'gluster': 'BlockdevOptionsGluster', 'host_cdrom': { 'type': 'BlockdevOptionsFile', - 'if': 'defined(HAVE_HOST_BLOCK_DEVICE)' }, + 'if': 'HAVE_HOST_BLOCK_DEVICE' }, 'host_device': { 'type': 'BlockdevOptionsFile', - 'if': 'defined(HAVE_HOST_BLOCK_DEVICE)' }, + 'if': 'HAVE_HOST_BLOCK_DEVICE' }, 'http': 'BlockdevOptionsCurlHttp', 'https': 'BlockdevOptionsCurlHttps', 'iscsi': 'BlockdevOptionsIscsi', @@ -4129,7 +4129,7 @@ 'raw': 'BlockdevOptionsRaw', 'rbd': 'BlockdevOptionsRbd', 'replication': { 'type': 'BlockdevOptionsReplication', - 'if': 'defined(CONFIG_REPLICATION)' }, + 'if': 'CONFIG_REPLICATION' }, 'ssh': 'BlockdevOptionsSsh', 'throttle': 'BlockdevOptionsThrottle', 'vdi': 'BlockdevOptionsGenericFormat', @@ -4307,8 +4307,8 @@ # @size: Size of the virtual disk in bytes # @preallocation: Preallocation mode for the new image (default: off; # allowed values: off, -# falloc (if defined CONFIG_POSIX_FALLOCATE), -# full (if defined CONFIG_POSIX)) +# falloc (if CONFIG_POSIX_FALLOCATE), +# full (if CONFIG_POSIX)) # @nocow: Turn off copy-on-write (valid only on btrfs; default: off) # @extent-size-hint: Extent size hint to add to the image file; 0 for not # adding an extent size hint (default: 1 MB, since 5.1) @@ -4331,8 +4331,8 @@ # @size: Size of the virtual disk in bytes # @preallocation: Preallocation mode for the new image (default: off; # allowed values: off, -# falloc (if defined CONFIG_GLUSTERFS_FALLOCATE), -# full (if defined CONFIG_GLUSTERFS_ZEROFILL)) +# falloc (if CONFIG_GLUSTERFS_FALLOCATE), +# full (if CONFIG_GLUSTERFS_ZEROFILL)) # # Since: 2.12 ## @@ -4432,7 +4432,7 @@ # Since: 5.1 ## { 'enum': 'Qcow2CompressionType', - 'data': [ 'zlib', { 'name': 'zstd', 'if': 'defined(CONFIG_ZSTD)' } ] } + 'data': [ 'zlib', { 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] } ## # @BlockdevCreateOptionsQcow2: diff --git a/qapi/block-export.json b/qapi/block-export.json index 0ed63442a8..c1b92ce1c1 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -168,7 +168,7 @@ 'data': { 'mountpoint': 'str', '*growable': 'bool', '*allow-other': 'FuseExportAllowOther' }, - 'if': 'defined(CONFIG_FUSE)' } + 'if': 'CONFIG_FUSE' } ## # @NbdServerAddOptions: @@ -278,7 +278,7 @@ ## { 'enum': 'BlockExportType', 'data': [ 'nbd', 'vhost-user-blk', - { 'name': 'fuse', 'if': 'defined(CONFIG_FUSE)' } ] } + { 'name': 'fuse', 'if': 'CONFIG_FUSE' } ] } ## # @BlockExportOptions: @@ -321,7 +321,7 @@ 'nbd': 'BlockExportOptionsNbd', 'vhost-user-blk': 'BlockExportOptionsVhostUserBlk', 'fuse': { 'type': 'BlockExportOptionsFuse', - 'if': 'defined(CONFIG_FUSE)' } + 'if': 'CONFIG_FUSE' } } } ## diff --git a/qapi/char.json b/qapi/char.json index adf2685f68..9b18ee3305 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -342,7 +342,7 @@ { 'struct': 'ChardevSpiceChannel', 'data': { 'type': 'str' }, 'base': 'ChardevCommon', - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # @ChardevSpicePort: @@ -356,7 +356,7 @@ { 'struct': 'ChardevSpicePort', 'data': { 'fqdn': 'str' }, 'base': 'ChardevCommon', - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # @ChardevVC: @@ -405,7 +405,7 @@ 'data': { '*mouse': 'bool', '*clipboard': 'bool' }, 'base': 'ChardevCommon', - 'if': 'defined(CONFIG_SPICE_PROTOCOL)' } + 'if': 'CONFIG_SPICE_PROTOCOL' } ## # @ChardevBackend: @@ -431,11 +431,11 @@ 'stdio': 'ChardevStdio', 'console': 'ChardevCommon', 'spicevmc': { 'type': 'ChardevSpiceChannel', - 'if': 'defined(CONFIG_SPICE)' }, + 'if': 'CONFIG_SPICE' }, 'spiceport': { 'type': 'ChardevSpicePort', - 'if': 'defined(CONFIG_SPICE)' }, + 'if': 'CONFIG_SPICE' }, 'qemu-vdagent': { 'type': 'ChardevQemuVDAgent', - 'if': 'defined(CONFIG_SPICE_PROTOCOL)' }, + 'if': 'CONFIG_SPICE_PROTOCOL' }, 'vc': 'ChardevVC', 'ringbuf': 'ChardevRingbuf', # next one is just for compatibility diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 9b56b81bea..f5ec4bc172 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -89,7 +89,7 @@ ## { 'struct': 'CpuModelBaselineInfo', 'data': { 'model': 'CpuModelInfo' }, - 'if': 'defined(TARGET_S390X)' } + 'if': 'TARGET_S390X' } ## # @CpuModelCompareInfo: @@ -112,7 +112,7 @@ { 'struct': 'CpuModelCompareInfo', 'data': { 'result': 'CpuModelCompareResult', 'responsible-properties': ['str'] }, - 'if': 'defined(TARGET_S390X)' } + 'if': 'TARGET_S390X' } ## # @query-cpu-model-comparison: @@ -156,7 +156,7 @@ { 'command': 'query-cpu-model-comparison', 'data': { 'modela': 'CpuModelInfo', 'modelb': 'CpuModelInfo' }, 'returns': 'CpuModelCompareInfo', - 'if': 'defined(TARGET_S390X)' } + 'if': 'TARGET_S390X' } ## # @query-cpu-model-baseline: @@ -200,7 +200,7 @@ 'data': { 'modela': 'CpuModelInfo', 'modelb': 'CpuModelInfo' }, 'returns': 'CpuModelBaselineInfo', - 'if': 'defined(TARGET_S390X)' } + 'if': 'TARGET_S390X' } ## # @CpuModelExpansionInfo: @@ -213,9 +213,9 @@ ## { 'struct': 'CpuModelExpansionInfo', 'data': { 'model': 'CpuModelInfo' }, - 'if': { 'any': [ 'defined(TARGET_S390X)', - 'defined(TARGET_I386)', - 'defined(TARGET_ARM)'] } } + 'if': { 'any': [ 'TARGET_S390X', + 'TARGET_I386', + 'TARGET_ARM' ] } } ## # @query-cpu-model-expansion: @@ -254,9 +254,9 @@ 'data': { 'type': 'CpuModelExpansionType', 'model': 'CpuModelInfo' }, 'returns': 'CpuModelExpansionInfo', - 'if': { 'any': [ 'defined(TARGET_S390X)', - 'defined(TARGET_I386)', - 'defined(TARGET_ARM)' ] } } + 'if': { 'any': [ 'TARGET_S390X', + 'TARGET_I386', + 'TARGET_ARM' ] } } ## # @CpuDefinitionInfo: @@ -320,11 +320,11 @@ 'typename': 'str', '*alias-of' : 'str', 'deprecated' : 'bool' }, - 'if': { 'any': [ 'defined(TARGET_PPC)', - 'defined(TARGET_ARM)', - 'defined(TARGET_I386)', - 'defined(TARGET_S390X)', - 'defined(TARGET_MIPS)' ] } } + 'if': { 'any': [ 'TARGET_PPC', + 'TARGET_ARM', + 'TARGET_I386', + 'TARGET_S390X', + 'TARGET_MIPS' ] } } ## # @query-cpu-definitions: @@ -336,8 +336,8 @@ # Since: 1.2 ## { 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'], - 'if': { 'any': [ 'defined(TARGET_PPC)', - 'defined(TARGET_ARM)', - 'defined(TARGET_I386)', - 'defined(TARGET_S390X)', - 'defined(TARGET_MIPS)' ] } } + 'if': { 'any': [ 'TARGET_PPC', + 'TARGET_ARM', + 'TARGET_I386', + 'TARGET_S390X', + 'TARGET_MIPS' ] } } diff --git a/qapi/migration.json b/qapi/migration.json index 1124a2dda8..88f07baedd 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -533,7 +533,7 @@ ## { 'enum': 'MultiFDCompression', 'data': [ 'none', 'zlib', - { 'name': 'zstd', 'if': 'defined(CONFIG_ZSTD)' } ] } + { 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] } ## # @BitmapMigrationBitmapAliasTransform: @@ -1562,7 +1562,7 @@ ## { 'command': 'xen-set-replication', 'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' }, - 'if': 'defined(CONFIG_REPLICATION)' } + 'if': 'CONFIG_REPLICATION' } ## # @ReplicationStatus: @@ -1578,7 +1578,7 @@ ## { 'struct': 'ReplicationStatus', 'data': { 'error': 'bool', '*desc': 'str' }, - 'if': 'defined(CONFIG_REPLICATION)' } + 'if': 'CONFIG_REPLICATION' } ## # @query-xen-replication-status: @@ -1596,7 +1596,7 @@ ## { 'command': 'query-xen-replication-status', 'returns': 'ReplicationStatus', - 'if': 'defined(CONFIG_REPLICATION)' } + 'if': 'CONFIG_REPLICATION' } ## # @xen-colo-do-checkpoint: @@ -1613,7 +1613,7 @@ # Since: 2.9 ## { 'command': 'xen-colo-do-checkpoint', - 'if': 'defined(CONFIG_REPLICATION)' } + 'if': 'CONFIG_REPLICATION' } ## # @COLOStatus: diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 9e2ea4a04a..3b05ad3dbf 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -23,17 +23,17 @@ ## { 'event': 'RTC_CHANGE', 'data': { 'offset': 'int' }, - 'if': { 'any': [ 'defined(TARGET_ALPHA)', - 'defined(TARGET_ARM)', - 'defined(TARGET_HPPA)', - 'defined(TARGET_I386)', - 'defined(TARGET_MIPS)', - 'defined(TARGET_MIPS64)', - 'defined(TARGET_PPC)', - 'defined(TARGET_PPC64)', - 'defined(TARGET_S390X)', - 'defined(TARGET_SH4)', - 'defined(TARGET_SPARC)' ] } } + 'if': { 'any': [ 'TARGET_ALPHA', + 'TARGET_ARM', + 'TARGET_HPPA', + 'TARGET_I386', + 'TARGET_MIPS', + 'TARGET_MIPS64', + 'TARGET_PPC', + 'TARGET_PPC64', + 'TARGET_S390X', + 'TARGET_SH4', + 'TARGET_SPARC' ] } } ## # @rtc-reset-reinjection: @@ -52,7 +52,7 @@ # ## { 'command': 'rtc-reset-reinjection', - 'if': 'defined(TARGET_I386)' } + 'if': 'TARGET_I386' } ## @@ -79,7 +79,7 @@ { 'enum': 'SevState', 'data': ['uninit', 'launch-update', 'launch-secret', 'running', 'send-update', 'receive-update' ], - 'if': 'defined(TARGET_I386)' } + 'if': 'TARGET_I386' } ## # @SevInfo: @@ -111,7 +111,7 @@ 'state' : 'SevState', 'handle' : 'uint32' }, - 'if': 'defined(TARGET_I386)' + 'if': 'TARGET_I386' } ## @@ -132,7 +132,7 @@ # ## { 'command': 'query-sev', 'returns': 'SevInfo', - 'if': 'defined(TARGET_I386)' } + 'if': 'TARGET_I386' } ## @@ -146,7 +146,7 @@ # ## { 'struct': 'SevLaunchMeasureInfo', 'data': {'data': 'str'}, - 'if': 'defined(TARGET_I386)' } + 'if': 'TARGET_I386' } ## # @query-sev-launch-measure: @@ -164,7 +164,7 @@ # ## { 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo', - 'if': 'defined(TARGET_I386)' } + 'if': 'TARGET_I386' } ## @@ -189,7 +189,7 @@ 'cert-chain': 'str', 'cbitpos': 'int', 'reduced-phys-bits': 'int'}, - 'if': 'defined(TARGET_I386)' } + 'if': 'TARGET_I386' } ## # @query-sev-capabilities: @@ -209,7 +209,7 @@ # ## { 'command': 'query-sev-capabilities', 'returns': 'SevCapability', - 'if': 'defined(TARGET_I386)' } + 'if': 'TARGET_I386' } ## # @sev-inject-launch-secret: @@ -227,7 +227,7 @@ ## { 'command': 'sev-inject-launch-secret', 'data': { 'packet-header': 'str', 'secret': 'str', '*gpa': 'uint64' }, - 'if': 'defined(TARGET_I386)' } + 'if': 'TARGET_I386' } ## # @dump-skeys: @@ -249,7 +249,7 @@ ## { 'command': 'dump-skeys', 'data': { 'filename': 'str' }, - 'if': 'defined(TARGET_S390X)' } + 'if': 'TARGET_S390X' } ## # @GICCapability: @@ -274,7 +274,7 @@ 'data': { 'version': 'int', 'emulated': 'bool', 'kernel': 'bool' }, - 'if': 'defined(TARGET_ARM)' } + 'if': 'TARGET_ARM' } ## # @query-gic-capabilities: @@ -294,7 +294,7 @@ # ## { 'command': 'query-gic-capabilities', 'returns': ['GICCapability'], - 'if': 'defined(TARGET_ARM)' } + 'if': 'TARGET_ARM' } ## @@ -310,7 +310,7 @@ ## { 'struct': 'SevAttestationReport', 'data': { 'data': 'str'}, - 'if': 'defined(TARGET_I386)' } + 'if': 'TARGET_I386' } ## # @query-sev-attestation-report: @@ -332,4 +332,4 @@ ## { 'command': 'query-sev-attestation-report', 'data': { 'mnonce': 'str' }, 'returns': 'SevAttestationReport', - 'if': 'defined(TARGET_I386)' } + 'if': 'TARGET_I386' } diff --git a/qapi/qom.json b/qapi/qom.json index 6d5f4a88e6..a25616bc7a 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -618,7 +618,7 @@ 'data': { '*align': 'size', '*discard-data': 'bool', 'mem-path': 'str', - '*pmem': { 'type': 'bool', 'if': 'defined(CONFIG_LIBPMEM)' }, + '*pmem': { 'type': 'bool', 'if': 'CONFIG_LIBPMEM' }, '*readonly': 'bool' } } ## @@ -782,7 +782,7 @@ 'cryptodev-backend', 'cryptodev-backend-builtin', { 'name': 'cryptodev-vhost-user', - 'if': 'defined(CONFIG_VHOST_CRYPTO)' }, + 'if': 'CONFIG_VHOST_CRYPTO' }, 'dbus-vmstate', 'filter-buffer', 'filter-dump', @@ -795,7 +795,7 @@ 'iothread', 'memory-backend-file', { 'name': 'memory-backend-memfd', - 'if': 'defined(CONFIG_LINUX)' }, + 'if': 'CONFIG_LINUX' }, 'memory-backend-ram', 'pef-guest', 'pr-manager-helper', @@ -840,7 +840,7 @@ 'cryptodev-backend': 'CryptodevBackendProperties', 'cryptodev-backend-builtin': 'CryptodevBackendProperties', 'cryptodev-vhost-user': { 'type': 'CryptodevVhostUserProperties', - 'if': 'defined(CONFIG_VHOST_CRYPTO)' }, + 'if': 'CONFIG_VHOST_CRYPTO' }, 'dbus-vmstate': 'DBusVMStateProperties', 'filter-buffer': 'FilterBufferProperties', 'filter-dump': 'FilterDumpProperties', @@ -853,7 +853,7 @@ 'iothread': 'IothreadProperties', 'memory-backend-file': 'MemoryBackendFileProperties', 'memory-backend-memfd': { 'type': 'MemoryBackendMemfdProperties', - 'if': 'defined(CONFIG_LINUX)' }, + 'if': 'CONFIG_LINUX' }, 'memory-backend-ram': 'MemoryBackendProperties', 'pr-manager-helper': 'PrManagerHelperProperties', 'qtest': 'QtestProperties', diff --git a/qapi/sockets.json b/qapi/sockets.json index 735eb4abb5..7866dc27d6 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -69,7 +69,7 @@ '*ipv4': 'bool', '*ipv6': 'bool', '*keep-alive': 'bool', - '*mptcp': { 'type': 'bool', 'if': 'defined(IPPROTO_MPTCP)' } } } + '*mptcp': { 'type': 'bool', 'if': 'IPPROTO_MPTCP' } } } ## # @UnixSocketAddress: @@ -89,8 +89,8 @@ { 'struct': 'UnixSocketAddress', 'data': { 'path': 'str', - '*abstract': { 'type': 'bool', 'if': 'defined(CONFIG_LINUX)' }, - '*tight': { 'type': 'bool', 'if': 'defined(CONFIG_LINUX)' } } } + '*abstract': { 'type': 'bool', 'if': 'CONFIG_LINUX' }, + '*tight': { 'type': 'bool', 'if': 'CONFIG_LINUX' } } } ## # @VsockSocketAddress: diff --git a/qapi/tpm.json b/qapi/tpm.json index 75590979fd..f4dde2f646 100644 --- a/qapi/tpm.json +++ b/qapi/tpm.json @@ -18,7 +18,7 @@ # Since: 1.5 ## { 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb', 'tpm-spapr' ], - 'if': 'defined(CONFIG_TPM)' } + 'if': 'CONFIG_TPM' } ## # @query-tpm-models: @@ -36,7 +36,7 @@ # ## { 'command': 'query-tpm-models', 'returns': ['TpmModel'], - 'if': 'defined(CONFIG_TPM)' } + 'if': 'CONFIG_TPM' } ## # @TpmType: @@ -50,7 +50,7 @@ # Since: 1.5 ## { 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ], - 'if': 'defined(CONFIG_TPM)' } + 'if': 'CONFIG_TPM' } ## # @query-tpm-types: @@ -68,7 +68,7 @@ # ## { 'command': 'query-tpm-types', 'returns': ['TpmType'], - 'if': 'defined(CONFIG_TPM)' } + 'if': 'CONFIG_TPM' } ## # @TPMPassthroughOptions: @@ -85,7 +85,7 @@ { 'struct': 'TPMPassthroughOptions', 'data': { '*path': 'str', '*cancel-path': 'str' }, - 'if': 'defined(CONFIG_TPM)' } + 'if': 'CONFIG_TPM' } ## # @TPMEmulatorOptions: @@ -97,7 +97,7 @@ # Since: 2.11 ## { 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' }, - 'if': 'defined(CONFIG_TPM)' } + 'if': 'CONFIG_TPM' } ## # @TpmTypeOptions: @@ -112,7 +112,7 @@ { 'union': 'TpmTypeOptions', 'data': { 'passthrough' : 'TPMPassthroughOptions', 'emulator': 'TPMEmulatorOptions' }, - 'if': 'defined(CONFIG_TPM)' } + 'if': 'CONFIG_TPM' } ## # @TPMInfo: @@ -131,7 +131,7 @@ 'data': {'id': 'str', 'model': 'TpmModel', 'options': 'TpmTypeOptions' }, - 'if': 'defined(CONFIG_TPM)' } + 'if': 'CONFIG_TPM' } ## # @query-tpm: @@ -162,4 +162,4 @@ # ## { 'command': 'query-tpm', 'returns': ['TPMInfo'], - 'if': 'defined(CONFIG_TPM)' } + 'if': 'CONFIG_TPM' } diff --git a/qapi/ui.json b/qapi/ui.json index aed2bec4ab..b2cf7a6759 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -123,7 +123,7 @@ 'data': { 'host': 'str', 'port': 'str', 'family': 'NetworkAddressFamily' }, - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # @SpiceServerInfo: @@ -137,7 +137,7 @@ { 'struct': 'SpiceServerInfo', 'base': 'SpiceBasicInfo', 'data': { '*auth': 'str' }, - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # @SpiceChannel: @@ -163,7 +163,7 @@ 'base': 'SpiceBasicInfo', 'data': {'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int', 'tls': 'bool'}, - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # @SpiceQueryMouseMode: @@ -183,7 +183,7 @@ ## { 'enum': 'SpiceQueryMouseMode', 'data': [ 'client', 'server', 'unknown' ], - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # @SpiceInfo: @@ -222,7 +222,7 @@ 'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int', '*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str', 'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']}, - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # @query-spice: @@ -268,7 +268,7 @@ # ## { 'command': 'query-spice', 'returns': 'SpiceInfo', - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # @SPICE_CONNECTED: @@ -294,7 +294,7 @@ { 'event': 'SPICE_CONNECTED', 'data': { 'server': 'SpiceBasicInfo', 'client': 'SpiceBasicInfo' }, - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # @SPICE_INITIALIZED: @@ -323,7 +323,7 @@ { 'event': 'SPICE_INITIALIZED', 'data': { 'server': 'SpiceServerInfo', 'client': 'SpiceChannel' }, - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # @SPICE_DISCONNECTED: @@ -349,7 +349,7 @@ { 'event': 'SPICE_DISCONNECTED', 'data': { 'server': 'SpiceBasicInfo', 'client': 'SpiceBasicInfo' }, - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # @SPICE_MIGRATE_COMPLETED: @@ -365,7 +365,7 @@ # ## { 'event': 'SPICE_MIGRATE_COMPLETED', - 'if': 'defined(CONFIG_SPICE)' } + 'if': 'CONFIG_SPICE' } ## # == VNC @@ -393,7 +393,7 @@ 'service': 'str', 'family': 'NetworkAddressFamily', 'websocket': 'bool' }, - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @VncServerInfo: @@ -408,7 +408,7 @@ { 'struct': 'VncServerInfo', 'base': 'VncBasicInfo', 'data': { '*auth': 'str' }, - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @VncClientInfo: @@ -426,7 +426,7 @@ { 'struct': 'VncClientInfo', 'base': 'VncBasicInfo', 'data': { '*x509_dname': 'str', '*sasl_username': 'str' }, - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @VncInfo: @@ -469,7 +469,7 @@ 'data': {'enabled': 'bool', '*host': 'str', '*family': 'NetworkAddressFamily', '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']}, - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @VncPrimaryAuth: @@ -481,7 +481,7 @@ { 'enum': 'VncPrimaryAuth', 'data': [ 'none', 'vnc', 'ra2', 'ra2ne', 'tight', 'ultra', 'tls', 'vencrypt', 'sasl' ], - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @VncVencryptSubAuth: @@ -496,7 +496,7 @@ 'tls-vnc', 'x509-vnc', 'tls-plain', 'x509-plain', 'tls-sasl', 'x509-sasl' ], - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @VncServerInfo2: @@ -514,7 +514,7 @@ 'base': 'VncBasicInfo', 'data': { 'auth' : 'VncPrimaryAuth', '*vencrypt' : 'VncVencryptSubAuth' }, - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @VncInfo2: @@ -547,7 +547,7 @@ 'auth' : 'VncPrimaryAuth', '*vencrypt' : 'VncVencryptSubAuth', '*display' : 'str' }, - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @query-vnc: @@ -579,7 +579,7 @@ # ## { 'command': 'query-vnc', 'returns': 'VncInfo', - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @query-vnc-servers: # @@ -590,7 +590,7 @@ # Since: 2.3 ## { 'command': 'query-vnc-servers', 'returns': ['VncInfo2'], - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @change-vnc-password: @@ -606,7 +606,7 @@ ## { 'command': 'change-vnc-password', 'data': { 'password': 'str' }, - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @VNC_CONNECTED: @@ -636,7 +636,7 @@ { 'event': 'VNC_CONNECTED', 'data': { 'server': 'VncServerInfo', 'client': 'VncBasicInfo' }, - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @VNC_INITIALIZED: @@ -664,7 +664,7 @@ { 'event': 'VNC_INITIALIZED', 'data': { 'server': 'VncServerInfo', 'client': 'VncClientInfo' }, - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # @VNC_DISCONNECTED: @@ -691,7 +691,7 @@ { 'event': 'VNC_DISCONNECTED', 'data': { 'server': 'VncServerInfo', 'client': 'VncClientInfo' }, - 'if': 'defined(CONFIG_VNC)' } + 'if': 'CONFIG_VNC' } ## # = Input @@ -1133,14 +1133,13 @@ 'data' : [ { 'name': 'default' }, { 'name': 'none' }, - { 'name': 'gtk', 'if': 'defined(CONFIG_GTK)' }, - { 'name': 'sdl', 'if': 'defined(CONFIG_SDL)' }, + { 'name': 'gtk', 'if': 'CONFIG_GTK' }, + { 'name': 'sdl', 'if': 'CONFIG_SDL' }, { 'name': 'egl-headless', - 'if': { 'all': [ 'defined(CONFIG_OPENGL)', - 'defined(CONFIG_GBM)' ] } }, - { 'name': 'curses', 'if': 'defined(CONFIG_CURSES)' }, - { 'name': 'cocoa', 'if': 'defined(CONFIG_COCOA)' }, - { 'name': 'spice-app', 'if': 'defined(CONFIG_SPICE)'} ] } + 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } }, + { 'name': 'curses', 'if': 'CONFIG_CURSES' }, + { 'name': 'cocoa', 'if': 'CONFIG_COCOA' }, + { 'name': 'spice-app', 'if': 'CONFIG_SPICE'} ] } ## # @DisplayOptions: @@ -1165,11 +1164,10 @@ '*gl' : 'DisplayGLMode' }, 'discriminator' : 'type', 'data' : { - 'gtk': { 'type': 'DisplayGTK', 'if': 'defined(CONFIG_GTK)' }, - 'curses': { 'type': 'DisplayCurses', 'if': 'defined(CONFIG_CURSES)' }, + 'gtk': { 'type': 'DisplayGTK', 'if': 'CONFIG_GTK' }, + 'curses': { 'type': 'DisplayCurses', 'if': 'CONFIG_CURSES' }, 'egl-headless': { 'type': 'DisplayEGLHeadless', - 'if': { 'all': [ 'defined(CONFIG_OPENGL)', - 'defined(CONFIG_GBM)' ] } } + 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } } } } diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index fb17eebde3..c60f5e669d 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1380,7 +1380,7 @@ 'data': { 'keys': ['str'] }, - 'if': 'defined(CONFIG_POSIX)' } + 'if': 'CONFIG_POSIX' } ## @@ -1398,7 +1398,7 @@ { 'command': 'guest-ssh-get-authorized-keys', 'data': { 'username': 'str' }, 'returns': 'GuestAuthorizedKeys', - 'if': 'defined(CONFIG_POSIX)' } + 'if': 'CONFIG_POSIX' } ## # @guest-ssh-add-authorized-keys: @@ -1416,7 +1416,7 @@ ## { 'command': 'guest-ssh-add-authorized-keys', 'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' }, - 'if': 'defined(CONFIG_POSIX)' } + 'if': 'CONFIG_POSIX' } ## # @guest-ssh-remove-authorized-keys: @@ -1434,4 +1434,4 @@ ## { 'command': 'guest-ssh-remove-authorized-keys', 'data': { 'username': 'str', 'keys': ['str'] }, - 'if': 'defined(CONFIG_POSIX)' } + 'if': 'CONFIG_POSIX' } diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 3fb2fbe7d4..1724ac32db 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -204,7 +204,7 @@ def cgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str: if not ifcond: return '' if isinstance(ifcond, str): - return ifcond + return 'defined(' + ifcond + ')' oper, operands = next(iter(ifcond.items())) if oper == 'not': diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index 120b31089f..019f4c97aa 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -275,10 +275,10 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None: def _check_if(cond: Union[str, object]) -> None: if isinstance(cond, str): - if not cond.strip(): + if not re.match(r'^[A-Z][A-Z0-9_]*$', cond): raise QAPISemError( info, - "'if' condition '%s' of %s makes no sense" + "'if' condition '%s' of %s is not a valid identifier" % (cond, source)) return diff --git a/tests/qapi-schema/alternate-branch-if-invalid.err b/tests/qapi-schema/alternate-branch-if-invalid.err index d384929c51..03bad877a3 100644 --- a/tests/qapi-schema/alternate-branch-if-invalid.err +++ b/tests/qapi-schema/alternate-branch-if-invalid.err @@ -1,2 +1,2 @@ alternate-branch-if-invalid.json: In alternate 'Alt': -alternate-branch-if-invalid.json:2: 'if' condition ' ' of 'data' member 'branch' makes no sense +alternate-branch-if-invalid.json:2: 'if' condition ' ' of 'data' member 'branch' is not a valid identifier diff --git a/tests/qapi-schema/bad-if-empty.err b/tests/qapi-schema/bad-if-empty.err index a0f3effefb..5208f543ce 100644 --- a/tests/qapi-schema/bad-if-empty.err +++ b/tests/qapi-schema/bad-if-empty.err @@ -1,2 +1,2 @@ bad-if-empty.json: In struct 'TestIfStruct': -bad-if-empty.json:2: 'if' condition '' of struct makes no sense +bad-if-empty.json:2: 'if' condition '' of struct is not a valid identifier diff --git a/tests/qapi-schema/bad-if-list.err b/tests/qapi-schema/bad-if-list.err index c462f11b90..334e8b845a 100644 --- a/tests/qapi-schema/bad-if-list.err +++ b/tests/qapi-schema/bad-if-list.err @@ -1,2 +1,2 @@ bad-if-list.json: In struct 'TestIfStruct': -bad-if-list.json:2: 'if' condition ' ' of struct makes no sense +bad-if-list.json:2: 'if' condition 'foo' of struct is not a valid identifier diff --git a/tests/qapi-schema/bad-if.json b/tests/qapi-schema/bad-if.json index fdc0c87bb3..2639e3c661 100644 --- a/tests/qapi-schema/bad-if.json +++ b/tests/qapi-schema/bad-if.json @@ -1,3 +1,3 @@ # check invalid 'if' type { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, - 'if': ['defined(TEST_IF_STRUCT)'] } + 'if': ['TEST_IF_STRUCT'] } diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 2a35c679a4..5e30790730 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -61,9 +61,9 @@ # @two is undocumented ## { 'enum': 'Enum', 'data': - [ { 'name': 'one', 'if': 'defined(IFONE)' }, 'two' ], + [ { 'name': 'one', 'if': 'IFONE' }, 'two' ], 'features': [ 'enum-feat' ], - 'if': 'defined(IFCOND)' } + 'if': 'IFCOND' } ## # @Base: @@ -87,7 +87,7 @@ 'features': [ 'variant1-feat' ], 'data': { 'var1': { 'type': 'str', 'features': [ 'member-feat' ], - 'if': 'defined(IFSTR)' } } } + 'if': 'IFSTR' } } } ## # @Variant2: diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index a8871e8f99..26d1fa5d28 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -12,16 +12,16 @@ enum QType module doc-good.json enum Enum member one - if defined(IFONE) + if IFONE member two - if defined(IFCOND) + if IFCOND feature enum-feat object Base member base1: Enum optional=False if OrderedDict([('all', ['IFALL1', 'IFALL2'])]) object Variant1 member var1: str optional=False - if defined(IFSTR) + if IFSTR feature member-feat feature variant1-feat object Variant2 diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index 03c98c4182..5bfe06e14e 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -43,7 +43,7 @@ Example: Values ~~~~~~ -"one" (**If: **"defined(IFONE)") +"one" (**If: **"IFONE") The _one_ {and only} "two" @@ -62,7 +62,7 @@ Features If ~~ -"defined(IFCOND)" +"IFCOND" "Base" (Object) @@ -93,7 +93,7 @@ Another paragraph (but no "var": line) Members ~~~~~~~ -"var1": "string" (**If: **"defined(IFSTR)") +"var1": "string" (**If: **"IFSTR") Not documented diff --git a/tests/qapi-schema/features-missing-name.json b/tests/qapi-schema/features-missing-name.json index 2314f97c00..8772c8f7b3 100644 --- a/tests/qapi-schema/features-missing-name.json +++ b/tests/qapi-schema/features-missing-name.json @@ -1,3 +1,3 @@ { 'struct': 'FeatureStruct0', 'data': { 'foo': 'int' }, - 'features': [ { 'if': 'defined(NAMELESS_FEATURES)' } ] } + 'features': [ { 'if': 'NAMELESS_FEATURES' } ] } diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index a700f2531b..fe028145e4 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -222,45 +222,45 @@ { 'struct': 'TestIfStruct', 'data': { 'foo': 'int', - 'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} }, - 'if': 'defined(TEST_IF_STRUCT)' } + 'bar': { 'type': 'int', 'if': 'TEST_IF_STRUCT_BAR'} }, + 'if': 'TEST_IF_STRUCT' } { 'enum': 'TestIfEnum', 'data': - [ 'foo', { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ], - 'if': 'defined(TEST_IF_ENUM)' } + [ 'foo', { 'name' : 'bar', 'if': 'TEST_IF_ENUM_BAR' } ], + 'if': 'TEST_IF_ENUM' } { 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct', - 'bar': { 'type': 'str', 'if': 'defined(TEST_IF_UNION_BAR)'} }, - 'if': { 'all': ['defined(TEST_IF_UNION)', 'defined(TEST_IF_STRUCT)'] } } + 'bar': { 'type': 'str', 'if': 'TEST_IF_UNION_BAR'} }, + 'if': { 'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT'] } } { 'command': 'test-if-union-cmd', 'data': { 'union-cmd-arg': 'TestIfUnion' }, - 'if': 'defined(TEST_IF_UNION)' } + 'if': 'TEST_IF_UNION' } { 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', - 'bar': { 'type': 'TestStruct', 'if': 'defined(TEST_IF_ALT_BAR)'} }, - 'if': { 'all': ['defined(TEST_IF_ALT)', 'defined(TEST_IF_STRUCT)'] } } + 'bar': { 'type': 'TestStruct', 'if': 'TEST_IF_ALT_BAR'} }, + 'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } } { 'command': 'test-if-alternate-cmd', 'data': { 'alt-cmd-arg': 'TestIfAlternate' }, - 'if': { 'all': ['defined(TEST_IF_ALT)', - {'not': 'defined(TEST_IF_NOT_ALT)'}] } } + 'if': { 'all': ['TEST_IF_ALT', + {'not': 'TEST_IF_NOT_ALT'}] } } { 'command': 'test-if-cmd', 'data': { 'foo': 'TestIfStruct', - 'bar': { 'type': 'TestIfEnum', 'if': 'defined(TEST_IF_CMD_BAR)' } }, + 'bar': { 'type': 'TestIfEnum', 'if': 'TEST_IF_CMD_BAR' } }, 'returns': 'UserDefThree', - 'if': { 'all': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] } } + 'if': { 'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT'] } } { 'command': 'test-cmd-return-def-three', 'returns': 'UserDefThree' } { 'event': 'TEST_IF_EVENT', 'data': { 'foo': 'TestIfStruct', - 'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)' } }, - 'if': { 'all': ['defined(TEST_IF_EVT)', 'defined(TEST_IF_STRUCT)'] } } + 'bar': { 'type': ['TestIfEnum'], 'if': 'TEST_IF_EVT_BAR' } }, + 'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } } # test 'features' @@ -282,21 +282,21 @@ { 'struct': 'CondFeatureStruct1', 'data': { 'foo': 'int' }, - 'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'} ] } + 'features': [ { 'name': 'feature1', 'if': 'TEST_IF_FEATURE_1'} ] } { 'struct': 'CondFeatureStruct2', 'data': { 'foo': 'int' }, - 'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'}, - { 'name': 'feature2', 'if': 'defined(TEST_IF_FEATURE_2)'} ] } + 'features': [ { 'name': 'feature1', 'if': 'TEST_IF_FEATURE_1'}, + { 'name': 'feature2', 'if': 'TEST_IF_FEATURE_2'} ] } { 'struct': 'CondFeatureStruct3', 'data': { 'foo': 'int' }, 'features': [ { 'name': 'feature1', - 'if': { 'all': [ 'defined(TEST_IF_COND_1)', - 'defined(TEST_IF_COND_2)'] } } ] } + 'if': { 'all': [ 'TEST_IF_COND_1', + 'TEST_IF_COND_2'] } } ] } { 'struct': 'CondFeatureStruct4', 'data': { 'foo': 'int' }, 'features': [ { 'name': 'feature1', - 'if': {'any': ['defined(TEST_IF_COND_1)', - 'defined(TEST_IF_COND_2)'] } } ] } + 'if': {'any': ['TEST_IF_COND_1', + 'TEST_IF_COND_2'] } } ] } { 'enum': 'FeatureEnum1', 'data': [ 'eins', 'zwei', 'drei' ], @@ -331,14 +331,14 @@ 'features': [ 'feature1', 'feature2' ] } { 'command': 'test-command-cond-features1', - 'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'} ] } + 'features': [ { 'name': 'feature1', 'if': 'TEST_IF_FEATURE_1'} ] } { 'command': 'test-command-cond-features2', - 'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'}, - { 'name': 'feature2', 'if': 'defined(TEST_IF_FEATURE_2)'} ] } + 'features': [ { 'name': 'feature1', 'if': 'TEST_IF_FEATURE_1'}, + { 'name': 'feature2', 'if': 'TEST_IF_FEATURE_2'} ] } { 'command': 'test-command-cond-features3', 'features': [ { 'name': 'feature1', - 'if': { 'all': [ 'defined(TEST_IF_COND_1)', - 'defined(TEST_IF_COND_2)'] } } ] } + 'if': { 'all': [ 'TEST_IF_COND_1', + 'TEST_IF_COND_2'] } } ] } { 'event': 'TEST_EVENT_FEATURES0', 'data': 'FeatureStruct1' } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 53e12f3534..3d0c6a8f28 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -298,65 +298,65 @@ command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Unio object TestIfStruct member foo: int optional=False member bar: int optional=False - if defined(TEST_IF_STRUCT_BAR) - if defined(TEST_IF_STRUCT) + if TEST_IF_STRUCT_BAR + if TEST_IF_STRUCT enum TestIfEnum member foo member bar - if defined(TEST_IF_ENUM_BAR) - if defined(TEST_IF_ENUM) + if TEST_IF_ENUM_BAR + if TEST_IF_ENUM object q_obj_TestStruct-wrapper member data: TestStruct optional=False enum TestIfUnionKind member foo member bar - if defined(TEST_IF_UNION_BAR) - if OrderedDict([('all', ['defined(TEST_IF_UNION)', 'defined(TEST_IF_STRUCT)'])]) + if TEST_IF_UNION_BAR + if OrderedDict([('all', ['TEST_IF_UNION', 'TEST_IF_STRUCT'])]) object TestIfUnion member type: TestIfUnionKind optional=False tag type case foo: q_obj_TestStruct-wrapper case bar: q_obj_str-wrapper - if defined(TEST_IF_UNION_BAR) - if OrderedDict([('all', ['defined(TEST_IF_UNION)', 'defined(TEST_IF_STRUCT)'])]) + if TEST_IF_UNION_BAR + if OrderedDict([('all', ['TEST_IF_UNION', 'TEST_IF_STRUCT'])]) object q_obj_test-if-union-cmd-arg member union-cmd-arg: TestIfUnion optional=False - if defined(TEST_IF_UNION) + if TEST_IF_UNION command test-if-union-cmd q_obj_test-if-union-cmd-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False - if defined(TEST_IF_UNION) + if TEST_IF_UNION alternate TestIfAlternate tag type case foo: int case bar: TestStruct - if defined(TEST_IF_ALT_BAR) - if OrderedDict([('all', ['defined(TEST_IF_ALT)', 'defined(TEST_IF_STRUCT)'])]) + if TEST_IF_ALT_BAR + if OrderedDict([('all', ['TEST_IF_ALT', 'TEST_IF_STRUCT'])]) object q_obj_test-if-alternate-cmd-arg member alt-cmd-arg: TestIfAlternate optional=False - if OrderedDict([('all', ['defined(TEST_IF_ALT)', OrderedDict([('not', 'defined(TEST_IF_NOT_ALT)')])])]) + if OrderedDict([('all', ['TEST_IF_ALT', OrderedDict([('not', 'TEST_IF_NOT_ALT')])])]) command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False - if OrderedDict([('all', ['defined(TEST_IF_ALT)', OrderedDict([('not', 'defined(TEST_IF_NOT_ALT)')])])]) + if OrderedDict([('all', ['TEST_IF_ALT', OrderedDict([('not', 'TEST_IF_NOT_ALT')])])]) object q_obj_test-if-cmd-arg member foo: TestIfStruct optional=False member bar: TestIfEnum optional=False - if defined(TEST_IF_CMD_BAR) - if OrderedDict([('all', ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'])]) + if TEST_IF_CMD_BAR + if OrderedDict([('all', ['TEST_IF_CMD', 'TEST_IF_STRUCT'])]) command test-if-cmd q_obj_test-if-cmd-arg -> UserDefThree gen=True success_response=True boxed=False oob=False preconfig=False - if OrderedDict([('all', ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'])]) + if OrderedDict([('all', ['TEST_IF_CMD', 'TEST_IF_STRUCT'])]) command test-cmd-return-def-three None -> UserDefThree gen=True success_response=True boxed=False oob=False preconfig=False array TestIfEnumList TestIfEnum - if defined(TEST_IF_ENUM) + if TEST_IF_ENUM object q_obj_TEST_IF_EVENT-arg member foo: TestIfStruct optional=False member bar: TestIfEnumList optional=False - if defined(TEST_IF_EVT_BAR) - if OrderedDict([('all', ['defined(TEST_IF_EVT)', 'defined(TEST_IF_STRUCT)'])]) + if TEST_IF_EVT_BAR + if OrderedDict([('all', ['TEST_IF_EVT', 'TEST_IF_STRUCT'])]) event TEST_IF_EVENT q_obj_TEST_IF_EVENT-arg boxed=False - if OrderedDict([('all', ['defined(TEST_IF_EVT)', 'defined(TEST_IF_STRUCT)'])]) + if OrderedDict([('all', ['TEST_IF_EVT', 'TEST_IF_STRUCT'])]) object FeatureStruct0 member foo: int optional=False object FeatureStruct1 @@ -379,21 +379,21 @@ object FeatureStruct4 object CondFeatureStruct1 member foo: int optional=False feature feature1 - if defined(TEST_IF_FEATURE_1) + if TEST_IF_FEATURE_1 object CondFeatureStruct2 member foo: int optional=False feature feature1 - if defined(TEST_IF_FEATURE_1) + if TEST_IF_FEATURE_1 feature feature2 - if defined(TEST_IF_FEATURE_2) + if TEST_IF_FEATURE_2 object CondFeatureStruct3 member foo: int optional=False feature feature1 - if OrderedDict([('all', ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'])]) + if OrderedDict([('all', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])]) object CondFeatureStruct4 member foo: int optional=False feature feature1 - if OrderedDict([('any', ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'])]) + if OrderedDict([('any', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])]) enum FeatureEnum1 member eins member zwei @@ -434,17 +434,17 @@ command test-command-features3 None -> None command test-command-cond-features1 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 - if defined(TEST_IF_FEATURE_1) + if TEST_IF_FEATURE_1 command test-command-cond-features2 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 - if defined(TEST_IF_FEATURE_1) + if TEST_IF_FEATURE_1 feature feature2 - if defined(TEST_IF_FEATURE_2) + if TEST_IF_FEATURE_2 command test-command-cond-features3 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 - if OrderedDict([('all', ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'])]) + if OrderedDict([('all', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])]) event TEST_EVENT_FEATURES0 FeatureStruct1 boxed=False event TEST_EVENT_FEATURES1 None diff --git a/tests/qapi-schema/redefined-event.json b/tests/qapi-schema/redefined-event.json index 09eff18412..7901930e3d 100644 --- a/tests/qapi-schema/redefined-event.json +++ b/tests/qapi-schema/redefined-event.json @@ -1,3 +1,3 @@ # we reject duplicate events { 'event': 'EVENT_A', 'data': { 'myint': 'int' } } -{ 'event': 'EVENT_A', 'data': { 'myint': 'int' }, 'if': 'defined(FOO)' } +{ 'event': 'EVENT_A', 'data': { 'myint': 'int' }, 'if': 'FOO' } diff --git a/tests/qapi-schema/union-branch-if-invalid.err b/tests/qapi-schema/union-branch-if-invalid.err index dd4518233e..046187a5b9 100644 --- a/tests/qapi-schema/union-branch-if-invalid.err +++ b/tests/qapi-schema/union-branch-if-invalid.err @@ -1,2 +1,2 @@ union-branch-if-invalid.json: In union 'Uni': -union-branch-if-invalid.json:4: 'if' condition '' of 'data' member 'branch1' makes no sense +union-branch-if-invalid.json:4: 'if' condition '' of 'data' member 'branch1' is not a valid identifier From f9734d5d4078f17daf328b9e113aaffe3d00ecaf Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:53:53 +0200 Subject: [PATCH 094/493] error: Use error_fatal to simplify obvious fatal errors (again) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We did this with scripts/coccinelle/use-error_fatal.cocci before, in commit 50beeb68094 and 007b06578ab. This commit cleans up rarer variations that don't seem worth matching with Coccinelle. Cc: Thomas Huth Cc: Cornelia Huck Cc: Peter Xu Cc: Juan Quintela Cc: Stefan Hajnoczi Cc: Paolo Bonzini Cc: Marc-André Lureau Cc: Gerd Hoffmann Cc: Daniel P. Berrangé Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-2-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Peter Xu Acked-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé --- hw/s390x/ipl.c | 6 +----- migration/migration.c | 7 +------ qemu-img.c | 6 +----- qemu-io.c | 6 +----- qemu-nbd.c | 5 +---- scsi/qemu-pr-helper.c | 11 +++-------- softmmu/vl.c | 7 +------ target/i386/sev.c | 8 +------- ui/console.c | 6 ++---- ui/spice-core.c | 7 +------ 10 files changed, 13 insertions(+), 56 deletions(-) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 8c863cf386..1821c6faee 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -711,7 +711,6 @@ int s390_ipl_pv_unpack(void) void s390_ipl_prepare_cpu(S390CPU *cpu) { S390IPLState *ipl = get_ipl_device(); - Error *err = NULL; cpu->env.psw.addr = ipl->start_addr; cpu->env.psw.mask = IPL_PSW_MASK; @@ -723,10 +722,7 @@ void s390_ipl_prepare_cpu(S390CPU *cpu) } } if (ipl->netboot) { - if (load_netboot_image(&err) < 0) { - error_report_err(err); - exit(1); - } + load_netboot_image(&error_fatal); ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr); } s390_ipl_set_boot_menu(ipl); diff --git a/migration/migration.c b/migration/migration.c index 041b8451a6..b169943f35 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -188,8 +188,6 @@ static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp) void migration_object_init(void) { - Error *err = NULL; - /* This can only be called once. */ assert(!current_migration); current_migration = MIGRATION_OBJ(object_new(TYPE_MIGRATION)); @@ -210,10 +208,7 @@ void migration_object_init(void) qemu_mutex_init(¤t_incoming->page_request_mutex); current_incoming->page_requested = g_tree_new(page_request_addr_cmp); - if (!migration_object_check(current_migration, &err)) { - error_report_err(err); - exit(1); - } + migration_object_check(current_migration, &error_fatal); blk_mig_init(); ram_mig_init(); diff --git a/qemu-img.c b/qemu-img.c index 908fd0cce5..d77f3e76a9 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -5350,7 +5350,6 @@ int main(int argc, char **argv) { const img_cmd_t *cmd; const char *cmdname; - Error *local_error = NULL; int c; static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, @@ -5368,10 +5367,7 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_TRACE); qemu_init_exec_dir(argv[0]); - if (qemu_init_main_loop(&local_error)) { - error_report_err(local_error); - exit(EXIT_FAILURE); - } + qemu_init_main_loop(&error_fatal); qcrypto_init(&error_fatal); diff --git a/qemu-io.c b/qemu-io.c index 57f07501df..3924639b92 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -529,7 +529,6 @@ int main(int argc, char **argv) int flags = BDRV_O_UNMAP; int ret; bool writethrough = true; - Error *local_error = NULL; QDict *opts = NULL; const char *format = NULL; bool force_share = false; @@ -629,10 +628,7 @@ int main(int argc, char **argv) exit(1); } - if (qemu_init_main_loop(&local_error)) { - error_report_err(local_error); - exit(1); - } + qemu_init_main_loop(&error_fatal); if (!trace_init_backends()) { exit(1); diff --git a/qemu-nbd.c b/qemu-nbd.c index 26ffbf15af..65ebec598f 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -963,10 +963,7 @@ int main(int argc, char **argv) } } - if (qemu_init_main_loop(&local_err)) { - error_report_err(local_err); - exit(EXIT_FAILURE); - } + qemu_init_main_loop(&error_fatal); bdrv_init(); atexit(qemu_nbd_shutdown); diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index 7b9389b47b..f281daeced 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -1044,10 +1044,7 @@ int main(int argc, char **argv) } } - if (qemu_init_main_loop(&local_err)) { - error_report_err(local_err); - exit(EXIT_FAILURE); - } + qemu_init_main_loop(&error_fatal); server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc), G_IO_IN, @@ -1061,10 +1058,8 @@ int main(int argc, char **argv) } } - if ((daemonize || pidfile_specified) && - !qemu_write_pidfile(pidfile, &local_err)) { - error_report_err(local_err); - exit(EXIT_FAILURE); + if (daemonize || pidfile_specified) { + qemu_write_pidfile(pidfile, &error_fatal); } #ifdef CONFIG_LIBCAP_NG diff --git a/softmmu/vl.c b/softmmu/vl.c index 5ca11e7469..6227f8f10e 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -2694,12 +2694,7 @@ void qmp_x_exit_preconfig(Error **errp) qemu_machine_creation_done(); if (loadvm) { - Error *local_err = NULL; - if (!load_snapshot(loadvm, NULL, false, NULL, &local_err)) { - error_report_err(local_err); - autostart = 0; - exit(1); - } + load_snapshot(loadvm, NULL, false, NULL, &error_fatal); } if (replay_mode != REPLAY_MODE_NONE) { replay_vmstate_init(); diff --git a/target/i386/sev.c b/target/i386/sev.c index 83df8c09f6..0b2c8f594a 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -737,7 +737,6 @@ static void sev_launch_finish(SevGuestState *sev) { int ret, error; - Error *local_err = NULL; trace_kvm_sev_launch_finish(); ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error); @@ -752,12 +751,7 @@ sev_launch_finish(SevGuestState *sev) /* add migration blocker */ error_setg(&sev_mig_blocker, "SEV: Migration is not implemented"); - ret = migrate_add_blocker(sev_mig_blocker, &local_err); - if (local_err) { - error_report_err(local_err); - error_free(sev_mig_blocker); - exit(1); - } + migrate_add_blocker(sev_mig_blocker, &error_fatal); } static void diff --git a/ui/console.c b/ui/console.c index 1103b65314..5d2e6178ff 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1508,7 +1508,6 @@ void register_displaychangelistener(DisplayChangeListener *dcl) "This VM has no graphic display device."; static DisplaySurface *dummy; QemuConsole *con; - Error *err = NULL; assert(!dcl->ds); @@ -1523,9 +1522,8 @@ void register_displaychangelistener(DisplayChangeListener *dcl) dcl->con->gl = dcl; } - if (dcl->con && !dpy_compatible_with(dcl->con, dcl, &err)) { - error_report_err(err); - exit(1); + if (dcl->con) { + dpy_compatible_with(dcl->con, dcl, &error_fatal); } trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); diff --git a/ui/spice-core.c b/ui/spice-core.c index 0371055e6c..31974b8d6c 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -671,18 +671,13 @@ static void qemu_spice_init(void) } passwordSecret = qemu_opt_get(opts, "password-secret"); if (passwordSecret) { - Error *local_err = NULL; if (qemu_opt_get(opts, "password")) { error_report("'password' option is mutually exclusive with " "'password-secret'"); exit(1); } password = qcrypto_secret_lookup_as_utf8(passwordSecret, - &local_err); - if (!password) { - error_report_err(local_err); - exit(1); - } + &error_fatal); } else { str = qemu_opt_get(opts, "password"); if (str) { From e2ef4fc7aeb7e5856594f3172e08e6650011084a Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:53:54 +0200 Subject: [PATCH 095/493] spapr: Plug memory leak when we can't add a migration blocker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 2500fb423adb17995485de0b4d507cf2f09e3a7f Cc: Aravinda Prasad Cc: Ganesh Goudar Cc: David Gibson Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-3-armbru@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé --- hw/ppc/spapr_events.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 23e2e2fff1..690533cbdc 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -872,7 +872,6 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); CPUState *cs = CPU(cpu); int ret; - Error *local_err = NULL; if (spapr->fwnmi_machine_check_addr == -1) { /* Non-FWNMI case, deliver it like an architected CPU interrupt. */ @@ -912,7 +911,7 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) } } - ret = migrate_add_blocker(spapr->fwnmi_migration_blocker, &local_err); + ret = migrate_add_blocker(spapr->fwnmi_migration_blocker, NULL); if (ret == -EBUSY) { /* * We don't want to abort so we let the migration to continue. From d7f5013e122e14c6f5ac8d973e6567413cfa3790 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:53:55 +0200 Subject: [PATCH 096/493] spapr: Explain purpose of ->fwnmi_migration_blocker more clearly spapr_mce_req_event() makes an effort to prevent migration from degrading the reporting of FWNMIs. It adds a migration blocker when it receives one, and deletes it when it's done handling it. This is a best effort. Commit 2500fb423a "migration: Include migration support for machine check handling" tried to explain this in a comment. Rewrite the comment for clarity, and reposition it to make it clear it applies to all failure modes, not just "migration already in progress". Cc: David Gibson Cc: Aravinda Prasad Cc: Ganesh Goudar Cc: Dr. David Alan Gilbert Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-4-armbru@redhat.com> Acked-by: Michael S. Tsirkin --- hw/ppc/spapr_events.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 690533cbdc..630e86282c 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -911,16 +911,17 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) } } + /* + * Try to block migration while FWNMI is being handled, so the + * machine check handler runs where the information passed to it + * actually makes sense. This shouldn't actually block migration, + * only delay it slightly, assuming migration is retried. If the + * attempt to block fails, carry on. Unfortunately, it always + * fails when running with -only-migrate. A proper interface to + * delay migration completion for a bit could avoid that. + */ ret = migrate_add_blocker(spapr->fwnmi_migration_blocker, NULL); if (ret == -EBUSY) { - /* - * We don't want to abort so we let the migration to continue. - * In a rare case, the machine check handler will run on the target. - * Though this is not preferable, it is better than aborting - * the migration or killing the VM. It is okay to call - * migrate_del_blocker on a blocker that was not added (which the - * nmi-interlock handler would do when it's called after this). - */ warn_report("Received a fwnmi while migration was in progress"); } From 96ac9719331c0fd6e928b340f850b8cb617a3cea Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:53:56 +0200 Subject: [PATCH 097/493] multi-process: Fix pci_proxy_dev_realize() error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Error ** argument must be NULL, &error_abort, &error_fatal, or a pointer to a variable containing NULL. Passing an argument of the latter kind twice without clearing it in between is wrong: if the first call sets an error, it no longer points to NULL for the second call. pci_proxy_dev_realize() is wrong that way: it passes @errp to qio_channel_new_fd() without checking for failure. If it runs into another failure, it trips error_setv()'s assertion. Fix it to check for failure properly. Fixes: 9f8112073aad8e485ac012ee18809457ab7f23a6 Cc: Elena Ufimtseva Cc: Jagannathan Raman Cc: John G Johnson Cc: Stefan Hajnoczi Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-5-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Acked-by: Jagannathan Raman Acked-by: Michael S. Tsirkin --- hw/remote/proxy.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index 6dda705fc2..499f540c94 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -102,10 +102,18 @@ static void pci_proxy_dev_realize(PCIDevice *device, Error **errp) } dev->ioc = qio_channel_new_fd(fd, errp); + if (!dev->ioc) { + close(fd); + return; + } error_setg(&dev->migration_blocker, "%s does not support migration", TYPE_PCI_PROXY_DEV); - migrate_add_blocker(dev->migration_blocker, errp); + if (migrate_add_blocker(dev->migration_blocker, errp) < 0) { + error_free(dev->migration_blocker); + object_unref(dev->ioc); + return; + } qemu_mutex_init(&dev->io_mutex); qio_channel_set_blocking(dev->ioc, true, NULL); From aa6f7448ebef35249374508c503f53a8859d8a1e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:53:57 +0200 Subject: [PATCH 098/493] vhost-scsi: Plug memory leak on migrate_add_blocker() failure Cc: Michael S. Tsirkin Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-6-armbru@redhat.com> Acked-by: Michael S. Tsirkin --- hw/scsi/vhost-scsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 8c611bfd2d..039caf2614 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -208,7 +208,6 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) "target SCSI device state or use shared storage over network), " "set 'migratable' property to true to enable migration."); if (migrate_add_blocker(vsc->migration_blocker, errp) < 0) { - error_free(vsc->migration_blocker); goto free_virtio; } } @@ -233,11 +232,12 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) return; free_vqs: + g_free(vsc->dev.vqs); if (!vsc->migratable) { migrate_del_blocker(vsc->migration_blocker); } - g_free(vsc->dev.vqs); free_virtio: + error_free(vsc->migration_blocker); virtio_scsi_common_unrealize(dev); close_fd: close(vhostfd); From a5c051b2cf11197b1663dfb470e80dbc32d77cd0 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:53:58 +0200 Subject: [PATCH 099/493] i386: Never free migration blocker objects instead of sometimes invtsc_mig_blocker has static storage duration. When a CPU with certain features is initialized, and invtsc_mig_blocker is still null, we add a migration blocker and store it in invtsc_mig_blocker. The object is freed when migrate_add_blocker() fails, leaving invtsc_mig_blocker dangling. It is not freed on later failures. Same for hv_passthrough_mig_blocker and hv_no_nonarch_cs_mig_blocker. All failures are actually fatal, so whether we free or not doesn't really matter, except as bad examples to be copied / imitated. Clean this up in a minimal way: never free these blocker objects. Cc: Paolo Bonzini Cc: Marcelo Tosatti Cc: Eduardo Habkost Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-7-armbru@redhat.com> Acked-by: Michael S. Tsirkin --- target/i386/kvm/kvm.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index e69abe48e3..57aed525b5 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1437,7 +1437,6 @@ static int hyperv_init_vcpu(X86CPU *cpu) ret = migrate_add_blocker(hv_passthrough_mig_blocker, &local_err); if (local_err) { error_report_err(local_err); - error_free(hv_passthrough_mig_blocker); return ret; } } @@ -1452,7 +1451,6 @@ static int hyperv_init_vcpu(X86CPU *cpu) ret = migrate_add_blocker(hv_no_nonarch_cs_mig_blocker, &local_err); if (local_err) { error_report_err(local_err); - error_free(hv_no_nonarch_cs_mig_blocker); return ret; } } @@ -1892,7 +1890,6 @@ int kvm_arch_init_vcpu(CPUState *cs) r = migrate_add_blocker(invtsc_mig_blocker, &local_err); if (local_err) { error_report_err(local_err); - error_free(invtsc_mig_blocker); return r; } } From eb24a23e15580c2c6ab144a6bbf7f825506849ae Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:53:59 +0200 Subject: [PATCH 100/493] vfio: Avoid error_propagate() after migrate_add_blocker() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When migrate_add_blocker(blocker, &err) is followed by error_propagate(errp, err), we can often just as well do migrate_add_blocker(..., errp). This is the case in vfio_migration_probe(). Prior art: commit 386f6c07d2 "error: Avoid error_propagate() after migrate_add_blocker()". Cc: Kirti Wankhede Cc: Alex Williamson Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-8-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed by: Kirti Wankhede Acked-by: Michael S. Tsirkin --- hw/vfio/migration.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 82f654afb6..ff6b45de6b 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -858,7 +858,6 @@ int vfio_migration_probe(VFIODevice *vbasedev, Error **errp) { VFIOContainer *container = vbasedev->group->container; struct vfio_region_info *info = NULL; - Error *local_err = NULL; int ret = -ENOTSUP; if (!vbasedev->enable_migration || !container->dirty_pages_supported) { @@ -885,9 +884,8 @@ add_blocker: "VFIO device doesn't support migration"); g_free(info); - ret = migrate_add_blocker(vbasedev->migration_blocker, &local_err); - if (local_err) { - error_propagate(errp, local_err); + ret = migrate_add_blocker(vbasedev->migration_blocker, errp); + if (ret < 0) { error_free(vbasedev->migration_blocker); vbasedev->migration_blocker = NULL; } From 650126f838f763fe02f5b41584fd9f13cb8cbf13 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:54:00 +0200 Subject: [PATCH 101/493] whpx nvmm: Drop useless migrate_del_blocker() There is nothing to delete after migrate_add_blocker() failed. Trying anyway is safe, but useless. Don't. Cc: Sunil Muthuswamy Cc: Kamil Rytarowski Cc: Reinoud Zandijk Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-9-armbru@redhat.com> Reviewed-by: Reinoud Zandijk Acked-by: Michael S. Tsirkin --- target/i386/nvmm/nvmm-all.c | 1 - target/i386/whpx/whpx-all.c | 1 - 2 files changed, 2 deletions(-) diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index dfa690d65d..7bb0d9e30e 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -929,7 +929,6 @@ nvmm_init_vcpu(CPUState *cpu) (void)migrate_add_blocker(nvmm_migration_blocker, &local_error); if (local_error) { error_report_err(local_error); - migrate_del_blocker(nvmm_migration_blocker); error_free(nvmm_migration_blocker); return -EINVAL; } diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index f832f286ac..cc8c0b984b 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1349,7 +1349,6 @@ int whpx_init_vcpu(CPUState *cpu) (void)migrate_add_blocker(whpx_migration_blocker, &local_error); if (local_error) { error_report_err(local_error); - migrate_del_blocker(whpx_migration_blocker); error_free(whpx_migration_blocker); ret = -EINVAL; goto error; From 436c831a281ed950110dbc3e3baea24054c37298 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:54:01 +0200 Subject: [PATCH 102/493] migration: Unify failure check for migrate_add_blocker() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most callers check the return value. Some check whether it set an error. Functionally equivalent, but the former tends to be easier on the eyes, so do that everywhere. Prior art: commit c6ecec43b2 "qemu-option: Check return value instead of @err where convenient". Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-10-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Acked-by: Michael S. Tsirkin --- backends/tpm/tpm_emulator.c | 3 +-- hw/virtio/vhost.c | 2 +- target/i386/kvm/kvm.c | 6 +++--- target/i386/nvmm/nvmm-all.c | 3 +-- target/i386/whpx/whpx-all.c | 3 +-- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index e5f1063ab6..f8095d23d5 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -492,8 +492,7 @@ static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) error_setg(&tpm_emu->migration_blocker, "Migration disabled: TPM emulator does not support " "migration"); - migrate_add_blocker(tpm_emu->migration_blocker, &err); - if (err) { + if (migrate_add_blocker(tpm_emu->migration_blocker, &err) < 0) { error_report_err(err); error_free(tpm_emu->migration_blocker); tpm_emu->migration_blocker = NULL; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index e8f85a5d2d..dbbc6b6915 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1372,7 +1372,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, if (hdev->migration_blocker != NULL) { r = migrate_add_blocker(hdev->migration_blocker, errp); - if (*errp) { + if (r < 0) { error_free(hdev->migration_blocker); goto fail_busyloop; } diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 57aed525b5..500d2e0e68 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1435,7 +1435,7 @@ static int hyperv_init_vcpu(X86CPU *cpu) "'hv-passthrough' CPU flag prevents migration, use explicit" " set of hv-* flags instead"); ret = migrate_add_blocker(hv_passthrough_mig_blocker, &local_err); - if (local_err) { + if (ret < 0) { error_report_err(local_err); return ret; } @@ -1449,7 +1449,7 @@ static int hyperv_init_vcpu(X86CPU *cpu) " make sure SMT is disabled and/or that vCPUs are properly" " pinned)"); ret = migrate_add_blocker(hv_no_nonarch_cs_mig_blocker, &local_err); - if (local_err) { + if (ret < 0) { error_report_err(local_err); return ret; } @@ -1888,7 +1888,7 @@ int kvm_arch_init_vcpu(CPUState *cs) "State blocked by non-migratable CPU device" " (invtsc flag)"); r = migrate_add_blocker(invtsc_mig_blocker, &local_err); - if (local_err) { + if (r < 0) { error_report_err(local_err); return r; } diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 7bb0d9e30e..28dee4c5ee 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -926,8 +926,7 @@ nvmm_init_vcpu(CPUState *cpu) error_setg(&nvmm_migration_blocker, "NVMM: Migration not supported"); - (void)migrate_add_blocker(nvmm_migration_blocker, &local_error); - if (local_error) { + if (migrate_add_blocker(nvmm_migration_blocker, &local_error) < 0) { error_report_err(local_error); error_free(nvmm_migration_blocker); return -EINVAL; diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index cc8c0b984b..3e925b9da7 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1346,8 +1346,7 @@ int whpx_init_vcpu(CPUState *cpu) "State blocked due to non-migratable CPUID feature support," "dirty memory tracking support, and XSAVE/XRSTOR support"); - (void)migrate_add_blocker(whpx_migration_blocker, &local_error); - if (local_error) { + if (migrate_add_blocker(whpx_migration_blocker, &local_error) < 0) { error_report_err(local_error); error_free(whpx_migration_blocker); ret = -EINVAL; From 7d6f6933aa73a67e3ab6a70b0f17b9f48620bf13 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:54:02 +0200 Subject: [PATCH 103/493] migration: Handle migration_incoming_setup() errors consistently Commit b673eab4e2 "multifd: Make multifd_load_setup() get an Error parameter" changed migration_incoming_setup() to take an Error ** argument, and adjusted the callers accordingly. It neglected to change adjust multifd_load_setup(): it still exit()s on error. Clean that up. The error now gets propagated up two call chains: via migration_fd_process_incoming() to rdma_accept_incoming_migration(), and via migration_ioc_process_incoming() to migration_channel_process_incoming(). Both chain ends report the error with error_report_err(), but otherwise ignore it. Behavioral change: we no longer exit() on this error. This is consistent with how we handle other errors here, e.g. from multifd_recv_new_channel() via migration_ioc_process_incoming() to migration_channel_process_incoming(). Whether it's consistently right or consistently wrong I can't tell. Also clean up the return value from the unusual 0 on success, 1 on error to the more common true on success, false on error. Cc: Juan Quintela Cc: Dr. David Alan Gilbert Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-11-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Pankaj Gupta Acked-by: Michael S. Tsirkin --- migration/migration.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index b169943f35..bb909781b7 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -611,30 +611,25 @@ fail: } /** - * @migration_incoming_setup: Setup incoming migration - * - * Returns 0 for no error or 1 for error - * + * migration_incoming_setup: Setup incoming migration * @f: file for main migration channel * @errp: where to put errors + * + * Returns: %true on success, %false on error. */ -static int migration_incoming_setup(QEMUFile *f, Error **errp) +static bool migration_incoming_setup(QEMUFile *f, Error **errp) { MigrationIncomingState *mis = migration_incoming_get_current(); - Error *local_err = NULL; - if (multifd_load_setup(&local_err) != 0) { - /* We haven't been able to create multifd threads - nothing better to do */ - error_report_err(local_err); - exit(EXIT_FAILURE); + if (multifd_load_setup(errp) != 0) { + return false; } if (!mis->from_src_file) { mis->from_src_file = f; } qemu_file_set_blocking(f, false); - return 0; + return true; } void migration_incoming_process(void) @@ -677,14 +672,11 @@ static bool postcopy_try_recover(QEMUFile *f) void migration_fd_process_incoming(QEMUFile *f, Error **errp) { - Error *local_err = NULL; - if (postcopy_try_recover(f)) { return; } - if (migration_incoming_setup(f, &local_err)) { - error_propagate(errp, local_err); + if (!migration_incoming_setup(f, errp)) { return; } migration_incoming_process(); @@ -705,8 +697,7 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) return; } - if (migration_incoming_setup(f, &local_err)) { - error_propagate(errp, local_err); + if (!migration_incoming_setup(f, errp)) { return; } From 0d9a6654512a6b7ab18c53fc3f5a6bba8de2dde8 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:54:03 +0200 Subject: [PATCH 104/493] microvm: Drop dead error handling in microvm_machine_state_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stillborn in commit 0ebf007dda "hw/i386: Introduce the microvm machine type". Cc: Sergio Lopez Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-12-armbru@redhat.com> Reviewed-by: Sergio Lopez Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pankaj Gupta Acked-by: Michael S. Tsirkin --- hw/i386/microvm.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index aba0c83219..f257ec5a0b 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -458,15 +458,10 @@ static void microvm_machine_state_init(MachineState *machine) { MicrovmMachineState *mms = MICROVM_MACHINE(machine); X86MachineState *x86ms = X86_MACHINE(machine); - Error *local_err = NULL; microvm_memory_init(mms); x86_cpus_init(x86ms, CPU_VERSION_LATEST); - if (local_err) { - error_report_err(local_err); - exit(1); - } microvm_devices_init(mms); } From 66647ed4591572e7928cb5865909bb6c572d7e40 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:54:04 +0200 Subject: [PATCH 105/493] vhost: Clean up how VhostOpts method vhost_get_config() fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vhost_user_get_config() can fail without setting an error. Unclean. Its caller vhost_dev_get_config() compensates by substituting a generic error then. Goes back to commit 50de51387f "vhost: Distinguish errors in vhost_dev_get_config()". Clean up by moving the generic error from vhost_dev_get_config() to all the failure paths that neglect to set an error. Cc: Kevin Wolf Cc: Michael S. Tsirkin Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-13-armbru@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé [Sign of error_setg_errno()'s second argument fixed in both calls] --- hw/virtio/vhost-user.c | 2 ++ hw/virtio/vhost.c | 10 ++-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index aec6cc1990..229c114a19 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -2139,10 +2139,12 @@ static int vhost_user_get_config(struct vhost_dev *dev, uint8_t *config, msg.payload.config.offset = 0; msg.payload.config.size = config_len; if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + error_setg_errno(errp, EPROTO, "vhost_get_config failed"); return -EPROTO; } if (vhost_user_read(dev, &msg) < 0) { + error_setg_errno(errp, EPROTO, "vhost_get_config failed"); return -EPROTO; } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index dbbc6b6915..88f8a397dc 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1564,17 +1564,11 @@ void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits, int vhost_dev_get_config(struct vhost_dev *hdev, uint8_t *config, uint32_t config_len, Error **errp) { - ERRP_GUARD(); - int ret; - assert(hdev->vhost_ops); if (hdev->vhost_ops->vhost_get_config) { - ret = hdev->vhost_ops->vhost_get_config(hdev, config, config_len, errp); - if (ret < 0 && !*errp) { - error_setg_errno(errp, -ret, "vhost_get_config failed"); - } - return ret; + return hdev->vhost_ops->vhost_get_config(hdev, config, config_len, + errp); } error_setg(errp, "vhost_get_config not implemented"); From 998647dc8feb32b9b75a8afb834ce971ee36a426 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:54:05 +0200 Subject: [PATCH 106/493] vhost: Clean up how VhostOpts method vhost_backend_init() fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vhost_user_backend_init() can fail without setting an error. Unclean. Its caller vhost_dev_init() compensates by substituting a generic error then. Goes back to commit 28770ff935 "vhost: Distinguish errors in vhost_backend_init()". Clean up by moving the generic error from vhost_dev_init() to all the failure paths that neglect to set an error. Cc: Kevin Wolf Cc: Michael S. Tsirkin Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-14-armbru@redhat.com> Acked-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé --- hw/virtio/vhost-user.c | 6 ++++++ hw/virtio/vhost.c | 4 ---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 229c114a19..2407836fac 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -1876,6 +1876,7 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, err = vhost_user_get_features(dev, &features); if (err < 0) { + error_setg_errno(errp, -err, "vhost_backend_init failed"); return err; } @@ -1885,6 +1886,7 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, err = vhost_user_get_u64(dev, VHOST_USER_GET_PROTOCOL_FEATURES, &protocol_features); if (err < 0) { + error_setg_errno(errp, EPROTO, "vhost_backend_init failed"); return -EPROTO; } @@ -1903,6 +1905,7 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, err = vhost_user_set_protocol_features(dev, dev->protocol_features); if (err < 0) { + error_setg_errno(errp, EPROTO, "vhost_backend_init failed"); return -EPROTO; } @@ -1911,6 +1914,7 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, err = vhost_user_get_u64(dev, VHOST_USER_GET_QUEUE_NUM, &dev->max_queues); if (err < 0) { + error_setg_errno(errp, EPROTO, "vhost_backend_init failed"); return -EPROTO; } } else { @@ -1940,6 +1944,7 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, } else { err = vhost_user_get_max_memslots(dev, &ram_slots); if (err < 0) { + error_setg_errno(errp, EPROTO, "vhost_backend_init failed"); return -EPROTO; } @@ -1966,6 +1971,7 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, if (dev->vq_index == 0) { err = vhost_setup_slave_channel(dev); if (err < 0) { + error_setg_errno(errp, EPROTO, "vhost_backend_init failed"); return -EPROTO; } } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 88f8a397dc..3c0b537f89 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1289,7 +1289,6 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, VhostBackendType backend_type, uint32_t busyloop_timeout, Error **errp) { - ERRP_GUARD(); uint64_t features; int i, r, n_initialized_vqs = 0; @@ -1301,9 +1300,6 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, r = hdev->vhost_ops->vhost_backend_init(hdev, opaque, errp); if (r < 0) { - if (!*errp) { - error_setg_errno(errp, -r, "vhost_backend_init failed"); - } goto fail; } From fff0e451f3ed3b73224ca91c84fbb13aeae9d844 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:54:06 +0200 Subject: [PATCH 107/493] Remove superfluous ERRP_GUARD() Macro ERRP_GUARD() is only needed when we want to dereference @errp or pass it to error_prepend() or error_append_hint(). Delete superfluous ones. Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-15-armbru@redhat.com> Acked-by: Michael S. Tsirkin --- hw/remote/mpqemu-link.c | 3 --- qga/commands-posix-ssh.c | 17 ----------------- qga/commands-win32.c | 1 - ui/console.c | 1 - 4 files changed, 22 deletions(-) diff --git a/hw/remote/mpqemu-link.c b/hw/remote/mpqemu-link.c index e67a5de72c..7e841820e5 100644 --- a/hw/remote/mpqemu-link.c +++ b/hw/remote/mpqemu-link.c @@ -34,7 +34,6 @@ */ bool mpqemu_msg_send(MPQemuMsg *msg, QIOChannel *ioc, Error **errp) { - ERRP_GUARD(); bool iolock = qemu_mutex_iothread_locked(); bool iothread = qemu_in_iothread(); struct iovec send[2] = {}; @@ -97,7 +96,6 @@ bool mpqemu_msg_send(MPQemuMsg *msg, QIOChannel *ioc, Error **errp) static ssize_t mpqemu_read(QIOChannel *ioc, void *buf, size_t len, int **fds, size_t *nfds, Error **errp) { - ERRP_GUARD(); struct iovec iov = { .iov_base = buf, .iov_len = len }; bool iolock = qemu_mutex_iothread_locked(); bool iothread = qemu_in_iothread(); @@ -192,7 +190,6 @@ fail: uint64_t mpqemu_msg_send_and_await_reply(MPQemuMsg *msg, PCIProxyDev *pdev, Error **errp) { - ERRP_GUARD(); MPQemuMsg msg_reply = {0}; uint64_t ret = UINT64_MAX; diff --git a/qga/commands-posix-ssh.c b/qga/commands-posix-ssh.c index 2dda136d64..f3a580b8cc 100644 --- a/qga/commands-posix-ssh.c +++ b/qga/commands-posix-ssh.c @@ -45,8 +45,6 @@ get_passwd_entry(const char *username, Error **errp) g_autoptr(GError) err = NULL; struct passwd *p; - ERRP_GUARD(); - p = g_unix_get_passwd_entry_qemu(username, &err); if (p == NULL) { error_setg(errp, "failed to lookup user '%s': %s", @@ -61,8 +59,6 @@ static bool mkdir_for_user(const char *path, const struct passwd *p, mode_t mode, Error **errp) { - ERRP_GUARD(); - if (g_mkdir(path, mode) == -1) { error_setg(errp, "failed to create directory '%s': %s", path, g_strerror(errno)); @@ -87,8 +83,6 @@ mkdir_for_user(const char *path, const struct passwd *p, static bool check_openssh_pub_key(const char *key, Error **errp) { - ERRP_GUARD(); - /* simple sanity-check, we may want more? */ if (!key || key[0] == '#' || strchr(key, '\n')) { error_setg(errp, "invalid OpenSSH public key: '%s'", key); @@ -104,8 +98,6 @@ check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp) size_t n = 0; strList *k; - ERRP_GUARD(); - for (k = keys; k != NULL; k = k->next) { if (!check_openssh_pub_key(k->value, errp)) { return false; @@ -126,8 +118,6 @@ write_authkeys(const char *path, const GStrv keys, g_autofree char *contents = NULL; g_autoptr(GError) err = NULL; - ERRP_GUARD(); - contents = g_strjoinv("\n", keys); if (!g_file_set_contents(path, contents, -1, &err)) { error_setg(errp, "failed to write to '%s': %s", path, err->message); @@ -155,8 +145,6 @@ read_authkeys(const char *path, Error **errp) g_autoptr(GError) err = NULL; g_autofree char *contents = NULL; - ERRP_GUARD(); - if (!g_file_get_contents(path, &contents, NULL, &err)) { error_setg(errp, "failed to read '%s': %s", path, err->message); return NULL; @@ -178,7 +166,6 @@ qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys, strList *k; size_t nkeys, nauthkeys; - ERRP_GUARD(); reset = has_reset && reset; if (!check_openssh_pub_keys(keys, &nkeys, errp)) { @@ -228,8 +215,6 @@ qmp_guest_ssh_remove_authorized_keys(const char *username, strList *keys, GStrv a; size_t nkeys = 0; - ERRP_GUARD(); - if (!check_openssh_pub_keys(keys, NULL, errp)) { return; } @@ -277,8 +262,6 @@ qmp_guest_ssh_get_authorized_keys(const char *username, Error **errp) g_autoptr(GuestAuthorizedKeys) ret = NULL; int i; - ERRP_GUARD(); - p = get_passwd_entry(username, errp); if (p == NULL) { return NULL; diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 7bac0c5d42..4e84afd83b 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -976,7 +976,6 @@ out: GuestDiskInfoList *qmp_guest_get_disks(Error **errp) { - ERRP_GUARD(); GuestDiskInfoList *ret = NULL; HDEVINFO dev_info; SP_DEVICE_INTERFACE_DATA dev_iface_data; diff --git a/ui/console.c b/ui/console.c index 5d2e6178ff..eabbbc951c 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1481,7 +1481,6 @@ static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl) static bool dpy_compatible_with(QemuConsole *con, DisplayChangeListener *dcl, Error **errp) { - ERRP_GUARD(); int flags; flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0; From f9dfae9cb6b27649085f662a863f6167650402e0 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 20 Jul 2021 14:54:07 +0200 Subject: [PATCH 108/493] vl: Clean up -smp error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Error ** argument must be NULL, &error_abort, &error_fatal, or a pointer to a variable containing NULL. Passing an argument of the latter kind twice without clearing it in between is wrong: if the first call sets an error, it no longer points to NULL for the second call. machine_parse_property_opt() is wrong that way: it passes @errp to keyval_parse() without checking for failure, then passes it to keyval_merge(). Harmless, since the only caller passes &error_fatal. Clean up: drop the parameter, and use &error_fatal directly. Cc: Paolo Bonzini Signed-off-by: Markus Armbruster Message-Id: <20210720125408.387910-16-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Acked-by: Michael S. Tsirkin [Rebased, conflict with commit a3c2f128306 resolved] --- softmmu/vl.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/softmmu/vl.c b/softmmu/vl.c index 6227f8f10e..bdeb17809d 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -1550,20 +1550,17 @@ machine_merge_property(const char *propname, QDict *prop, Error **errp) static void machine_parse_property_opt(QemuOptsList *opts_list, const char *propname, - const char *arg, Error **errp) + const char *arg) { QDict *prop = NULL; bool help = false; - prop = keyval_parse(arg, opts_list->implied_opt_name, &help, errp); + prop = keyval_parse(arg, opts_list->implied_opt_name, &help, &error_fatal); if (help) { qemu_opts_print_help(opts_list, true); exit(0); } - if (!prop) { - return; - } - machine_merge_property(propname, prop, errp); + machine_merge_property(propname, prop, &error_fatal); qobject_unref(prop); } @@ -3343,7 +3340,8 @@ void qemu_init(int argc, char **argv, char **envp) } break; case QEMU_OPTION_smp: - machine_parse_property_opt(qemu_find_opts("smp-opts"), "smp", optarg, &error_fatal); + machine_parse_property_opt(qemu_find_opts("smp-opts"), + "smp", optarg); break; case QEMU_OPTION_vnc: vnc_parse(optarg); From 34a3a71db619c46f2a9ab76257d296d9c8b43aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Aug 2021 18:34:19 +0200 Subject: [PATCH 109/493] hw/arm/xlnx-zynqmp: Realize qspi controller *after* qspi_dma MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we link QOM object (a) as a property of QOM object (b), we must set the property *before* (b) is realized. Move QSPI realization *after* QSPI DMA. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20210819163422.2863447-2-philmd@redhat.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zynqmp.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 3597e8db4d..9724978761 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -570,26 +570,6 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) g_free(bus_name); } - if (!sysbus_realize(SYS_BUS_DEVICE(&s->qspi), errp)) { - return; - } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 0, QSPI_ADDR); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 1, LQSPI_ADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi), 0, gic_spi[QSPI_IRQ]); - - for (i = 0; i < XLNX_ZYNQMP_NUM_QSPI_BUS; i++) { - gchar *bus_name; - gchar *target_bus; - - /* Alias controller SPI bus to the SoC itself */ - bus_name = g_strdup_printf("qspi%d", i); - target_bus = g_strdup_printf("spi%d", i); - object_property_add_alias(OBJECT(s), bus_name, - OBJECT(&s->qspi), target_bus); - g_free(bus_name); - g_free(target_bus); - } - if (!sysbus_realize(SYS_BUS_DEVICE(&s->dp), errp)) { return; } @@ -646,8 +626,26 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi_dma), 0, QSPI_DMA_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi_dma), 0, gic_spi[QSPI_IRQ]); - object_property_set_link(OBJECT(&s->qspi), "stream-connected-dma", - OBJECT(&s->qspi_dma), errp); + + if (!object_property_set_link(OBJECT(&s->qspi), "stream-connected-dma", + OBJECT(&s->qspi_dma), errp)) { + return; + } + if (!sysbus_realize(SYS_BUS_DEVICE(&s->qspi), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 0, QSPI_ADDR); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 1, LQSPI_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi), 0, gic_spi[QSPI_IRQ]); + + for (i = 0; i < XLNX_ZYNQMP_NUM_QSPI_BUS; i++) { + g_autofree gchar *bus_name = g_strdup_printf("qspi%d", i); + g_autofree gchar *target_bus = g_strdup_printf("spi%d", i); + + /* Alias controller SPI bus to the SoC itself */ + object_property_add_alias(OBJECT(s), bus_name, + OBJECT(&s->qspi), target_bus); + } } static Property xlnx_zynqmp_props[] = { From 348ba7bede1513f7e5aba0b755380d2ff1720192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Aug 2021 18:34:20 +0200 Subject: [PATCH 110/493] hw/dma/xlnx_csu_dma: Run trivial checks early in realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If some property are not set, we'll return indicating a failure, so it is pointless to allocate / initialize some fields too early. Move the trivial checks earlier in realize(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20210819163422.2863447-3-philmd@redhat.com Signed-off-by: Peter Maydell --- hw/dma/xlnx_csu_dma.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index 797b4fed8f..2d19f415ef 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -626,6 +626,11 @@ static void xlnx_csu_dma_realize(DeviceState *dev, Error **errp) XlnxCSUDMA *s = XLNX_CSU_DMA(dev); RegisterInfoArray *reg_array; + if (!s->is_dst && !s->tx_dev) { + error_setg(errp, "zynqmp.csu-dma: Stream not connected"); + return; + } + reg_array = register_init_block32(dev, xlnx_csu_dma_regs_info[!!s->is_dst], XLNX_CSU_DMA_R_MAX, @@ -640,11 +645,6 @@ static void xlnx_csu_dma_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); - if (!s->is_dst && !s->tx_dev) { - error_setg(errp, "zynqmp.csu-dma: Stream not connected"); - return; - } - s->src_timer = ptimer_init(xlnx_csu_dma_src_timeout_hit, s, PTIMER_POLICY_DEFAULT); From c31b7f59014252e8de02597ee3af956259bc0d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Aug 2021 18:34:21 +0200 Subject: [PATCH 111/493] hw/dma/xlnx_csu_dma: Always expect 'dma' link property to be set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify by always passing a MemoryRegion property to the device. Doing so we can move the AddressSpace field to the device struct, removing need for heap allocation. Update the Xilinx ZynqMP SoC model to pass the default system memory instead of a NULL value. Suggested-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20210819163422.2863447-4-philmd@redhat.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zynqmp.c | 4 ++++ hw/dma/xlnx_csu_dma.c | 21 ++++++++++----------- include/hw/dma/xlnx_csu_dma.h | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 9724978761..4344e223f2 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -620,6 +620,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) gic_spi[adma_ch_intr[i]]); } + if (!object_property_set_link(OBJECT(&s->qspi_dma), "dma", + OBJECT(system_memory), errp)) { + return; + } if (!sysbus_realize(SYS_BUS_DEVICE(&s->qspi_dma), errp)) { return; } diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index 2d19f415ef..896bb3574d 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -201,11 +201,11 @@ static uint32_t xlnx_csu_dma_read(XlnxCSUDMA *s, uint8_t *buf, uint32_t len) for (i = 0; i < len && (result == MEMTX_OK); i += s->width) { uint32_t mlen = MIN(len - i, s->width); - result = address_space_rw(s->dma_as, addr, s->attr, + result = address_space_rw(&s->dma_as, addr, s->attr, buf + i, mlen, false); } } else { - result = address_space_rw(s->dma_as, addr, s->attr, buf, len, false); + result = address_space_rw(&s->dma_as, addr, s->attr, buf, len, false); } if (result == MEMTX_OK) { @@ -232,12 +232,12 @@ static uint32_t xlnx_csu_dma_write(XlnxCSUDMA *s, uint8_t *buf, uint32_t len) for (i = 0; i < len && (result == MEMTX_OK); i += s->width) { uint32_t mlen = MIN(len - i, s->width); - result = address_space_rw(s->dma_as, addr, s->attr, + result = address_space_rw(&s->dma_as, addr, s->attr, buf, mlen, true); buf += mlen; } } else { - result = address_space_rw(s->dma_as, addr, s->attr, buf, len, true); + result = address_space_rw(&s->dma_as, addr, s->attr, buf, len, true); } if (result != MEMTX_OK) { @@ -631,6 +631,12 @@ static void xlnx_csu_dma_realize(DeviceState *dev, Error **errp) return; } + if (!s->dma_mr) { + error_setg(errp, TYPE_XLNX_CSU_DMA " 'dma' link not set"); + return; + } + address_space_init(&s->dma_as, s->dma_mr, "csu-dma"); + reg_array = register_init_block32(dev, xlnx_csu_dma_regs_info[!!s->is_dst], XLNX_CSU_DMA_R_MAX, @@ -648,13 +654,6 @@ static void xlnx_csu_dma_realize(DeviceState *dev, Error **errp) s->src_timer = ptimer_init(xlnx_csu_dma_src_timeout_hit, s, PTIMER_POLICY_DEFAULT); - if (s->dma_mr) { - s->dma_as = g_malloc0(sizeof(AddressSpace)); - address_space_init(s->dma_as, s->dma_mr, NULL); - } else { - s->dma_as = &address_space_memory; - } - s->attr = MEMTXATTRS_UNSPECIFIED; s->r_size_last_word = 0; diff --git a/include/hw/dma/xlnx_csu_dma.h b/include/hw/dma/xlnx_csu_dma.h index 204d94c673..9e9dc551e9 100644 --- a/include/hw/dma/xlnx_csu_dma.h +++ b/include/hw/dma/xlnx_csu_dma.h @@ -30,7 +30,7 @@ typedef struct XlnxCSUDMA { MemoryRegion iomem; MemTxAttrs attr; MemoryRegion *dma_mr; - AddressSpace *dma_as; + AddressSpace dma_as; qemu_irq irq; StreamSink *tx_dev; /* Used as generic StreamSink */ ptimer_state *src_timer; From 783dbab19fb79eee2b59c23043ca555d996cb91b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Aug 2021 18:34:22 +0200 Subject: [PATCH 112/493] hw/dma/xlnx-zdma Always expect 'dma' link property to be set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify by always passing a MemoryRegion property to the device. Doing so we can move the AddressSpace field to the device struct, removing need for heap allocation. Update the Xilinx ZynqMP / Versal SoC models to pass the default system memory instead of a NULL value. Suggested-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-id: 20210819163422.2863447-5-philmd@redhat.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 2 ++ hw/arm/xlnx-zynqmp.c | 8 ++++++++ hw/dma/xlnx-zdma.c | 24 ++++++++++++------------ include/hw/dma/xlnx-zdma.h | 2 +- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index fb776834f7..d60eb4fb18 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -218,6 +218,8 @@ static void versal_create_admas(Versal *s, qemu_irq *pic) TYPE_XLNX_ZDMA); dev = DEVICE(&s->lpd.iou.adma[i]); object_property_set_int(OBJECT(dev), "bus-width", 128, &error_abort); + object_property_set_link(OBJECT(dev), "dma", + OBJECT(get_system_memory()), &error_fatal); sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 4344e223f2..6cfce26210 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -601,6 +601,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) errp)) { return; } + if (!object_property_set_link(OBJECT(&s->gdma[i]), "dma", + OBJECT(system_memory), errp)) { + return; + } if (!sysbus_realize(SYS_BUS_DEVICE(&s->gdma[i]), errp)) { return; } @@ -611,6 +615,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } for (i = 0; i < XLNX_ZYNQMP_NUM_ADMA_CH; i++) { + if (!object_property_set_link(OBJECT(&s->adma[i]), "dma", + OBJECT(system_memory), errp)) { + return; + } if (!sysbus_realize(SYS_BUS_DEVICE(&s->adma[i]), errp)) { return; } diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index fa38a55634..a5a92b4ff8 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -320,9 +320,9 @@ static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, return false; } - descr->addr = address_space_ldq_le(s->dma_as, addr, s->attr, NULL); - descr->size = address_space_ldl_le(s->dma_as, addr + 8, s->attr, NULL); - descr->attr = address_space_ldl_le(s->dma_as, addr + 12, s->attr, NULL); + descr->addr = address_space_ldq_le(&s->dma_as, addr, s->attr, NULL); + descr->size = address_space_ldl_le(&s->dma_as, addr + 8, s->attr, NULL); + descr->attr = address_space_ldl_le(&s->dma_as, addr + 12, s->attr, NULL); return true; } @@ -354,7 +354,7 @@ static void zdma_update_descr_addr(XlnxZDMA *s, bool type, } else { addr = zdma_get_regaddr64(s, basereg); addr += sizeof(s->dsc_dst); - next = address_space_ldq_le(s->dma_as, addr, s->attr, NULL); + next = address_space_ldq_le(&s->dma_as, addr, s->attr, NULL); } zdma_put_regaddr64(s, basereg, next); @@ -421,7 +421,7 @@ static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len) } } - address_space_write(s->dma_as, s->dsc_dst.addr, s->attr, buf, dlen); + address_space_write(&s->dma_as, s->dsc_dst.addr, s->attr, buf, dlen); if (burst_type == AXI_BURST_INCR) { s->dsc_dst.addr += dlen; } @@ -497,7 +497,7 @@ static void zdma_process_descr(XlnxZDMA *s) len = s->cfg.bus_width / 8; } } else { - address_space_read(s->dma_as, src_addr, s->attr, s->buf, len); + address_space_read(&s->dma_as, src_addr, s->attr, s->buf, len); if (burst_type == AXI_BURST_INCR) { src_addr += len; } @@ -765,6 +765,12 @@ static void zdma_realize(DeviceState *dev, Error **errp) XlnxZDMA *s = XLNX_ZDMA(dev); unsigned int i; + if (!s->dma_mr) { + error_setg(errp, TYPE_XLNX_ZDMA " 'dma' link not set"); + return; + } + address_space_init(&s->dma_as, s->dma_mr, "zdma-dma"); + for (i = 0; i < ARRAY_SIZE(zdma_regs_info); ++i) { RegisterInfo *r = &s->regs_info[zdma_regs_info[i].addr / 4]; @@ -777,12 +783,6 @@ static void zdma_realize(DeviceState *dev, Error **errp) }; } - if (s->dma_mr) { - s->dma_as = g_malloc0(sizeof(AddressSpace)); - address_space_init(s->dma_as, s->dma_mr, NULL); - } else { - s->dma_as = &address_space_memory; - } s->attr = MEMTXATTRS_UNSPECIFIED; } diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h index 6602e7ffa7..efc75217d5 100644 --- a/include/hw/dma/xlnx-zdma.h +++ b/include/hw/dma/xlnx-zdma.h @@ -56,7 +56,7 @@ struct XlnxZDMA { MemoryRegion iomem; MemTxAttrs attr; MemoryRegion *dma_mr; - AddressSpace *dma_as; + AddressSpace dma_as; qemu_irq irq_zdma_ch_imr; struct { From 0659e4680efa5b26244dceb29fb2db754ceaf8af Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Thu, 19 Aug 2021 21:56:37 +0530 Subject: [PATCH 113/493] hw/arm/Kconfig: no need to enable ACPI_MEMORY_HOTPLUG/ACPI_NVDIMM explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 36b79e3219d ("hw/acpi/Kconfig: Add missing Kconfig dependencies (build error)"), ACPI_MEMORY_HOTPLUG and ACPI_NVDIMM is implicitly turned on when ACPI_HW_REDUCED is selected. ACPI_HW_REDUCED is already enabled. No need to turn on ACPI_MEMORY_HOTPLUG or ACPI_NVDIMM explicitly. This is a minor cleanup. Signed-off-by: Ani Sinha Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210819162637.518507-1-ani@anisinha.ca Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 4ba0aca067..dc050b5c37 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -25,9 +25,7 @@ config ARM_VIRT select ACPI_PCI select MEM_DEVICE select DIMM - select ACPI_MEMORY_HOTPLUG select ACPI_HW_REDUCED - select ACPI_NVDIMM select ACPI_APEI config CHEETAH From 5401b1e08d468d28de1a7f433062f338fc47bad9 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 23 Aug 2021 18:06:44 +0200 Subject: [PATCH 114/493] target/arm/cpu: Introduce sve_vq_supported bitmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow CPUs that support SVE to specify which SVE vector lengths they support by setting them in this bitmap. Currently only the 'max' and 'host' CPU types supports SVE and 'host' requires KVM which obtains its supported bitmap from the host. So, we only need to initialize the bitmap for 'max' with TCG. And, since 'max' should support all SVE vector lengths we simply fill the bitmap. Future CPU types may have less trivial maps though. Signed-off-by: Andrew Jones Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20210823160647.34028-2-drjones@redhat.com Signed-off-by: Peter Maydell --- target/arm/cpu.h | 4 ++++ target/arm/cpu64.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 5cf8996ae3..1060825c74 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1020,9 +1020,13 @@ struct ARMCPU { * While processing properties during initialization, corresponding * sve_vq_init bits are set for bits in sve_vq_map that have been * set by properties. + * + * Bits set in sve_vq_supported represent valid vector lengths for + * the CPU type. */ DECLARE_BITMAP(sve_vq_map, ARM_MAX_VQ); DECLARE_BITMAP(sve_vq_init, ARM_MAX_VQ); + DECLARE_BITMAP(sve_vq_supported, ARM_MAX_VQ); /* Generic timer counter frequency, in Hz */ uint64_t gt_cntfrq_hz; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index c690318a9b..eb9318c83b 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -840,6 +840,8 @@ static void aarch64_max_initfn(Object *obj) /* Default to PAUTH on, with the architected algorithm. */ qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_property); qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_impdef_property); + + bitmap_fill(cpu->sve_vq_supported, ARM_MAX_VQ); } aarch64_add_sve_properties(obj); From 927703cc40fb86b10bd7bb5bb0efa8da69fc6db6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 23 Aug 2021 18:06:45 +0200 Subject: [PATCH 115/493] target/arm/kvm64: Ensure sve vls map is completely clear MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bitmap_clear() only clears the given range. While the given range should be sufficient in this case we might as well be 100% sure all bits are zeroed by using bitmap_zero(). Signed-off-by: Andrew Jones Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20210823160647.34028-3-drjones@redhat.com Signed-off-by: Peter Maydell --- target/arm/kvm64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 59982d470d..e790d6c9a5 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -740,7 +740,7 @@ void kvm_arm_sve_get_vls(CPUState *cs, unsigned long *map) uint32_t vq = 0; int i, j; - bitmap_clear(map, 0, ARM_MAX_VQ); + bitmap_zero(map, ARM_MAX_VQ); /* * KVM ensures all host CPUs support the same set of vector lengths. From 5b65e5abeacae691cd13f1dadf7666e63d8b48a6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 23 Aug 2021 18:06:46 +0200 Subject: [PATCH 116/493] target/arm/cpu64: Replace kvm_supported with sve_vq_supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have an ARMCPU member sve_vq_supported we no longer need the local kvm_supported bitmap for KVM's supported vector lengths. Signed-off-by: Andrew Jones Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20210823160647.34028-4-drjones@redhat.com Signed-off-by: Peter Maydell --- target/arm/cpu64.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index eb9318c83b..557fd47577 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -265,14 +265,17 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) * any of the above. Finally, if SVE is not disabled, then at least one * vector length must be enabled. */ - DECLARE_BITMAP(kvm_supported, ARM_MAX_VQ); DECLARE_BITMAP(tmp, ARM_MAX_VQ); uint32_t vq, max_vq = 0; - /* Collect the set of vector lengths supported by KVM. */ - bitmap_zero(kvm_supported, ARM_MAX_VQ); + /* + * CPU models specify a set of supported vector lengths which are + * enabled by default. Attempting to enable any vector length not set + * in the supported bitmap results in an error. When KVM is enabled we + * fetch the supported bitmap from the host. + */ if (kvm_enabled() && kvm_arm_sve_supported()) { - kvm_arm_sve_get_vls(CPU(cpu), kvm_supported); + kvm_arm_sve_get_vls(CPU(cpu), cpu->sve_vq_supported); } else if (kvm_enabled()) { assert(!cpu_isar_feature(aa64_sve, cpu)); } @@ -299,7 +302,7 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) * For KVM we have to automatically enable all supported unitialized * lengths, even when the smaller lengths are not all powers-of-two. */ - bitmap_andnot(tmp, kvm_supported, cpu->sve_vq_init, max_vq); + bitmap_andnot(tmp, cpu->sve_vq_supported, cpu->sve_vq_init, max_vq); bitmap_or(cpu->sve_vq_map, cpu->sve_vq_map, tmp, max_vq); } else { /* Propagate enabled bits down through required powers-of-two. */ @@ -322,12 +325,12 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) /* Disabling a supported length disables all larger lengths. */ for (vq = 1; vq <= ARM_MAX_VQ; ++vq) { if (test_bit(vq - 1, cpu->sve_vq_init) && - test_bit(vq - 1, kvm_supported)) { + test_bit(vq - 1, cpu->sve_vq_supported)) { break; } } max_vq = vq <= ARM_MAX_VQ ? vq - 1 : ARM_MAX_VQ; - bitmap_andnot(cpu->sve_vq_map, kvm_supported, + bitmap_andnot(cpu->sve_vq_map, cpu->sve_vq_supported, cpu->sve_vq_init, max_vq); if (max_vq == 0 || bitmap_empty(cpu->sve_vq_map, max_vq)) { error_setg(errp, "cannot disable sve%d", vq * 128); @@ -392,7 +395,7 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) if (kvm_enabled()) { /* Ensure the set of lengths matches what KVM supports. */ - bitmap_xor(tmp, cpu->sve_vq_map, kvm_supported, max_vq); + bitmap_xor(tmp, cpu->sve_vq_map, cpu->sve_vq_supported, max_vq); if (!bitmap_empty(tmp, max_vq)) { vq = find_last_bit(tmp, max_vq) + 1; if (test_bit(vq - 1, cpu->sve_vq_map)) { From 022707e5d617de2811d9447887ce67e187258613 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 23 Aug 2021 18:06:47 +0200 Subject: [PATCH 117/493] target/arm/cpu64: Validate sve vector lengths are supported Future CPU types may specify which vector lengths are supported. We can apply nearly the same logic to validate those lengths as we do for KVM's supported vector lengths. We merge the code where we can, but unfortunately can't completely merge it because KVM requires all vector lengths, power-of-two or not, smaller than the maximum enabled length to also be enabled. The architecture only requires all the power-of-two lengths, though, so TCG will only enforce that. Signed-off-by: Andrew Jones Reviewed-by: Richard Henderson Message-id: 20210823160647.34028-5-drjones@redhat.com Signed-off-by: Peter Maydell --- target/arm/cpu64.c | 101 ++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 56 deletions(-) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 557fd47577..2f0cbddab5 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -329,35 +329,26 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) break; } } - max_vq = vq <= ARM_MAX_VQ ? vq - 1 : ARM_MAX_VQ; - bitmap_andnot(cpu->sve_vq_map, cpu->sve_vq_supported, - cpu->sve_vq_init, max_vq); - if (max_vq == 0 || bitmap_empty(cpu->sve_vq_map, max_vq)) { - error_setg(errp, "cannot disable sve%d", vq * 128); - error_append_hint(errp, "Disabling sve%d results in all " - "vector lengths being disabled.\n", - vq * 128); - error_append_hint(errp, "With SVE enabled, at least one " - "vector length must be enabled.\n"); - return; - } } else { /* Disabling a power-of-two disables all larger lengths. */ - if (test_bit(0, cpu->sve_vq_init)) { - error_setg(errp, "cannot disable sve128"); - error_append_hint(errp, "Disabling sve128 results in all " - "vector lengths being disabled.\n"); - error_append_hint(errp, "With SVE enabled, at least one " - "vector length must be enabled.\n"); - return; - } - for (vq = 2; vq <= ARM_MAX_VQ; vq <<= 1) { + for (vq = 1; vq <= ARM_MAX_VQ; vq <<= 1) { if (test_bit(vq - 1, cpu->sve_vq_init)) { break; } } - max_vq = vq <= ARM_MAX_VQ ? vq - 1 : ARM_MAX_VQ; - bitmap_complement(cpu->sve_vq_map, cpu->sve_vq_init, max_vq); + } + + max_vq = vq <= ARM_MAX_VQ ? vq - 1 : ARM_MAX_VQ; + bitmap_andnot(cpu->sve_vq_map, cpu->sve_vq_supported, + cpu->sve_vq_init, max_vq); + if (max_vq == 0 || bitmap_empty(cpu->sve_vq_map, max_vq)) { + error_setg(errp, "cannot disable sve%d", vq * 128); + error_append_hint(errp, "Disabling sve%d results in all " + "vector lengths being disabled.\n", + vq * 128); + error_append_hint(errp, "With SVE enabled, at least one " + "vector length must be enabled.\n"); + return; } max_vq = find_last_bit(cpu->sve_vq_map, max_vq) + 1; @@ -393,46 +384,44 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) assert(max_vq != 0); bitmap_clear(cpu->sve_vq_map, max_vq, ARM_MAX_VQ - max_vq); - if (kvm_enabled()) { - /* Ensure the set of lengths matches what KVM supports. */ - bitmap_xor(tmp, cpu->sve_vq_map, cpu->sve_vq_supported, max_vq); - if (!bitmap_empty(tmp, max_vq)) { - vq = find_last_bit(tmp, max_vq) + 1; - if (test_bit(vq - 1, cpu->sve_vq_map)) { - if (cpu->sve_max_vq) { - error_setg(errp, "cannot set sve-max-vq=%d", - cpu->sve_max_vq); - error_append_hint(errp, "This KVM host does not support " - "the vector length %d-bits.\n", - vq * 128); - error_append_hint(errp, "It may not be possible to use " - "sve-max-vq with this KVM host. Try " - "using only sve properties.\n"); - } else { - error_setg(errp, "cannot enable sve%d", vq * 128); - error_append_hint(errp, "This KVM host does not support " - "the vector length %d-bits.\n", - vq * 128); - } + /* Ensure the set of lengths matches what is supported. */ + bitmap_xor(tmp, cpu->sve_vq_map, cpu->sve_vq_supported, max_vq); + if (!bitmap_empty(tmp, max_vq)) { + vq = find_last_bit(tmp, max_vq) + 1; + if (test_bit(vq - 1, cpu->sve_vq_map)) { + if (cpu->sve_max_vq) { + error_setg(errp, "cannot set sve-max-vq=%d", cpu->sve_max_vq); + error_append_hint(errp, "This CPU does not support " + "the vector length %d-bits.\n", vq * 128); + error_append_hint(errp, "It may not be possible to use " + "sve-max-vq with this CPU. Try " + "using only sve properties.\n"); } else { + error_setg(errp, "cannot enable sve%d", vq * 128); + error_append_hint(errp, "This CPU does not support " + "the vector length %d-bits.\n", vq * 128); + } + return; + } else { + if (kvm_enabled()) { error_setg(errp, "cannot disable sve%d", vq * 128); error_append_hint(errp, "The KVM host requires all " "supported vector lengths smaller " "than %d bits to also be enabled.\n", max_vq * 128); - } - return; - } - } else { - /* Ensure all required powers-of-two are enabled. */ - for (vq = pow2floor(max_vq); vq >= 1; vq >>= 1) { - if (!test_bit(vq - 1, cpu->sve_vq_map)) { - error_setg(errp, "cannot disable sve%d", vq * 128); - error_append_hint(errp, "sve%d is required as it " - "is a power-of-two length smaller than " - "the maximum, sve%d\n", - vq * 128, max_vq * 128); return; + } else { + /* Ensure all required powers-of-two are enabled. */ + for (vq = pow2floor(max_vq); vq >= 1; vq >>= 1) { + if (!test_bit(vq - 1, cpu->sve_vq_map)) { + error_setg(errp, "cannot disable sve%d", vq * 128); + error_append_hint(errp, "sve%d is required as it " + "is a power-of-two length smaller " + "than the maximum, sve%d\n", + vq * 128, max_vq * 128); + return; + } + } } } } From 444fa22549434331db70718f073432ed2057ada8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 27 Jul 2021 18:04:10 +0100 Subject: [PATCH 118/493] docs/specs/acpu_cpu_hotplug: Convert to rST Do a basic conversion of the acpi_cpu_hotplug spec document to rST. Signed-off-by: Peter Maydell Reviewed-by: Igor Mammedov Message-id: 20210727170414.3368-2-peter.maydell@linaro.org --- docs/specs/acpi_cpu_hotplug.rst | 235 ++++++++++++++++++++++++++++++++ docs/specs/acpi_cpu_hotplug.txt | 160 ---------------------- docs/specs/index.rst | 1 + 3 files changed, 236 insertions(+), 160 deletions(-) create mode 100644 docs/specs/acpi_cpu_hotplug.rst delete mode 100644 docs/specs/acpi_cpu_hotplug.txt diff --git a/docs/specs/acpi_cpu_hotplug.rst b/docs/specs/acpi_cpu_hotplug.rst new file mode 100644 index 0000000000..351057c967 --- /dev/null +++ b/docs/specs/acpi_cpu_hotplug.rst @@ -0,0 +1,235 @@ +QEMU<->ACPI BIOS CPU hotplug interface +====================================== + +QEMU supports CPU hotplug via ACPI. This document +describes the interface between QEMU and the ACPI BIOS. + +ACPI BIOS GPE.2 handler is dedicated for notifying OS about CPU hot-add +and hot-remove events. + + +Legacy ACPI CPU hotplug interface registers +------------------------------------------- + +CPU present bitmap for: + +- ICH9-LPC (IO port 0x0cd8-0xcf7, 1-byte access) +- PIIX-PM (IO port 0xaf00-0xaf1f, 1-byte access) +- One bit per CPU. Bit position reflects corresponding CPU APIC ID. Read-only. +- The first DWORD in bitmap is used in write mode to switch from legacy + to modern CPU hotplug interface, write 0 into it to do switch. + +QEMU sets corresponding CPU bit on hot-add event and issues SCI +with GPE.2 event set. CPU present map is read by ACPI BIOS GPE.2 handler +to notify OS about CPU hot-add events. CPU hot-remove isn't supported. + + +Modern ACPI CPU hotplug interface registers +------------------------------------------- + +Register block base address: + +- ICH9-LPC IO port 0x0cd8 +- PIIX-PM IO port 0xaf00 + +Register block size: + +- ACPI_CPU_HOTPLUG_REG_LEN = 12 + +All accesses to registers described below, imply little-endian byte order. + +Reserved registers behavior: + +- write accesses are ignored +- read accesses return all bits set to 0. + +The last stored value in 'CPU selector' must refer to a possible CPU, otherwise + +- reads from any register return 0 +- writes to any other register are ignored until valid value is stored into it + +On QEMU start, 'CPU selector' is initialized to a valid value, on reset it +keeps the current value. + +Read access behavior +^^^^^^^^^^^^^^^^^^^^ + +offset [0x0-0x3] + Command data 2: (DWORD access) + + If value last stored in 'Command field' is: + + 0: + reads as 0x0 + 3: + upper 32 bits of architecture specific CPU ID value + other values: + reserved + +offset [0x4] + CPU device status fields: (1 byte access) + + bits: + + 0: + Device is enabled and may be used by guest + 1: + Device insert event, used to distinguish device for which + no device check event to OSPM was issued. + It's valid only when bit 0 is set. + 2: + Device remove event, used to distinguish device for which + no device eject request to OSPM was issued. Firmware must + ignore this bit. + 3: + reserved and should be ignored by OSPM + 4: + if set to 1, OSPM requests firmware to perform device eject. + 5-7: + reserved and should be ignored by OSPM + +offset [0x5-0x7] + reserved + +offset [0x8] + Command data: (DWORD access) + + If value last stored in 'Command field' is one of: + + 0: + contains 'CPU selector' value of a CPU with pending event[s] + 3: + lower 32 bits of architecture specific CPU ID value + (in x86 case: APIC ID) + otherwise: + contains 0 + +Write access behavior +^^^^^^^^^^^^^^^^^^^^^ + +offset [0x0-0x3] + CPU selector: (DWORD access) + + Selects active CPU device. All following accesses to other + registers will read/store data from/to selected CPU. + Valid values: [0 .. max_cpus) + +offset [0x4] + CPU device control fields: (1 byte access) + + bits: + + 0: + reserved, OSPM must clear it before writing to register. + 1: + if set to 1 clears device insert event, set by OSPM + after it has emitted device check event for the + selected CPU device + 2: + if set to 1 clears device remove event, set by OSPM + after it has emitted device eject request for the + selected CPU device. + 3: + if set to 1 initiates device eject, set by OSPM when it + triggers CPU device removal and calls _EJ0 method or by firmware + when bit #4 is set. In case bit #4 were set, it's cleared as + part of device eject. + 4: + if set to 1, OSPM hands over device eject to firmware. + Firmware shall issue device eject request as described above + (bit #3) and OSPM should not touch device eject bit (#3) in case + it's asked firmware to perform CPU device eject. + 5-7: + reserved, OSPM must clear them before writing to register + +offset[0x5] + Command field: (1 byte access) + + value: + + 0: + selects a CPU device with inserting/removing events and + following reads from 'Command data' register return + selected CPU ('CPU selector' value). + If no CPU with events found, the current 'CPU selector' doesn't + change and corresponding insert/remove event flags are not modified. + + 1: + following writes to 'Command data' register set OST event + register in QEMU + 2: + following writes to 'Command data' register set OST status + register in QEMU + 3: + following reads from 'Command data' and 'Command data 2' return + architecture specific CPU ID value for currently selected CPU. + other values: + reserved + +offset [0x6-0x7] + reserved + +offset [0x8] + Command data: (DWORD access) + + If last stored 'Command field' value is: + + 1: + stores value into OST event register + 2: + stores value into OST status register, triggers + ACPI_DEVICE_OST QMP event from QEMU to external applications + with current values of OST event and status registers. + other values: + reserved + +Typical usecases +---------------- + +(x86) Detecting and enabling modern CPU hotplug interface +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +QEMU starts with legacy CPU hotplug interface enabled. Detecting and +switching to modern interface is based on the 2 legacy CPU hotplug features: + +#. Writes into CPU bitmap are ignored. +#. CPU bitmap always has bit #0 set, corresponding to boot CPU. + +Use following steps to detect and enable modern CPU hotplug interface: + +#. Store 0x0 to the 'CPU selector' register, attempting to switch to modern mode +#. Store 0x0 to the 'CPU selector' register, to ensure valid selector value +#. Store 0x0 to the 'Command field' register +#. Read the 'Command data 2' register. + If read value is 0x0, the modern interface is enabled. + Otherwise legacy or no CPU hotplug interface available + +Get a cpu with pending event +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#. Store 0x0 to the 'CPU selector' register. +#. Store 0x0 to the 'Command field' register. +#. Read the 'CPU device status fields' register. +#. If both bit #1 and bit #2 are clear in the value read, there is no CPU + with a pending event and selected CPU remains unchanged. +#. Otherwise, read the 'Command data' register. The value read is the + selector of the CPU with the pending event (which is already selected). + +Enumerate CPUs present/non present CPUs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#. Set the present CPU count to 0. +#. Set the iterator to 0. +#. Store 0x0 to the 'CPU selector' register, to ensure that it's in + a valid state and that access to other registers won't be ignored. +#. Store 0x0 to the 'Command field' register to make 'Command data' + register return 'CPU selector' value of selected CPU +#. Read the 'CPU device status fields' register. +#. If bit #0 is set, increment the present CPU count. +#. Increment the iterator. +#. Store the iterator to the 'CPU selector' register. +#. Read the 'Command data' register. +#. If the value read is not zero, goto 05. +#. Otherwise store 0x0 to the 'CPU selector' register, to put it + into a valid state and exit. + The iterator at this point equals "max_cpus". diff --git a/docs/specs/acpi_cpu_hotplug.txt b/docs/specs/acpi_cpu_hotplug.txt deleted file mode 100644 index 9bd59ae0da..0000000000 --- a/docs/specs/acpi_cpu_hotplug.txt +++ /dev/null @@ -1,160 +0,0 @@ -QEMU<->ACPI BIOS CPU hotplug interface --------------------------------------- - -QEMU supports CPU hotplug via ACPI. This document -describes the interface between QEMU and the ACPI BIOS. - -ACPI BIOS GPE.2 handler is dedicated for notifying OS about CPU hot-add -and hot-remove events. - -============================================ -Legacy ACPI CPU hotplug interface registers: --------------------------------------------- -CPU present bitmap for: - ICH9-LPC (IO port 0x0cd8-0xcf7, 1-byte access) - PIIX-PM (IO port 0xaf00-0xaf1f, 1-byte access) - One bit per CPU. Bit position reflects corresponding CPU APIC ID. Read-only. - The first DWORD in bitmap is used in write mode to switch from legacy - to modern CPU hotplug interface, write 0 into it to do switch. ---------------------------------------------------------------- -QEMU sets corresponding CPU bit on hot-add event and issues SCI -with GPE.2 event set. CPU present map is read by ACPI BIOS GPE.2 handler -to notify OS about CPU hot-add events. CPU hot-remove isn't supported. - -===================================== -Modern ACPI CPU hotplug interface registers: -------------------------------------- -Register block base address: - ICH9-LPC IO port 0x0cd8 - PIIX-PM IO port 0xaf00 -Register block size: - ACPI_CPU_HOTPLUG_REG_LEN = 12 - -All accesses to registers described below, imply little-endian byte order. - -Reserved resisters behavior: - - write accesses are ignored - - read accesses return all bits set to 0. - -The last stored value in 'CPU selector' must refer to a possible CPU, otherwise - - reads from any register return 0 - - writes to any other register are ignored until valid value is stored into it -On QEMU start, 'CPU selector' is initialized to a valid value, on reset it -keeps the current value. - -read access: - offset: - [0x0-0x3] Command data 2: (DWORD access) - if value last stored in 'Command field': - 0: reads as 0x0 - 3: upper 32 bits of architecture specific CPU ID value - other values: reserved - [0x4] CPU device status fields: (1 byte access) - bits: - 0: Device is enabled and may be used by guest - 1: Device insert event, used to distinguish device for which - no device check event to OSPM was issued. - It's valid only when bit 0 is set. - 2: Device remove event, used to distinguish device for which - no device eject request to OSPM was issued. Firmware must - ignore this bit. - 3: reserved and should be ignored by OSPM - 4: if set to 1, OSPM requests firmware to perform device eject. - 5-7: reserved and should be ignored by OSPM - [0x5-0x7] reserved - [0x8] Command data: (DWORD access) - contains 0 unless value last stored in 'Command field' is one of: - 0: contains 'CPU selector' value of a CPU with pending event[s] - 3: lower 32 bits of architecture specific CPU ID value - (in x86 case: APIC ID) - -write access: - offset: - [0x0-0x3] CPU selector: (DWORD access) - selects active CPU device. All following accesses to other - registers will read/store data from/to selected CPU. - Valid values: [0 .. max_cpus) - [0x4] CPU device control fields: (1 byte access) - bits: - 0: reserved, OSPM must clear it before writing to register. - 1: if set to 1 clears device insert event, set by OSPM - after it has emitted device check event for the - selected CPU device - 2: if set to 1 clears device remove event, set by OSPM - after it has emitted device eject request for the - selected CPU device. - 3: if set to 1 initiates device eject, set by OSPM when it - triggers CPU device removal and calls _EJ0 method or by firmware - when bit #4 is set. In case bit #4 were set, it's cleared as - part of device eject. - 4: if set to 1, OSPM hands over device eject to firmware. - Firmware shall issue device eject request as described above - (bit #3) and OSPM should not touch device eject bit (#3) in case - it's asked firmware to perform CPU device eject. - 5-7: reserved, OSPM must clear them before writing to register - [0x5] Command field: (1 byte access) - value: - 0: selects a CPU device with inserting/removing events and - following reads from 'Command data' register return - selected CPU ('CPU selector' value). - If no CPU with events found, the current 'CPU selector' doesn't - change and corresponding insert/remove event flags are not modified. - 1: following writes to 'Command data' register set OST event - register in QEMU - 2: following writes to 'Command data' register set OST status - register in QEMU - 3: following reads from 'Command data' and 'Command data 2' return - architecture specific CPU ID value for currently selected CPU. - other values: reserved - [0x6-0x7] reserved - [0x8] Command data: (DWORD access) - if last stored 'Command field' value: - 1: stores value into OST event register - 2: stores value into OST status register, triggers - ACPI_DEVICE_OST QMP event from QEMU to external applications - with current values of OST event and status registers. - other values: reserved - -Typical usecases: - - (x86) Detecting and enabling modern CPU hotplug interface. - QEMU starts with legacy CPU hotplug interface enabled. Detecting and - switching to modern interface is based on the 2 legacy CPU hotplug features: - 1. Writes into CPU bitmap are ignored. - 2. CPU bitmap always has bit#0 set, corresponding to boot CPU. - - Use following steps to detect and enable modern CPU hotplug interface: - 1. Store 0x0 to the 'CPU selector' register, - attempting to switch to modern mode - 2. Store 0x0 to the 'CPU selector' register, - to ensure valid selector value - 3. Store 0x0 to the 'Command field' register, - 4. Read the 'Command data 2' register. - If read value is 0x0, the modern interface is enabled. - Otherwise legacy or no CPU hotplug interface available - - - Get a cpu with pending event - 1. Store 0x0 to the 'CPU selector' register. - 2. Store 0x0 to the 'Command field' register. - 3. Read the 'CPU device status fields' register. - 4. If both bit#1 and bit#2 are clear in the value read, there is no CPU - with a pending event and selected CPU remains unchanged. - 5. Otherwise, read the 'Command data' register. The value read is the - selector of the CPU with the pending event (which is already - selected). - - - Enumerate CPUs present/non present CPUs - 01. Set the present CPU count to 0. - 02. Set the iterator to 0. - 03. Store 0x0 to the 'CPU selector' register, to ensure that it's in - a valid state and that access to other registers won't be ignored. - 04. Store 0x0 to the 'Command field' register to make 'Command data' - register return 'CPU selector' value of selected CPU - 05. Read the 'CPU device status fields' register. - 06. If bit#0 is set, increment the present CPU count. - 07. Increment the iterator. - 08. Store the iterator to the 'CPU selector' register. - 09. Read the 'Command data' register. - 10. If the value read is not zero, goto 05. - 11. Otherwise store 0x0 to the 'CPU selector' register, to put it - into a valid state and exit. - The iterator at this point equals "max_cpus". diff --git a/docs/specs/index.rst b/docs/specs/index.rst index b7b08ea30d..24b765e1a4 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -13,3 +13,4 @@ guest hardware that is specific to QEMU. acpi_hw_reduced_hotplug tpm acpi_hest_ghes + acpi_cpu_hotplug From 615a55827c590d75bff8a660fc20675fea6c73d3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 27 Jul 2021 18:04:11 +0100 Subject: [PATCH 119/493] docs/specs/acpi_mem_hotplug: Convert to rST Convert the acpi memory hotplug spec to rST. Note that this includes converting a lot of weird whitespace characters to plain old spaces (the rST parser does not like whatever the old ones were). Signed-off-by: Peter Maydell Reviewed-by: Igor Mammedov Message-id: 20210727170414.3368-3-peter.maydell@linaro.org --- docs/specs/acpi_mem_hotplug.rst | 128 ++++++++++++++++++++++++++++++++ docs/specs/acpi_mem_hotplug.txt | 94 ----------------------- docs/specs/index.rst | 1 + 3 files changed, 129 insertions(+), 94 deletions(-) create mode 100644 docs/specs/acpi_mem_hotplug.rst delete mode 100644 docs/specs/acpi_mem_hotplug.txt diff --git a/docs/specs/acpi_mem_hotplug.rst b/docs/specs/acpi_mem_hotplug.rst new file mode 100644 index 0000000000..069819bc3e --- /dev/null +++ b/docs/specs/acpi_mem_hotplug.rst @@ -0,0 +1,128 @@ +QEMU<->ACPI BIOS memory hotplug interface +========================================= + +ACPI BIOS GPE.3 handler is dedicated for notifying OS about memory hot-add +and hot-remove events. + +Memory hot-plug interface (IO port 0xa00-0xa17, 1-4 byte access) +---------------------------------------------------------------- + +Read access behavior +^^^^^^^^^^^^^^^^^^^^ + +[0x0-0x3] + Lo part of memory device phys address +[0x4-0x7] + Hi part of memory device phys address +[0x8-0xb] + Lo part of memory device size in bytes +[0xc-0xf] + Hi part of memory device size in bytes +[0x10-0x13] + Memory device proximity domain +[0x14] + Memory device status fields + + bits: + + 0: + Device is enabled and may be used by guest + 1: + Device insert event, used to distinguish device for which + no device check event to OSPM was issued. + It's valid only when bit 1 is set. + 2: + Device remove event, used to distinguish device for which + no device eject request to OSPM was issued. + 3-7: + reserved and should be ignored by OSPM + +[0x15-0x17] + reserved + +Write access behavior +^^^^^^^^^^^^^^^^^^^^^ + + +[0x0-0x3] + Memory device slot selector, selects active memory device. + All following accesses to other registers in 0xa00-0xa17 + region will read/store data from/to selected memory device. +[0x4-0x7] + OST event code reported by OSPM +[0x8-0xb] + OST status code reported by OSPM +[0xc-0x13] + reserved, writes into it are ignored +[0x14] + Memory device control fields + + bits: + + 0: + reserved, OSPM must clear it before writing to register. + Due to BUG in versions prior 2.4 that field isn't cleared + when other fields are written. Keep it reserved and don't + try to reuse it. + 1: + if set to 1 clears device insert event, set by OSPM + after it has emitted device check event for the + selected memory device + 2: + if set to 1 clears device remove event, set by OSPM + after it has emitted device eject request for the + selected memory device + 3: + if set to 1 initiates device eject, set by OSPM when it + triggers memory device removal and calls _EJ0 method + 4-7: + reserved, OSPM must clear them before writing to register + +Selecting memory device slot beyond present range has no effect on platform: + +- write accesses to memory hot-plug registers not documented above are ignored +- read accesses to memory hot-plug registers not documented above return + all bits set to 1. + +Memory hot remove process diagram +--------------------------------- + +:: + + +-------------+ +-----------------------+ +------------------+ + | 1. QEMU | | 2. QEMU | |3. QEMU | + | device_del +---->+ device unplug request +----->+Send SCI to guest,| + | | | cb | |return control to | + | | | | |management | + +-------------+ +-----------------------+ +------------------+ + + +---------------------------------------------------------------------+ + + +---------------------+ +-------------------------+ + | OSPM: | remove event | OSPM: | + | send Eject Request, | | Scan memory devices | + | clear remove event +<-------------+ for event flags | + | | | | + +---------------------+ +-------------------------+ + | + | + +---------v--------+ +-----------------------+ + | Guest OS: | success | OSPM: | + | process Ejection +----------->+ Execute _EJ0 method, | + | request | | set eject bit in flags| + +------------------+ +-----------------------+ + |failure | + v v + +------------------------+ +-----------------------+ + | OSPM: | | QEMU: | + | set OST event & status | | call device unplug cb | + | fields | | | + +------------------------+ +-----------------------+ + | | + v v + +------------------+ +-------------------+ + |QEMU: | |QEMU: | + |Send OST QMP event| |Send device deleted| + | | |QMP event | + +------------------+ | | + +-------------------+ diff --git a/docs/specs/acpi_mem_hotplug.txt b/docs/specs/acpi_mem_hotplug.txt deleted file mode 100644 index 3df3620ce4..0000000000 --- a/docs/specs/acpi_mem_hotplug.txt +++ /dev/null @@ -1,94 +0,0 @@ -QEMU<->ACPI BIOS memory hotplug interface --------------------------------------- - -ACPI BIOS GPE.3 handler is dedicated for notifying OS about memory hot-add -and hot-remove events. - -Memory hot-plug interface (IO port 0xa00-0xa17, 1-4 byte access): ---------------------------------------------------------------- -0xa00: - read access: - [0x0-0x3] Lo part of memory device phys address - [0x4-0x7] Hi part of memory device phys address - [0x8-0xb] Lo part of memory device size in bytes - [0xc-0xf] Hi part of memory device size in bytes - [0x10-0x13] Memory device proximity domain - [0x14] Memory device status fields - bits: - 0: Device is enabled and may be used by guest - 1: Device insert event, used to distinguish device for which - no device check event to OSPM was issued. - It's valid only when bit 1 is set. - 2: Device remove event, used to distinguish device for which - no device eject request to OSPM was issued. - 3-7: reserved and should be ignored by OSPM - [0x15-0x17] reserved - - write access: - [0x0-0x3] Memory device slot selector, selects active memory device. - All following accesses to other registers in 0xa00-0xa17 - region will read/store data from/to selected memory device. - [0x4-0x7] OST event code reported by OSPM - [0x8-0xb] OST status code reported by OSPM - [0xc-0x13] reserved, writes into it are ignored - [0x14] Memory device control fields - bits: - 0: reserved, OSPM must clear it before writing to register. - Due to BUG in versions prior 2.4 that field isn't cleared - when other fields are written. Keep it reserved and don't - try to reuse it. - 1: if set to 1 clears device insert event, set by OSPM - after it has emitted device check event for the - selected memory device - 2: if set to 1 clears device remove event, set by OSPM - after it has emitted device eject request for the - selected memory device - 3: if set to 1 initiates device eject, set by OSPM when it - triggers memory device removal and calls _EJ0 method - 4-7: reserved, OSPM must clear them before writing to register - -Selecting memory device slot beyond present range has no effect on platform: - - write accesses to memory hot-plug registers not documented above are - ignored - - read accesses to memory hot-plug registers not documented above return - all bits set to 1. - -Memory hot remove process diagram: ----------------------------------- - +-------------+     +-----------------------+      +------------------+      - |  1. QEMU    |     | 2. QEMU               |      |3. QEMU           |      - |  device_del +---->+ device unplug request +----->+Send SCI to guest,|      - |             |     |         cb            |      |return control to |      - +-------------+     +-----------------------+      |management        |      -                                                    +------------------+      -                                                                              - +---------------------------------------------------------------------+      -                                                                              - +---------------------+              +-------------------------+             - | OSPM:               | remove event | OSPM:                   |             - | send Eject Request, |              | Scan memory devices     |             - | clear remove event  +<-------------+ for event flags         |             - |                     |              |                         |             - +---------------------+              +-------------------------+             -           |                                                                  -           |                                                                  - +---------v--------+            +-----------------------+                    - | Guest OS:        |  success   | OSPM:                 |                    - | process Ejection +----------->+ Execute _EJ0 method,  |                    - | request          |            | set eject bit in flags|                    - +------------------+            +-----------------------+                    -           |failure                         |                                 -           v                                v                                 - +------------------------+      +-----------------------+                    - | OSPM:                  |      | QEMU:                 |                    - | set OST event & status |      | call device unplug cb |                    - | fields                 |      |                       |                    - +------------------------+      +-----------------------+                    -          |                                  |                                -          v                                  v                                - +------------------+              +-------------------+                      - |QEMU:             |              |QEMU:              |                      - |Send OST QMP event|              |Send device deleted|                      - |                  |              |QMP event          |                      - +------------------+              |                   |                      -                                   +-------------------+ diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 24b765e1a4..17cc7868b9 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -14,3 +14,4 @@ guest hardware that is specific to QEMU. tpm acpi_hest_ghes acpi_cpu_hotplug + acpi_mem_hotplug From f054eb1c920671fe1055a62714087ec05aa09348 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 27 Jul 2021 18:04:12 +0100 Subject: [PATCH 120/493] docs/specs/acpi_pci_hotplug: Convert to rST Convert the PCI hotplug spec document to rST. Signed-off-by: Peter Maydell Reviewed-by: Igor Mammedov --- ...i_pci_hotplug.txt => acpi_pci_hotplug.rst} | 37 ++++++++++--------- docs/specs/index.rst | 1 + 2 files changed, 21 insertions(+), 17 deletions(-) rename docs/specs/{acpi_pci_hotplug.txt => acpi_pci_hotplug.rst} (51%) diff --git a/docs/specs/acpi_pci_hotplug.txt b/docs/specs/acpi_pci_hotplug.rst similarity index 51% rename from docs/specs/acpi_pci_hotplug.txt rename to docs/specs/acpi_pci_hotplug.rst index a839434f31..685bc5c322 100644 --- a/docs/specs/acpi_pci_hotplug.txt +++ b/docs/specs/acpi_pci_hotplug.rst @@ -1,45 +1,48 @@ QEMU<->ACPI BIOS PCI hotplug interface --------------------------------------- +====================================== QEMU supports PCI hotplug via ACPI, for PCI bus 0. This document describes the interface between QEMU and the ACPI BIOS. -ACPI GPE block (IO ports 0xafe0-0xafe3, byte access): ------------------------------------------ +ACPI GPE block (IO ports 0xafe0-0xafe3, byte access) +---------------------------------------------------- Generic ACPI GPE block. Bit 1 (GPE.1) used to notify PCI hotplug/eject event to ACPI BIOS, via SCI interrupt. -PCI slot injection notification pending (IO port 0xae00-0xae03, 4-byte access): ---------------------------------------------------------------- +PCI slot injection notification pending (IO port 0xae00-0xae03, 4-byte access) +------------------------------------------------------------------------------ + Slot injection notification pending. One bit per slot. Read by ACPI BIOS GPE.1 handler to notify OS of injection events. Read-only. -PCI slot removal notification (IO port 0xae04-0xae07, 4-byte access): ------------------------------------------------------ +PCI slot removal notification (IO port 0xae04-0xae07, 4-byte access) +-------------------------------------------------------------------- + Slot removal notification pending. One bit per slot. Read by ACPI BIOS GPE.1 handler to notify OS of removal events. Read-only. -PCI device eject (IO port 0xae08-0xae0b, 4-byte access): ----------------------------------------- +PCI device eject (IO port 0xae08-0xae0b, 4-byte access) +------------------------------------------------------- Write: Used by ACPI BIOS _EJ0 method to request device removal. One bit per slot. Read: Hotplug features register. Used by platform to identify features available. Current base feature set (no bits set): - - Read-only "up" register @0xae00, 4-byte access, bit per slot - - Read-only "down" register @0xae04, 4-byte access, bit per slot - - Read/write "eject" register @0xae08, 4-byte access, - write: bit per slot eject, read: hotplug feature set - - Read-only hotplug capable register @0xae0c, 4-byte access, bit per slot -PCI removability status (IO port 0xae0c-0xae0f, 4-byte access): ------------------------------------------------ +- Read-only "up" register @0xae00, 4-byte access, bit per slot +- Read-only "down" register @0xae04, 4-byte access, bit per slot +- Read/write "eject" register @0xae08, 4-byte access, + write: bit per slot eject, read: hotplug feature set +- Read-only hotplug capable register @0xae0c, 4-byte access, bit per slot + +PCI removability status (IO port 0xae0c-0xae0f, 4-byte access) +-------------------------------------------------------------- Used by ACPI BIOS _RMV method to indicate removability status to OS. One -bit per slot. Read-only +bit per slot. Read-only. diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 17cc7868b9..8296fb19b7 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -15,3 +15,4 @@ guest hardware that is specific to QEMU. acpi_hest_ghes acpi_cpu_hotplug acpi_mem_hotplug + acpi_pci_hotplug From 50f8174c5c12e887693fb17fc99cfd802192e0fa Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 27 Jul 2021 18:04:13 +0100 Subject: [PATCH 121/493] docs/specs/acpi_nvdimm: Convert to rST Convert the ACPI NVDIMM spec document to rST. Signed-off-by: Peter Maydell Reviewed-by: Igor Mammedov Message-id: 20210727170414.3368-5-peter.maydell@linaro.org --- docs/specs/acpi_nvdimm.rst | 228 +++++++++++++++++++++++++++++++++++++ docs/specs/acpi_nvdimm.txt | 188 ------------------------------ docs/specs/index.rst | 1 + 3 files changed, 229 insertions(+), 188 deletions(-) create mode 100644 docs/specs/acpi_nvdimm.rst delete mode 100644 docs/specs/acpi_nvdimm.txt diff --git a/docs/specs/acpi_nvdimm.rst b/docs/specs/acpi_nvdimm.rst new file mode 100644 index 0000000000..ab0335253d --- /dev/null +++ b/docs/specs/acpi_nvdimm.rst @@ -0,0 +1,228 @@ +QEMU<->ACPI BIOS NVDIMM interface +================================= + +QEMU supports NVDIMM via ACPI. This document describes the basic concepts of +NVDIMM ACPI and the interface between QEMU and the ACPI BIOS. + +NVDIMM ACPI Background +---------------------- + +NVDIMM is introduced in ACPI 6.0 which defines an NVDIMM root device under +_SB scope with a _HID of "ACPI0012". For each NVDIMM present or intended +to be supported by platform, platform firmware also exposes an ACPI +Namespace Device under the root device. + +The NVDIMM child devices under the NVDIMM root device are defined with _ADR +corresponding to the NFIT device handle. The NVDIMM root device and the +NVDIMM devices can have device specific methods (_DSM) to provide additional +functions specific to a particular NVDIMM implementation. + +This is an example from ACPI 6.0, a platform contains one NVDIMM:: + + Scope (\_SB){ + Device (NVDR) // Root device + { + Name (_HID, "ACPI0012") + Method (_STA) {...} + Method (_FIT) {...} + Method (_DSM, ...) {...} + Device (NVD) + { + Name(_ADR, h) //where h is NFIT Device Handle for this NVDIMM + Method (_DSM, ...) {...} + } + } + } + +Methods supported on both NVDIMM root device and NVDIMM device +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +_DSM (Device Specific Method) + It is a control method that enables devices to provide device specific + control functions that are consumed by the device driver. + The NVDIMM DSM specification can be found at + http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf + + Arguments: + + Arg0 + A Buffer containing a UUID (16 Bytes) + Arg1 + An Integer containing the Revision ID (4 Bytes) + Arg2 + An Integer containing the Function Index (4 Bytes) + Arg3 + A package containing parameters for the function specified by the + UUID, Revision ID, and Function Index + + Return Value: + + If Function Index = 0, a Buffer containing a function index bitfield. + Otherwise, the return value and type depends on the UUID, revision ID + and function index which are described in the DSM specification. + +Methods on NVDIMM ROOT Device +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +_FIT(Firmware Interface Table) + It evaluates to a buffer returning data in the format of a series of NFIT + Type Structure. + + Arguments: None + + Return Value: + A Buffer containing a list of NFIT Type structure entries. + + The detailed definition of the structure can be found at ACPI 6.0: 5.2.25 + NVDIMM Firmware Interface Table (NFIT). + +QEMU NVDIMM Implementation +-------------------------- + +QEMU uses 4 bytes IO Port starting from 0x0a18 and a RAM-based memory page +for NVDIMM ACPI. + +Memory: + QEMU uses BIOS Linker/loader feature to ask BIOS to allocate a memory + page and dynamically patch its address into an int32 object named "MEMA" + in ACPI. + + This page is RAM-based and it is used to transfer data between _DSM + method and QEMU. If ACPI has control, this pages is owned by ACPI which + writes _DSM input data to it, otherwise, it is owned by QEMU which + emulates _DSM access and writes the output data to it. + + ACPI writes _DSM Input Data (based on the offset in the page): + + [0x0 - 0x3] + 4 bytes, NVDIMM Device Handle. + + The handle is completely QEMU internal thing, the values in + range [1, 0xFFFF] indicate nvdimm device. Other values are + reserved for other purposes. + + Reserved handles: + + - 0 is reserved for nvdimm root device named NVDR. + - 0x10000 is reserved for QEMU internal DSM function called on + the root device. + + [0x4 - 0x7] + 4 bytes, Revision ID, that is the Arg1 of _DSM method. + + [0x8 - 0xB] + 4 bytes. Function Index, that is the Arg2 of _DSM method. + + [0xC - 0xFFF] + 4084 bytes, the Arg3 of _DSM method. + + QEMU writes Output Data (based on the offset in the page): + + [0x0 - 0x3] + 4 bytes, the length of result + + [0x4 - 0xFFF] + 4092 bytes, the DSM result filled by QEMU + +IO Port 0x0a18 - 0xa1b: + ACPI writes the address of the memory page allocated by BIOS to this + port then QEMU gets the control and fills the result in the memory page. + + Write Access: + + [0x0a18 - 0xa1b] + 4 bytes, the address of the memory page allocated by BIOS. + +_DSM process diagram +-------------------- + +"MEMA" indicates the address of memory page allocated by BIOS. + +:: + + +----------------------+ +-----------------------+ + | 1. OSPM | | 2. OSPM | + | save _DSM input data | | write "MEMA" to | Exit to QEMU + | to the page +----->| IO port 0x0a18 +------------+ + | indicated by "MEMA" | | | | + +----------------------+ +-----------------------+ | + | + v + +--------------------+ +-----------+ +------------------+--------+ + | 5 QEMU | | 4 QEMU | | 3. QEMU | + | write _DSM result | | emulate | | get _DSM input data from | + | to the page +<------+ _DSM +<-----+ the page indicated by the | + | | | | | value from the IO port | + +--------+-----------+ +-----------+ +---------------------------+ + | + | Enter Guest + | + v + +--------------------------+ +--------------+ + | 6 OSPM | | 7 OSPM | + | result size is returned | | _DSM return | + | by reading DSM +----->+ | + | result from the page | | | + +--------------------------+ +--------------+ + +NVDIMM hotplug +-------------- + +ACPI BIOS GPE.4 handler is dedicated for notifying OS about nvdimm device +hot-add event. + +QEMU internal use only _DSM functions +------------------------------------- + +Read FIT +^^^^^^^^ + +_FIT method uses _DSM method to fetch NFIT structures blob from QEMU +in 1 page sized increments which are then concatenated and returned +as _FIT method result. + +Input parameters: + +Arg0 + UUID {set to 648B9CF2-CDA1-4312-8AD9-49C4AF32BD62} +Arg1 + Revision ID (set to 1) +Arg2 + Function Index, 0x1 +Arg3 + A package containing a buffer whose layout is as follows: + + +----------+--------+--------+-------------------------------------------+ + | Field | Length | Offset | Description | + +----------+--------+--------+-------------------------------------------+ + | offset | 4 | 0 | offset in QEMU's NFIT structures blob to | + | | | | read from | + +----------+--------+--------+-------------------------------------------+ + +Output layout in the dsm memory page: + + +----------+--------+--------+-------------------------------------------+ + | Field | Length | Offset | Description | + +----------+--------+--------+-------------------------------------------+ + | length | 4 | 0 | length of entire returned data | + | | | | (including this header) | + +----------+--------+--------+-------------------------------------------+ + | | | | return status codes | + | | | | | + | | | | - 0x0 - success | + | | | | - 0x100 - error caused by NFIT update | + | status | 4 | 4 | while read by _FIT wasn't completed | + | | | | - other codes follow Chapter 3 in | + | | | | DSM Spec Rev1 | + +----------+--------+--------+-------------------------------------------+ + | fit data | Varies | 8 | contains FIT data. This field is present | + | | | | if status field is 0. | + +----------+--------+--------+-------------------------------------------+ + +The FIT offset is maintained by the OSPM itself, current offset plus +the size of the fit data returned by the function is the next offset +OSPM should read. When all FIT data has been read out, zero fit data +size is returned. + +If it returns status code 0x100, OSPM should restart to read FIT (read +from offset 0 again). diff --git a/docs/specs/acpi_nvdimm.txt b/docs/specs/acpi_nvdimm.txt deleted file mode 100644 index 3ec42ecbce..0000000000 --- a/docs/specs/acpi_nvdimm.txt +++ /dev/null @@ -1,188 +0,0 @@ -QEMU<->ACPI BIOS NVDIMM interface ---------------------------------- - -QEMU supports NVDIMM via ACPI. This document describes the basic concepts of -NVDIMM ACPI and the interface between QEMU and the ACPI BIOS. - -NVDIMM ACPI Background ----------------------- -NVDIMM is introduced in ACPI 6.0 which defines an NVDIMM root device under -_SB scope with a _HID of “ACPI0012”. For each NVDIMM present or intended -to be supported by platform, platform firmware also exposes an ACPI -Namespace Device under the root device. - -The NVDIMM child devices under the NVDIMM root device are defined with _ADR -corresponding to the NFIT device handle. The NVDIMM root device and the -NVDIMM devices can have device specific methods (_DSM) to provide additional -functions specific to a particular NVDIMM implementation. - -This is an example from ACPI 6.0, a platform contains one NVDIMM: - -Scope (\_SB){ - Device (NVDR) // Root device - { - Name (_HID, “ACPI0012”) - Method (_STA) {...} - Method (_FIT) {...} - Method (_DSM, ...) {...} - Device (NVD) - { - Name(_ADR, h) //where h is NFIT Device Handle for this NVDIMM - Method (_DSM, ...) {...} - } - } -} - -Method supported on both NVDIMM root device and NVDIMM device -_DSM (Device Specific Method) - It is a control method that enables devices to provide device specific - control functions that are consumed by the device driver. - The NVDIMM DSM specification can be found at: - http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf - - Arguments: - Arg0 – A Buffer containing a UUID (16 Bytes) - Arg1 – An Integer containing the Revision ID (4 Bytes) - Arg2 – An Integer containing the Function Index (4 Bytes) - Arg3 – A package containing parameters for the function specified by the - UUID, Revision ID, and Function Index - - Return Value: - If Function Index = 0, a Buffer containing a function index bitfield. - Otherwise, the return value and type depends on the UUID, revision ID - and function index which are described in the DSM specification. - -Methods on NVDIMM ROOT Device -_FIT(Firmware Interface Table) - It evaluates to a buffer returning data in the format of a series of NFIT - Type Structure. - - Arguments: None - - Return Value: - A Buffer containing a list of NFIT Type structure entries. - - The detailed definition of the structure can be found at ACPI 6.0: 5.2.25 - NVDIMM Firmware Interface Table (NFIT). - -QEMU NVDIMM Implementation -========================== -QEMU uses 4 bytes IO Port starting from 0x0a18 and a RAM-based memory page -for NVDIMM ACPI. - -Memory: - QEMU uses BIOS Linker/loader feature to ask BIOS to allocate a memory - page and dynamically patch its address into an int32 object named "MEMA" - in ACPI. - - This page is RAM-based and it is used to transfer data between _DSM - method and QEMU. If ACPI has control, this pages is owned by ACPI which - writes _DSM input data to it, otherwise, it is owned by QEMU which - emulates _DSM access and writes the output data to it. - - ACPI writes _DSM Input Data (based on the offset in the page): - [0x0 - 0x3]: 4 bytes, NVDIMM Device Handle. - - The handle is completely QEMU internal thing, the values in - range [1, 0xFFFF] indicate nvdimm device. Other values are - reserved for other purposes. - - Reserved handles: - 0 is reserved for nvdimm root device named NVDR. - 0x10000 is reserved for QEMU internal DSM function called on - the root device. - - [0x4 - 0x7]: 4 bytes, Revision ID, that is the Arg1 of _DSM method. - [0x8 - 0xB]: 4 bytes. Function Index, that is the Arg2 of _DSM method. - [0xC - 0xFFF]: 4084 bytes, the Arg3 of _DSM method. - - QEMU Writes Output Data (based on the offset in the page): - [0x0 - 0x3]: 4 bytes, the length of result - [0x4 - 0xFFF]: 4092 bytes, the DSM result filled by QEMU - -IO Port 0x0a18 - 0xa1b: - ACPI writes the address of the memory page allocated by BIOS to this - port then QEMU gets the control and fills the result in the memory page. - - write Access: - [0x0a18 - 0xa1b]: 4 bytes, the address of the memory page allocated - by BIOS. - -_DSM process diagram: ---------------------- -"MEMA" indicates the address of memory page allocated by BIOS. - - +----------------------+   +-----------------------+ - |   1. OSPM   |      | 2. OSPM | - | save _DSM input data | | write "MEMA" to | Exit to QEMU - | to the page +----->| IO port 0x0a18 +------------+ - | indicated by "MEMA" | | | | - +----------------------+ +-----------------------+ | -  | -  v - +------------- ----+ +-----------+ +------------------+--------+ - | 5 QEMU | | 4 QEMU | | 3. QEMU | - | write _DSM result | | emulate | | get _DSM input data from | - | to the page +<------+ _DSM +<-----+ the page indicated by the | - | | | | | value from the IO port | - +--------+-----------+ +-----------+ +---------------------------+ - | - | Enter Guest - | - v - +--------------------------+ +--------------+ - | 6 OSPM | | 7 OSPM | - | result size is returned | | _DSM return | - | by reading DSM +----->+ | - | result from the page | | | - +--------------------------+ +--------------+ - -NVDIMM hotplug --------------- -ACPI BIOS GPE.4 handler is dedicated for notifying OS about nvdimm device -hot-add event. - -QEMU internal use only _DSM function ------------------------------------- -1) Read FIT - _FIT method uses _DSM method to fetch NFIT structures blob from QEMU - in 1 page sized increments which are then concatenated and returned - as _FIT method result. - - Input parameters: - Arg0 – UUID {set to 648B9CF2-CDA1-4312-8AD9-49C4AF32BD62} - Arg1 – Revision ID (set to 1) - Arg2 - Function Index, 0x1 - Arg3 - A package containing a buffer whose layout is as follows: - - +----------+--------+--------+-------------------------------------------+ - | Field | Length | Offset | Description | - +----------+--------+--------+-------------------------------------------+ - | offset | 4 | 0 | offset in QEMU's NFIT structures blob to | - | | | | read from | - +----------+--------+--------+-------------------------------------------+ - - Output layout in the dsm memory page: - +----------+--------+--------+-------------------------------------------+ - | Field | Length | Offset | Description | - +----------+--------+--------+-------------------------------------------+ - | length | 4 | 0 | length of entire returned data | - | | | | (including this header) | - +----------+-----------------+-------------------------------------------+ - | | | | return status codes | - | | | | 0x0 - success | - | | | | 0x100 - error caused by NFIT update while | - | status | 4 | 4 | read by _FIT wasn't completed, other | - | | | | codes follow Chapter 3 in DSM Spec Rev1 | - +----------+-----------------+-------------------------------------------+ - | fit data | Varies | 8 | contains FIT data, this field is present | - | | | | if status field is 0; | - +----------+--------+--------+-------------------------------------------+ - - The FIT offset is maintained by the OSPM itself, current offset plus - the size of the fit data returned by the function is the next offset - OSPM should read. When all FIT data has been read out, zero fit data - size is returned. - - If it returns status code 0x100, OSPM should restart to read FIT (read - from offset 0 again). diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 8296fb19b7..65e9663916 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -16,3 +16,4 @@ guest hardware that is specific to QEMU. acpi_cpu_hotplug acpi_mem_hotplug acpi_pci_hotplug + acpi_nvdimm From fcc6f733690f54ed0409585db619d05b34c8e751 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 27 Jul 2021 18:04:14 +0100 Subject: [PATCH 122/493] MAINTAINERS: Add ACPI specs documents to ACPI and NVDIMM sections Add entries for the ACPI specs documents in docs/specs to appropriate sections of MAINTAINERS. Signed-off-by: Peter Maydell Reviewed-by: Igor Mammedov Message-id: 20210727170414.3368-6-peter.maydell@linaro.org --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6b3697962c..dffcb651f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1751,6 +1751,10 @@ F: qapi/acpi.json F: tests/qtest/bios-tables-test* F: tests/qtest/acpi-utils.[hc] F: tests/data/acpi/ +F: docs/specs/acpi_cpu_hotplug.rst +F: docs/specs/acpi_mem_hotplug.rst +F: docs/specs/acpi_pci_hotplug.rst +F: docs/specs/acpi_hw_reduced_hotplug.rst ACPI/HEST/GHES R: Dongjiu Geng @@ -2057,6 +2061,7 @@ F: hw/acpi/nvdimm.c F: hw/mem/nvdimm.c F: include/hw/mem/nvdimm.h F: docs/nvdimm.txt +F: docs/specs/acpi_nvdimm.rst e1000x M: Dmitry Fleytman From 6773fbf8c07c58429ca876ddea760f604d0497a8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 30 Jul 2021 11:59:40 +0100 Subject: [PATCH 123/493] softmmu: Use accel_find("xen") instead of xen_available() The xen_available() function is used only to produce an error for some Xen-specific command line options in QEMU binaries where Xen support was not compiled in: it just returns the value of the CONFIG_XEN define. Now that accelerators are QOM classes, we can check for "does this binary have Xen compiled in" with accel_find("xen"), and drop the xen_available() function. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20210730105947.28215-2-peter.maydell@linaro.org --- include/sysemu/arch_init.h | 1 - softmmu/arch_init.c | 9 --------- softmmu/vl.c | 6 +++--- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index e723c467eb..7acfc62418 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -30,7 +30,6 @@ enum { extern const uint32_t arch_type; int kvm_available(void); -int xen_available(void); /* default virtio transport per architecture */ #define QEMU_ARCH_VIRTIO_PCI (QEMU_ARCH_ALPHA | QEMU_ARCH_ARM | \ diff --git a/softmmu/arch_init.c b/softmmu/arch_init.c index 6ff9f30bad..3f4d7c1b1c 100644 --- a/softmmu/arch_init.c +++ b/softmmu/arch_init.c @@ -96,12 +96,3 @@ int kvm_available(void) return 0; #endif } - -int xen_available(void) -{ -#ifdef CONFIG_XEN - return 1; -#else - return 0; -#endif -} diff --git a/softmmu/vl.c b/softmmu/vl.c index 5ca11e7469..82d574fe4b 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -3448,21 +3448,21 @@ void qemu_init(int argc, char **argv, char **envp) has_defaults = 0; break; case QEMU_OPTION_xen_domid: - if (!(xen_available())) { + if (!(accel_find("xen"))) { error_report("Option not supported for this target"); exit(1); } xen_domid = atoi(optarg); break; case QEMU_OPTION_xen_attach: - if (!(xen_available())) { + if (!(accel_find("xen"))) { error_report("Option not supported for this target"); exit(1); } xen_mode = XEN_ATTACH; break; case QEMU_OPTION_xen_domid_restrict: - if (!(xen_available())) { + if (!(accel_find("xen"))) { error_report("Option not supported for this target"); exit(1); } From 4f9205be45ab84398e6ef3bb10af01b3f4a9cef7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 30 Jul 2021 11:59:41 +0100 Subject: [PATCH 124/493] monitor: Use accel_find("kvm") instead of kvm_available() The kvm_available() function reports whether KVM support was compiled into the QEMU binary; it returns the value of the CONFIG_KVM define. The only place in the codebase where we use this function is in qmp_query_kvm(). Now that accelerators are based on QOM classes we can instead use accel_find("kvm") and remove the kvm_available() function. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20210730105947.28215-3-peter.maydell@linaro.org --- include/sysemu/arch_init.h | 2 -- monitor/qmp-cmds.c | 2 +- softmmu/arch_init.c | 9 --------- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 7acfc62418..57caad1c67 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -29,8 +29,6 @@ enum { extern const uint32_t arch_type; -int kvm_available(void); - /* default virtio transport per architecture */ #define QEMU_ARCH_VIRTIO_PCI (QEMU_ARCH_ALPHA | QEMU_ARCH_ARM | \ QEMU_ARCH_HPPA | QEMU_ARCH_I386 | \ diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index f7d64a6457..9ddb9352e6 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -58,7 +58,7 @@ KvmInfo *qmp_query_kvm(Error **errp) KvmInfo *info = g_malloc0(sizeof(*info)); info->enabled = kvm_enabled(); - info->present = kvm_available(); + info->present = accel_find("kvm"); return info; } diff --git a/softmmu/arch_init.c b/softmmu/arch_init.c index 3f4d7c1b1c..9011af74e4 100644 --- a/softmmu/arch_init.c +++ b/softmmu/arch_init.c @@ -87,12 +87,3 @@ int graphic_depth = 32; #endif const uint32_t arch_type = QEMU_ARCH; - -int kvm_available(void) -{ -#ifdef CONFIG_KVM - return 1; -#else - return 0; -#endif -} From ed5d8c9d1c38d4022294741eb759d42bd7690948 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 30 Jul 2021 11:59:42 +0100 Subject: [PATCH 125/493] softmmu/arch_init.c: Trim down include list arch_init.c does very little but has a long list of #include lines. Remove all the unnecessary ones. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20210730105947.28215-4-peter.maydell@linaro.org --- softmmu/arch_init.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/softmmu/arch_init.c b/softmmu/arch_init.c index 9011af74e4..91cbf883e2 100644 --- a/softmmu/arch_init.c +++ b/softmmu/arch_init.c @@ -23,13 +23,6 @@ */ #include "qemu/osdep.h" #include "sysemu/arch_init.h" -#include "hw/pci/pci.h" -#include "hw/audio/soundhw.h" -#include "qapi/error.h" -#include "qemu/config-file.h" -#include "qemu/error-report.h" -#include "hw/acpi/acpi.h" -#include "qemu/help_option.h" #ifdef TARGET_SPARC int graphic_width = 1024; From cb2c553152d3c78eb08b1393ae074acdfd43eda9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 30 Jul 2021 11:59:43 +0100 Subject: [PATCH 126/493] meson.build: Define QEMU_ARCH in config-target.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using an ifdef ladder in arch_init.c (which we then have to manually update every time we add or remove a target architecture), have meson.build put "#define QEMU_ARCH QEMU_ARCH_FOO" in the config-target.h file. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20210730105947.28215-5-peter.maydell@linaro.org --- meson.build | 2 ++ softmmu/arch_init.c | 41 ----------------------------------------- 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/meson.build b/meson.build index b3e7ec0e92..bf63784812 100644 --- a/meson.build +++ b/meson.build @@ -1625,6 +1625,8 @@ foreach target : target_dirs config_target_data.set(k, v) endif endforeach + config_target_data.set('QEMU_ARCH', + 'QEMU_ARCH_' + config_target['TARGET_BASE_ARCH'].to_upper()) config_target_h += {target: configure_file(output: target + '-config-target.h', configuration: config_target_data)} diff --git a/softmmu/arch_init.c b/softmmu/arch_init.c index 91cbf883e2..8919405c7b 100644 --- a/softmmu/arch_init.c +++ b/softmmu/arch_init.c @@ -38,45 +38,4 @@ int graphic_height = 600; int graphic_depth = 32; #endif - -#if defined(TARGET_ALPHA) -#define QEMU_ARCH QEMU_ARCH_ALPHA -#elif defined(TARGET_ARM) -#define QEMU_ARCH QEMU_ARCH_ARM -#elif defined(TARGET_CRIS) -#define QEMU_ARCH QEMU_ARCH_CRIS -#elif defined(TARGET_HPPA) -#define QEMU_ARCH QEMU_ARCH_HPPA -#elif defined(TARGET_I386) -#define QEMU_ARCH QEMU_ARCH_I386 -#elif defined(TARGET_M68K) -#define QEMU_ARCH QEMU_ARCH_M68K -#elif defined(TARGET_MICROBLAZE) -#define QEMU_ARCH QEMU_ARCH_MICROBLAZE -#elif defined(TARGET_MIPS) -#define QEMU_ARCH QEMU_ARCH_MIPS -#elif defined(TARGET_NIOS2) -#define QEMU_ARCH QEMU_ARCH_NIOS2 -#elif defined(TARGET_OPENRISC) -#define QEMU_ARCH QEMU_ARCH_OPENRISC -#elif defined(TARGET_PPC) -#define QEMU_ARCH QEMU_ARCH_PPC -#elif defined(TARGET_RISCV) -#define QEMU_ARCH QEMU_ARCH_RISCV -#elif defined(TARGET_RX) -#define QEMU_ARCH QEMU_ARCH_RX -#elif defined(TARGET_S390X) -#define QEMU_ARCH QEMU_ARCH_S390X -#elif defined(TARGET_SH4) -#define QEMU_ARCH QEMU_ARCH_SH4 -#elif defined(TARGET_SPARC) -#define QEMU_ARCH QEMU_ARCH_SPARC -#elif defined(TARGET_TRICORE) -#define QEMU_ARCH QEMU_ARCH_TRICORE -#elif defined(TARGET_XTENSA) -#define QEMU_ARCH QEMU_ARCH_XTENSA -#elif defined(TARGET_AVR) -#define QEMU_ARCH QEMU_ARCH_AVR -#endif - const uint32_t arch_type = QEMU_ARCH; From cc68292e86f594142ef3770f72adccb103220716 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 30 Jul 2021 11:59:44 +0100 Subject: [PATCH 127/493] arch_init.h: Add QEMU_ARCH_HEXAGON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Hexagon was added we forgot to add it to the QEMU_ARCH_* enumeration. This doesn't cause a visible effect because at the moment Hexagon is linux-user only and the QEMU_ARCH_* constants are only used in softmmu, but we might as well add it in, since it's the only architecture currently missing from the list. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Taylor Simpson Message-id: 20210730105947.28215-6-peter.maydell@linaro.org --- include/sysemu/arch_init.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 57caad1c67..60270c5ad1 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -23,6 +23,7 @@ enum { QEMU_ARCH_RISCV = (1 << 19), QEMU_ARCH_RX = (1 << 20), QEMU_ARCH_AVR = (1 << 21), + QEMU_ARCH_HEXAGON = (1 << 22), QEMU_ARCH_NONE = (1 << 31), }; From 3669282cde01809edac8c91f61da50da2a83b067 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 30 Jul 2021 11:59:45 +0100 Subject: [PATCH 128/493] arch_init.h: Move QEMU_ARCH_VIRTIO_* to qdev-monitor.c The QEMU_ARCH_VIRTIO_* defines are used only in one file, qdev-monitor.c. Move them to that file. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Markus Armbruster Message-id: 20210730105947.28215-7-peter.maydell@linaro.org --- include/sysemu/arch_init.h | 9 --------- softmmu/qdev-monitor.c | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 60270c5ad1..e778939950 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -30,13 +30,4 @@ enum { extern const uint32_t arch_type; -/* default virtio transport per architecture */ -#define QEMU_ARCH_VIRTIO_PCI (QEMU_ARCH_ALPHA | QEMU_ARCH_ARM | \ - QEMU_ARCH_HPPA | QEMU_ARCH_I386 | \ - QEMU_ARCH_MIPS | QEMU_ARCH_PPC | \ - QEMU_ARCH_RISCV | QEMU_ARCH_SH4 | \ - QEMU_ARCH_SPARC | QEMU_ARCH_XTENSA) -#define QEMU_ARCH_VIRTIO_CCW (QEMU_ARCH_S390X) -#define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K) - #endif diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 721dec2d82..a304754ab9 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -52,6 +52,15 @@ typedef struct QDevAlias uint32_t arch_mask; } QDevAlias; +/* default virtio transport per architecture */ +#define QEMU_ARCH_VIRTIO_PCI (QEMU_ARCH_ALPHA | QEMU_ARCH_ARM | \ + QEMU_ARCH_HPPA | QEMU_ARCH_I386 | \ + QEMU_ARCH_MIPS | QEMU_ARCH_PPC | \ + QEMU_ARCH_RISCV | QEMU_ARCH_SH4 | \ + QEMU_ARCH_SPARC | QEMU_ARCH_XTENSA) +#define QEMU_ARCH_VIRTIO_CCW (QEMU_ARCH_S390X) +#define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K) + /* Please keep this table sorted by typename. */ static const QDevAlias qdev_alias_table[] = { { "AC97", "ac97" }, /* -soundhw name */ From 7f4c520dac8c8d39e3d19db5a7e6e74693c9c6a4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 30 Jul 2021 11:59:46 +0100 Subject: [PATCH 129/493] arch_init.h: Don't include arch_init.h unnecessarily MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arch_init.h only defines the QEMU_ARCH_* enumeration and the arch_type global. Don't include it in files that don't use those. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-id: 20210730105947.28215-8-peter.maydell@linaro.org --- blockdev.c | 1 - hw/i386/pc.c | 1 - hw/i386/pc_piix.c | 1 - hw/i386/pc_q35.c | 1 - hw/mips/jazz.c | 1 - hw/mips/malta.c | 1 - hw/ppc/prep.c | 1 - hw/riscv/sifive_e.c | 1 - hw/riscv/sifive_u.c | 1 - hw/riscv/spike.c | 1 - hw/riscv/virt.c | 1 - monitor/qmp-cmds.c | 1 - target/ppc/cpu_init.c | 1 - target/s390x/cpu-sysemu.c | 1 - 14 files changed, 14 deletions(-) diff --git a/blockdev.c b/blockdev.c index 3d8ac368a1..e79c5f3b5e 100644 --- a/blockdev.c +++ b/blockdev.c @@ -56,7 +56,6 @@ #include "sysemu/iothread.h" #include "block/block_int.h" #include "block/trace.h" -#include "sysemu/arch_init.h" #include "sysemu/runstate.h" #include "sysemu/replay.h" #include "qemu/cutils.h" diff --git a/hw/i386/pc.c b/hw/i386/pc.c index c2b9d62a35..102b223946 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -65,7 +65,6 @@ #include "hw/xen/start_info.h" #include "ui/qemu-spice.h" #include "exec/memory.h" -#include "sysemu/arch_init.h" #include "qemu/bitmap.h" #include "qemu/config-file.h" #include "qemu/error-report.h" diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 30b8bd6ea9..1bc30167ac 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -43,7 +43,6 @@ #include "sysemu/kvm.h" #include "hw/kvm/clock.h" #include "hw/sysbus.h" -#include "sysemu/arch_init.h" #include "hw/i2c/smbus_eeprom.h" #include "hw/xen/xen-x86.h" #include "exec/memory.h" diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 04b4a4788d..eeb0b185b1 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -31,7 +31,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "hw/loader.h" -#include "sysemu/arch_init.h" #include "hw/i2c/smbus_eeprom.h" #include "hw/rtc/mc146818rtc.h" #include "sysemu/kvm.h" diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index d6183e1882..f5a26e174d 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -35,7 +35,6 @@ #include "hw/isa/isa.h" #include "hw/block/fdc.h" #include "sysemu/sysemu.h" -#include "sysemu/arch_init.h" #include "hw/boards.h" #include "net/net.h" #include "hw/scsi/esp.h" diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 7dcf175d72..b770b8d367 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -38,7 +38,6 @@ #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" #include "hw/pci/pci.h" -#include "sysemu/arch_init.h" #include "qemu/log.h" #include "hw/mips/bios.h" #include "hw/ide.h" diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index acfc2a91d8..25a2e86b42 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -40,7 +40,6 @@ #include "hw/rtc/mc146818rtc.h" #include "hw/isa/pc87312.h" #include "hw/qdev-properties.h" -#include "sysemu/arch_init.h" #include "sysemu/kvm.h" #include "sysemu/reset.h" #include "trace.h" diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index ddc658c8d6..5b7b245e1f 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -45,7 +45,6 @@ #include "hw/intc/sifive_plic.h" #include "hw/misc/sifive_e_prci.h" #include "chardev/char.h" -#include "sysemu/arch_init.h" #include "sysemu/sysemu.h" static const MemMapEntry sifive_e_memmap[] = { diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 87bbd10b21..6cc1a62b0f 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -55,7 +55,6 @@ #include "hw/intc/sifive_plic.h" #include "chardev/char.h" #include "net/eth.h" -#include "sysemu/arch_init.h" #include "sysemu/device_tree.h" #include "sysemu/runstate.h" #include "sysemu/sysemu.h" diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index fead77f0c4..aae36f2cb4 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -37,7 +37,6 @@ #include "hw/char/riscv_htif.h" #include "hw/intc/sifive_clint.h" #include "chardev/char.h" -#include "sysemu/arch_init.h" #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 4a3cd2599a..0e55411045 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -36,7 +36,6 @@ #include "hw/intc/sifive_plic.h" #include "hw/misc/sifive_test.h" #include "chardev/char.h" -#include "sysemu/arch_init.h" #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" #include "hw/pci/pci.h" diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 9ddb9352e6..5c0d5e116b 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -27,7 +27,6 @@ #include "sysemu/kvm.h" #include "sysemu/runstate.h" #include "sysemu/runstate-action.h" -#include "sysemu/arch_init.h" #include "sysemu/blockdev.h" #include "sysemu/block-backend.h" #include "qapi/error.h" diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 505a0ed6ac..319a272d4c 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -22,7 +22,6 @@ #include "disas/dis-asm.h" #include "exec/gdbstub.h" #include "kvm_ppc.h" -#include "sysemu/arch_init.h" #include "sysemu/cpus.h" #include "sysemu/hw_accel.h" #include "sysemu/tcg.h" diff --git a/target/s390x/cpu-sysemu.c b/target/s390x/cpu-sysemu.c index df2c6bf694..5471e01ee8 100644 --- a/target/s390x/cpu-sysemu.c +++ b/target/s390x/cpu-sysemu.c @@ -34,7 +34,6 @@ #include "hw/s390x/pv.h" #include "hw/boards.h" -#include "sysemu/arch_init.h" #include "sysemu/sysemu.h" #include "sysemu/tcg.h" #include "hw/core/sysemu-cpu-ops.h" From 62fffaa6c9244bc1ee10da6bceacea3ae6c6431a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 30 Jul 2021 11:59:47 +0100 Subject: [PATCH 130/493] stubs: Remove unused arch_type.c stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We added a stub for the arch_type global in commit 5964ed56d9a1 so that we could compile blockdev.c into the tools. However, in commit 9db1d3a2be9bf we removed the only use of arch_type from blockdev.c. The stub is therefore no longer needed, and we can delete it again, together with the QEMU_ARCH_NONE value that only the stub was using. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20210730105947.28215-9-peter.maydell@linaro.org --- include/sysemu/arch_init.h | 2 -- stubs/arch_type.c | 4 ---- stubs/meson.build | 1 - 3 files changed, 7 deletions(-) delete mode 100644 stubs/arch_type.c diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index e778939950..70c579560a 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -24,8 +24,6 @@ enum { QEMU_ARCH_RX = (1 << 20), QEMU_ARCH_AVR = (1 << 21), QEMU_ARCH_HEXAGON = (1 << 22), - - QEMU_ARCH_NONE = (1 << 31), }; extern const uint32_t arch_type; diff --git a/stubs/arch_type.c b/stubs/arch_type.c deleted file mode 100644 index fc5423bc98..0000000000 --- a/stubs/arch_type.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "qemu/osdep.h" -#include "sysemu/arch_init.h" - -const uint32_t arch_type = QEMU_ARCH_NONE; diff --git a/stubs/meson.build b/stubs/meson.build index d3fa8646b3..717bfa9a99 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -1,4 +1,3 @@ -stub_ss.add(files('arch_type.c')) stub_ss.add(files('bdrv-next-monitor-owned.c')) stub_ss.add(files('blk-commit-all.c')) stub_ss.add(files('blk-exp-close-all.c')) From 312c496a95430dcabe0028e5a68d595c9411aa91 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Aug 2021 15:18:03 +0100 Subject: [PATCH 131/493] hw/core/loader: In gunzip(), check index is in range before use, not after MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gunzip() function reads various fields from a passed in source buffer in order to skip a header before passing the actual compressed data to the zlib inflate() function. It does check whether the passed in buffer is too small, but unfortunately it checks that only after reading bytes from the src buffer, so it could read off the end of the buffer. You can see this with valgrind: $ printf "%b" '\x1f\x8b' > /tmp/image $ valgrind qemu-system-aarch64 -display none -M virt -cpu max -kernel /tmp/image [...] ==19224== Invalid read of size 1 ==19224== at 0x67302E: gunzip (loader.c:558) ==19224== by 0x673907: load_image_gzipped_buffer (loader.c:788) ==19224== by 0xA18032: load_aarch64_image (boot.c:932) ==19224== by 0xA18489: arm_setup_direct_kernel_boot (boot.c:1063) ==19224== by 0xA18D90: arm_load_kernel (boot.c:1317) ==19224== by 0x9F3651: machvirt_init (virt.c:2114) ==19224== by 0x794B7A: machine_run_board_init (machine.c:1272) ==19224== by 0xD5CAD3: qemu_init_board (vl.c:2618) ==19224== by 0xD5CCA6: qmp_x_exit_preconfig (vl.c:2692) ==19224== by 0xD5F32E: qemu_init (vl.c:3713) ==19224== by 0x5ADDB1: main (main.c:49) ==19224== Address 0x3802a873 is 0 bytes after a block of size 3 alloc'd ==19224== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==19224== by 0x61E7657: g_file_get_contents (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.5600.4) ==19224== by 0x673895: load_image_gzipped_buffer (loader.c:771) ==19224== by 0xA18032: load_aarch64_image (boot.c:932) ==19224== by 0xA18489: arm_setup_direct_kernel_boot (boot.c:1063) ==19224== by 0xA18D90: arm_load_kernel (boot.c:1317) ==19224== by 0x9F3651: machvirt_init (virt.c:2114) ==19224== by 0x794B7A: machine_run_board_init (machine.c:1272) ==19224== by 0xD5CAD3: qemu_init_board (vl.c:2618) ==19224== by 0xD5CCA6: qmp_x_exit_preconfig (vl.c:2692) ==19224== by 0xD5F32E: qemu_init (vl.c:3713) ==19224== by 0x5ADDB1: main (main.c:49) Check that we have enough bytes of data to read the header bytes that we read before we read them. Fixes: Coverity 1458997 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210812141803.20913-1-peter.maydell@linaro.org --- hw/core/loader.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index 5b34869a54..c623318b73 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -555,24 +555,35 @@ ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen) /* skip header */ i = 10; + if (srclen < 4) { + goto toosmall; + } flags = src[3]; if (src[2] != DEFLATED || (flags & RESERVED) != 0) { puts ("Error: Bad gzipped data\n"); return -1; } - if ((flags & EXTRA_FIELD) != 0) + if ((flags & EXTRA_FIELD) != 0) { + if (srclen < 12) { + goto toosmall; + } i = 12 + src[10] + (src[11] << 8); - if ((flags & ORIG_NAME) != 0) - while (src[i++] != 0) - ; - if ((flags & COMMENT) != 0) - while (src[i++] != 0) - ; - if ((flags & HEAD_CRC) != 0) + } + if ((flags & ORIG_NAME) != 0) { + while (i < srclen && src[i++] != 0) { + /* do nothing */ + } + } + if ((flags & COMMENT) != 0) { + while (i < srclen && src[i++] != 0) { + /* do nothing */ + } + } + if ((flags & HEAD_CRC) != 0) { i += 2; + } if (i >= srclen) { - puts ("Error: gunzip out of data in header\n"); - return -1; + goto toosmall; } s.zalloc = zalloc; @@ -596,6 +607,10 @@ ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen) inflateEnd(&s); return dstbytes; + +toosmall: + puts("Error: gunzip out of data in header\n"); + return -1; } /* Load a U-Boot image. */ From 8f1bdb0ea136c38cf963f5fafff103e1b6fb488d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Aug 2021 16:06:24 +0100 Subject: [PATCH 132/493] softmmu/physmem.c: Remove unneeded NULL check in qemu_ram_alloc_from_fd() In the alignment check added to qemu_ram_alloc_from_fd() in commit ce317be98db0dfdfa, the condition includes a check that 'mr' is not NULL. This check is unnecessary because we can assume that the caller always passes us a valid MemoryRegion, and indeed later in the function we assume mr is not NULL when we pass it to file_ram_alloc() as new_block->mr. Remove it. Fixes: Coverity 1459867 Fixes: ce317be98d ("exec: fetch the alignment of Linux devdax pmem character device nodes") Signed-off-by: Peter Maydell Reviewed-by: Jingqi Liu Message-id: 20210812150624.29139-1-peter.maydell@linaro.org --- softmmu/physmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 2e18947598..31baf3a887 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -2075,7 +2075,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, } file_align = get_file_align(fd); - if (file_align > 0 && mr && file_align > mr->align) { + if (file_align > 0 && file_align > mr->align) { error_setg(errp, "backing store align 0x%" PRIx64 " is larger than 'align' option 0x%" PRIx64, file_align, mr->align); From 8efdb7ba1b2acce9fb63ccc2e7982e19fdf5be86 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Aug 2021 16:15:25 +0100 Subject: [PATCH 133/493] softmmu/physmem.c: Check return value from realpath() The realpath() function can return NULL on error, so we need to check for it to avoid crashing when we try to strstr() into it. This can happen if we run out of memory, or if /sys/ is not mounted, among other situations. Fixes: Coverity 1459913, 1460474 Fixes: ce317be98db0 ("exec: fetch the alignment of Linux devdax pmem character device nodes") Signed-off-by: Peter Maydell Reviewed-by: Jingqi Liu Message-id: 20210812151525.31456-1-peter.maydell@linaro.org --- softmmu/physmem.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 31baf3a887..23e77cb771 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -1451,6 +1451,9 @@ static int64_t get_file_align(int fd) path = g_strdup_printf("/sys/dev/char/%d:%d", major(st.st_rdev), minor(st.st_rdev)); rpath = realpath(path, NULL); + if (!rpath) { + return -errno; + } rc = daxctl_new(&ctx); if (rc) { From 59292384621e93f707f862b6936694e56a6daed0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 16:05:03 +0100 Subject: [PATCH 134/493] net: Zero sockaddr_in in parse_host_port() We don't currently zero-initialize the 'struct sockaddr_in' that parse_host_port() fills in, so any fields we don't explicitly initialize might be left as random garbage. POSIX states that implementations may define extensions in sockaddr_in, and that those extensions must not trigger if zero-initialized. So not zero initializing might result in inadvertently triggering an impdef extension. memset() the sockaddr_in before we start to fill it in. Fixes: Coverity CID 1005338 Signed-off-by: Peter Maydell Reviewed-by: Eric Blake Message-id: 20210813150506.7768-2-peter.maydell@linaro.org --- net/net.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/net.c b/net/net.c index 76bbb7c31b..52c99196c6 100644 --- a/net/net.c +++ b/net/net.c @@ -75,6 +75,8 @@ int parse_host_port(struct sockaddr_in *saddr, const char *str, const char *addr, *p, *r; int port, ret = 0; + memset(saddr, 0, sizeof(*saddr)); + substrings = g_strsplit(str, ":", 2); if (!substrings || !substrings[0] || !substrings[1]) { error_setg(errp, "host address '%s' doesn't contain ':' " From fdcdf54d1e93792c66e7566cec4638786990174e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 16:05:04 +0100 Subject: [PATCH 135/493] gdbstub: Zero-initialize sockaddr structs Zero-initialize sockaddr_in and sockaddr_un structs that we're about to fill in and pass to bind() or connect(), to ensure we don't leave possible implementation-defined extension fields as uninitialized garbage. Signed-off-by: Peter Maydell Reviewed-by: Eric Blake Message-id: 20210813150506.7768-3-peter.maydell@linaro.org --- gdbstub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 52bde5bdc9..5d8e6ae3cd 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -3218,7 +3218,7 @@ static bool gdb_accept_socket(int gdb_fd) static int gdbserver_open_socket(const char *path) { - struct sockaddr_un sockaddr; + struct sockaddr_un sockaddr = {}; int fd, ret; fd = socket(AF_UNIX, SOCK_STREAM, 0); @@ -3247,7 +3247,7 @@ static int gdbserver_open_socket(const char *path) static bool gdb_accept_tcp(int gdb_fd) { - struct sockaddr_in sockaddr; + struct sockaddr_in sockaddr = {}; socklen_t len; int fd; From a8ca0033c25939d609c1bab12f6b8402ff719552 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 16:05:05 +0100 Subject: [PATCH 136/493] tests/qtest/ipmi-bt-test: Zero-initialize sockaddr struct Zero-initialize the sockaddr_in struct that we're about to fill in and pass to bind(), to ensure we don't leave possible implementation-defined extension fields as uninitialized garbage. Signed-off-by: Peter Maydell Reviewed-by: Eric Blake Reviewed-by: Corey Minyard Acked-by: Thomas Huth Message-id: 20210813150506.7768-4-peter.maydell@linaro.org --- tests/qtest/ipmi-bt-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/ipmi-bt-test.c b/tests/qtest/ipmi-bt-test.c index 8492f02a9c..19612e9405 100644 --- a/tests/qtest/ipmi-bt-test.c +++ b/tests/qtest/ipmi-bt-test.c @@ -378,7 +378,7 @@ static void test_enable_irq(void) */ static void open_socket(void) { - struct sockaddr_in myaddr; + struct sockaddr_in myaddr = {}; socklen_t addrlen; myaddr.sin_family = AF_INET; From baa873f7508df9f622c9d1ed43d3a03c9cce785c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 16:05:06 +0100 Subject: [PATCH 137/493] tests/tcg/multiarch/linux-test: Zero-initialize sockaddr structs Zero-initialize sockaddr_in and sockaddr_un structs that we're about to fill in and pass to bind() or connect(), to ensure we don't leave possible implementation-defined extension fields as uninitialized garbage. Signed-off-by: Peter Maydell Reviewed-by: Eric Blake Message-id: 20210813150506.7768-5-peter.maydell@linaro.org --- tests/tcg/multiarch/linux-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tcg/multiarch/linux-test.c b/tests/tcg/multiarch/linux-test.c index c8c6aeddeb..019d8175ca 100644 --- a/tests/tcg/multiarch/linux-test.c +++ b/tests/tcg/multiarch/linux-test.c @@ -251,7 +251,7 @@ static void test_time(void) static int server_socket(void) { int val, fd; - struct sockaddr_in sockaddr; + struct sockaddr_in sockaddr = {}; /* server socket */ fd = chk_error(socket(PF_INET, SOCK_STREAM, 0)); @@ -271,7 +271,7 @@ static int server_socket(void) static int client_socket(uint16_t port) { int fd; - struct sockaddr_in sockaddr; + struct sockaddr_in sockaddr = {}; /* server socket */ fd = chk_error(socket(PF_INET, SOCK_STREAM, 0)); From 33c20e3caf82096e7fd50eed0d47778109f62081 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 16 Aug 2021 14:58:40 +0100 Subject: [PATCH 138/493] raspi: Use error_fatal for SoC realize errors, not error_abort MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SoC realize can fail for legitimate reasons, because it propagates errors up from CPU realize, which in turn can be provoked by user error in setting commandline options. Use error_fatal so we report the error message to the user and exit, rather than asserting via error_abort. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210816135842.25302-2-peter.maydell@linaro.org --- hw/arm/raspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index b30a17871f..0ada91c05e 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -281,7 +281,7 @@ static void raspi_machine_init(MachineState *machine) object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(machine->ram)); object_property_set_int(OBJECT(&s->soc), "board-rev", board_rev, &error_abort); - qdev_realize(DEVICE(&s->soc), NULL, &error_abort); + qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); /* Create and plug in the SD cards */ di = drive_get_next(IF_SD); From 49e7f191cab7cdb83c6a278a8a83a3334f416c96 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 16 Aug 2021 14:58:41 +0100 Subject: [PATCH 139/493] target/arm: Avoid assertion trying to use KVM and multiple ASes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM cannot support multiple address spaces per CPU; if you try to create more than one then cpu_address_space_init() will assert. In the Arm CPU realize function, detect the configurations which would cause us to need more than one AS, and cleanly fail the realize rather than blundering on into the assertion. This turns this: $ qemu-system-aarch64 -enable-kvm -display none -cpu max -machine raspi3b qemu-system-aarch64: ../../softmmu/physmem.c:747: cpu_address_space_init: Assertion `asidx == 0 || !kvm_enabled()' failed. Aborted into: $ qemu-system-aarch64 -enable-kvm -display none -machine raspi3b qemu-system-aarch64: Cannot enable KVM when guest CPU has EL3 enabled and this: $ qemu-system-aarch64 -enable-kvm -display none -machine mps3-an524 qemu-system-aarch64: ../../softmmu/physmem.c:747: cpu_address_space_init: Assertion `asidx == 0 || !kvm_enabled()' failed. Aborted into: $ qemu-system-aarch64 -enable-kvm -display none -machine mps3-an524 qemu-system-aarch64: Cannot enable KVM when using an M-profile guest CPU Fixes: https://gitlab.com/qemu-project/qemu/-/issues/528 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210816135842.25302-3-peter.maydell@linaro.org --- target/arm/cpu.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index a82e39dd97..d631c4683c 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1422,6 +1422,29 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } } + if (kvm_enabled()) { + /* + * Catch all the cases which might cause us to create more than one + * address space for the CPU (otherwise we will assert() later in + * cpu_address_space_init()). + */ + if (arm_feature(env, ARM_FEATURE_M)) { + error_setg(errp, + "Cannot enable KVM when using an M-profile guest CPU"); + return; + } + if (cpu->has_el3) { + error_setg(errp, + "Cannot enable KVM when guest CPU has EL3 enabled"); + return; + } + if (cpu->tag_memory) { + error_setg(errp, + "Cannot enable KVM when guest CPUs has MTE enabled"); + return; + } + } + { uint64_t scale; From 665cddbe15fdc5f5c66caac62472bd5af1e23e10 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 16 Aug 2021 14:58:42 +0100 Subject: [PATCH 140/493] hw/arm/virt: Delete EL3 error checksnow provided in CPU realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the CPU realize function will fail cleanly if we ask for EL3 when KVM is enabled, we don't need to check for errors explicitly in the virt board code. The reported message is slightly different; it is now: qemu-system-aarch64: Cannot enable KVM when guest CPU has EL3 enabled instead of: qemu-system-aarch64: mach-virt: KVM does not support Security extensions We don't delete the MTE check because there the logic is more complex; deleting the check would work but makes the error message less helpful, as it would read: qemu-system-aarch64: MTE requested, but not supported by the guest CPU instead of: qemu-system-aarch64: mach-virt: KVM does not support providing MTE to the guest CPU Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210816135842.25302-4-peter.maydell@linaro.org --- hw/arm/virt.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 81eda46b0b..86c8a4ca3d 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1852,11 +1852,6 @@ static void machvirt_init(MachineState *machine) } if (vms->secure) { - if (kvm_enabled()) { - error_report("mach-virt: KVM does not support Security extensions"); - exit(1); - } - /* * The Secure view of the world is the same as the NonSecure, * but with a few extra devices. Create it as a container region From cc7613bfaa1f653a6eb6ff50ac45d5c5fd717052 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 16 Aug 2021 19:03:04 +0100 Subject: [PATCH 141/493] target/arm: Implement HSTR.TTEE In v7, the HSTR register has a TTEE bit which allows EL0/EL1 accesses to the Thumb2EE TEECR and TEEHBR registers to be trapped to the hypervisor. Implement these traps. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20210816180305.20137-2-peter.maydell@linaro.org --- target/arm/cpu.h | 2 ++ target/arm/helper.c | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 1060825c74..0cd3206041 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1541,6 +1541,8 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_ENSCXT (1U << 25) #define SCR_ATA (1U << 26) +#define HSTR_TTEE (1 << 16) + /* Return the current FPSCR value. */ uint32_t vfp_get_fpscr(CPUARMState *env); void vfp_set_fpscr(CPUARMState *env, uint32_t val); diff --git a/target/arm/helper.c b/target/arm/helper.c index 56c520cf8e..54ac8c54b1 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2446,20 +2446,34 @@ static void teecr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->teecr = value; } +static CPAccessResult teecr_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* + * HSTR.TTEE only exists in v7A, not v8A, but v8A doesn't have T2EE + * at all, so we don't need to check whether we're v8A. + */ + if (arm_current_el(env) < 2 && !arm_is_secure_below_el3(env) && + (env->cp15.hstr_el2 & HSTR_TTEE)) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + static CPAccessResult teehbr_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if (arm_current_el(env) == 0 && (env->teecr & 1)) { return CP_ACCESS_TRAP; } - return CP_ACCESS_OK; + return teecr_access(env, ri, isread); } static const ARMCPRegInfo t2ee_cp_reginfo[] = { { .name = "TEECR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 6, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, teecr), .resetvalue = 0, - .writefn = teecr_write }, + .writefn = teecr_write, .accessfn = teecr_access }, { .name = "TEEHBR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 6, .opc2 = 0, .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, teehbr), .accessfn = teehbr_access, .resetvalue = 0 }, From 8e228c9e4bcfea634e7ee404f4d13136d2072c71 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 16 Aug 2021 19:03:05 +0100 Subject: [PATCH 142/493] target/arm: Implement HSTR.TJDBX In v7A, the HSTR register has a TJDBX bit which traps NS EL0/EL1 access to the JOSCR and JMCR trivial Jazelle registers, and also BXJ. Implement these traps. In v8A this HSTR bit doesn't exist, so don't trap for v8A CPUs. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20210816180305.20137-3-peter.maydell@linaro.org --- target/arm/cpu.h | 1 + target/arm/helper.c | 17 +++++++++++++++++ target/arm/helper.h | 2 ++ target/arm/op_helper.c | 16 ++++++++++++++++ target/arm/syndrome.h | 7 +++++++ target/arm/translate.c | 12 ++++++++++++ 6 files changed, 55 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 0cd3206041..09760333cc 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1542,6 +1542,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_ATA (1U << 26) #define HSTR_TTEE (1 << 16) +#define HSTR_TJDBX (1 << 17) /* Return the current FPSCR value. */ uint32_t vfp_get_fpscr(CPUARMState *env); diff --git a/target/arm/helper.c b/target/arm/helper.c index 54ac8c54b1..d2dd4aa0b8 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7602,6 +7602,21 @@ static CPAccessResult access_jazelle(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult access_joscr_jmcr(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + /* + * HSTR.TJDBX traps JOSCR and JMCR accesses, but it exists only + * in v7A, not in v8A. + */ + if (!arm_feature(env, ARM_FEATURE_V8) && + arm_current_el(env) < 2 && !arm_is_secure_below_el3(env) && + (env->cp15.hstr_el2 & HSTR_TJDBX)) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + static const ARMCPRegInfo jazelle_regs[] = { { .name = "JIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 7, .opc2 = 0, @@ -7609,9 +7624,11 @@ static const ARMCPRegInfo jazelle_regs[] = { .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "JOSCR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 7, .opc2 = 0, + .accessfn = access_joscr_jmcr, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "JMCR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 7, .opc2 = 0, + .accessfn = access_joscr_jmcr, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, REGINFO_SENTINEL }; diff --git a/target/arm/helper.h b/target/arm/helper.h index aee8f0019b..448a86edfd 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -73,6 +73,8 @@ DEF_HELPER_2(v7m_vlldm, void, env, i32) DEF_HELPER_2(v8m_stackcheck, void, env, i32) +DEF_HELPER_FLAGS_2(check_bxj_trap, TCG_CALL_NO_WG, void, env, i32) + DEF_HELPER_4(access_check_cp_reg, void, env, ptr, i32, i32) DEF_HELPER_3(set_cp_reg, void, env, ptr, i32) DEF_HELPER_2(get_cp_reg, i32, env, ptr) diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index e98fd86305..70b42b55fd 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -224,6 +224,22 @@ void HELPER(setend)(CPUARMState *env) arm_rebuild_hflags(env); } +void HELPER(check_bxj_trap)(CPUARMState *env, uint32_t rm) +{ + /* + * Only called if in NS EL0 or EL1 for a BXJ for a v7A CPU; + * check if HSTR.TJDBX means we need to trap to EL2. + */ + if (env->cp15.hstr_el2 & HSTR_TJDBX) { + /* + * We know the condition code check passed, so take the IMPDEF + * choice to always report CV=1 COND 0xe + */ + uint32_t syn = syn_bxjtrap(1, 0xe, rm); + raise_exception_ra(env, EXCP_HYP_TRAP, syn, 2, GETPC()); + } +} + #ifndef CONFIG_USER_ONLY /* Function checks whether WFx (WFI/WFE) instructions are set up to be trapped. * The function returns the target EL (1-3) if the instruction is to be trapped; diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index 39a31260f2..8dd88a0cb1 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -36,6 +36,7 @@ enum arm_exception_class { EC_ADVSIMDFPACCESSTRAP = 0x07, EC_FPIDTRAP = 0x08, EC_PACTRAP = 0x09, + EC_BXJTRAP = 0x0a, EC_CP14RRTTRAP = 0x0c, EC_BTITRAP = 0x0d, EC_ILLEGALSTATE = 0x0e, @@ -215,6 +216,12 @@ static inline uint32_t syn_btitrap(int btype) return (EC_BTITRAP << ARM_EL_EC_SHIFT) | btype; } +static inline uint32_t syn_bxjtrap(int cv, int cond, int rm) +{ + return (EC_BXJTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL | + (cv << 24) | (cond << 20) | rm; +} + static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) { return (EC_INSNABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) diff --git a/target/arm/translate.c b/target/arm/translate.c index 115aa768b6..24b7f49d76 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -6440,6 +6440,18 @@ static bool trans_BXJ(DisasContext *s, arg_BXJ *a) if (!ENABLE_ARCH_5J || arm_dc_feature(s, ARM_FEATURE_M)) { return false; } + /* + * v7A allows BXJ to be trapped via HSTR.TJDBX. We don't waste a + * TBFLAGS bit on a basically-never-happens case, so call a helper + * function to check for the trap and raise the exception if needed + * (passing it the register number for the syndrome value). + * v8A doesn't have this HSTR bit. + */ + if (!arm_dc_feature(s, ARM_FEATURE_V8) && + arm_dc_feature(s, ARM_FEATURE_EL2) && + s->current_el < 2 && s->ns) { + gen_helper_check_bxj_trap(cpu_env, tcg_constant_i32(a->rm)); + } /* Trivial implementation equivalent to bx. */ gen_bx(s, load_reg(s, a->rm)); return true; From e784807cd26314071290a036b5a70322eda31db1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 17 Aug 2021 21:18:43 +0100 Subject: [PATCH 143/493] target/arm: Do hflags rebuild in cpsr_write() Currently we rely on all the callsites of cpsr_write() to rebuild the cached hflags if they change one of the CPSR bits which we use as a TB flag and cache in hflags. This is a bit awkward when we want to change the set of CPSR bits that we cache, because it means we need to re-audit all the cpsr_write() callsites to see which flags they are writing and whether they now need to rebuild the hflags. Switch instead to making cpsr_write() call arm_rebuild_hflags() itself if one of the bits being changed is a cached bit. We don't do the rebuild for the CPSRWriteRaw write type, because that kind of write is generally doing something special anyway. For the CPSRWriteRaw callsites in the KVM code and inbound migration we definitely don't want to recalculate the hflags; the callsites in boot.c and arm-powerctl.c have to do a rebuild-hflags call themselves anyway because of other CPU state changes they make. This allows us to drop explicit arm_rebuild_hflags() calls in a couple of places where the only reason we needed to call it was the CPSR write. This fixes a bug where we were incorrectly failing to rebuild hflags in the code path for a gdbstub write to CPSR, which meant that you could make QEMU assert by breaking into a running guest, altering the CPSR to change the value of, for example, CPSR.E, and then continuing. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20210817201843.3829-1-peter.maydell@linaro.org --- linux-user/arm/signal.c | 2 -- target/arm/cpu.h | 10 ++++++++-- target/arm/helper.c | 5 +++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index 32b68ee302..1dfcfd2d57 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -289,7 +289,6 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, env->regs[14] = retcode; env->regs[15] = handler & (thumb ? ~1 : ~3); cpsr_write(env, cpsr, CPSR_IT | CPSR_T | CPSR_E, CPSRWriteByInstr); - arm_rebuild_hflags(env); return 0; } @@ -547,7 +546,6 @@ restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) __get_user(env->regs[15], &sc->arm_pc); __get_user(cpsr, &sc->arm_cpsr); cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); - arm_rebuild_hflags(env); err |= !valid_user_regs(env); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 09760333cc..6a987f65e4 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1398,11 +1398,17 @@ uint32_t cpsr_read(CPUARMState *env); typedef enum CPSRWriteType { CPSRWriteByInstr = 0, /* from guest MSR or CPS */ CPSRWriteExceptionReturn = 1, /* from guest exception return insn */ - CPSRWriteRaw = 2, /* trust values, do not switch reg banks */ + CPSRWriteRaw = 2, + /* trust values, no reg bank switch, no hflags rebuild */ CPSRWriteByGDBStub = 3, /* from the GDB stub */ } CPSRWriteType; -/* Set the CPSR. Note that some bits of mask must be all-set or all-clear.*/ +/* + * Set the CPSR. Note that some bits of mask must be all-set or all-clear. + * This will do an arm_rebuild_hflags() if any of the bits in @mask + * correspond to TB flags bits cached in the hflags, unless @write_type + * is CPSRWriteRaw. + */ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, CPSRWriteType write_type); diff --git a/target/arm/helper.c b/target/arm/helper.c index d2dd4aa0b8..a7ae78146d 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9246,6 +9246,8 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, CPSRWriteType write_type) { uint32_t changed_daif; + bool rebuild_hflags = (write_type != CPSRWriteRaw) && + (mask & (CPSR_M | CPSR_E | CPSR_IL)); if (mask & CPSR_NZCV) { env->ZF = (~val) & CPSR_Z; @@ -9365,6 +9367,9 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, } mask &= ~CACHED_CPSR_BITS; env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask); + if (rebuild_hflags) { + arm_rebuild_hflags(env); + } } /* Sign/zero extend */ From 9a0fcb7f5fd04fcbfd9a611789806614fa5d2365 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Mon, 23 Aug 2021 10:38:17 -0700 Subject: [PATCH 144/493] hw/arm/xlnx-versal: Add unimplemented APU mmio Add unimplemented APU mmio region to xlnx-versal for booting bare-metal guests built with standalone bsp, which access the region from one of the following places: https://github.com/Xilinx/embeddedsw/blob/release-2020.2/lib/bsp/standalone/src/arm/ARMv8/64bit/armclang/boot.S#L139 https://github.com/Xilinx/embeddedsw/blob/release-2020.2/lib/bsp/standalone/src/arm/ARMv8/64bit/gcc/boot.S#L183 Acked-by: Alistair Francis Reviewed-by: Edgar E. Iglesias Signed-off-by: Tong Ho Message-id: 20210823173818.201259-2-tong.ho@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 2 ++ include/hw/arm/xlnx-versal.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index d60eb4fb18..547a26603a 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -378,6 +378,8 @@ static void versal_unimp(Versal *s) MM_CRL, MM_CRL_SIZE); versal_unimp_area(s, "crf", &s->mr_ps, MM_FPD_CRF, MM_FPD_CRF_SIZE); + versal_unimp_area(s, "apu", &s->mr_ps, + MM_FPD_FPD_APU, MM_FPD_FPD_APU_SIZE); versal_unimp_area(s, "crp", &s->mr_ps, MM_PMC_CRP, MM_PMC_CRP_SIZE); versal_unimp_area(s, "iou-scntr", &s->mr_ps, diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 22a8fa5d11..9b79051747 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -167,6 +167,8 @@ struct Versal { #define MM_IOU_SCNTRS_SIZE 0x10000 #define MM_FPD_CRF 0xfd1a0000U #define MM_FPD_CRF_SIZE 0x140000 +#define MM_FPD_FPD_APU 0xfd5c0000 +#define MM_FPD_FPD_APU_SIZE 0x100 #define MM_PMC_SD0 0xf1040000U #define MM_PMC_SD0_SIZE 0x10000 From d2e6f370138a7f32bc28b20dcd55374b7a638f39 Mon Sep 17 00:00:00 2001 From: Tong Ho Date: Mon, 23 Aug 2021 10:38:18 -0700 Subject: [PATCH 145/493] hw/arm/xlnx-zynqmp: Add unimplemented APU mmio Add unimplemented APU mmio region to xlnx-zynqmp for booting bare-metal guests built with standalone bsp, which access the region from one of the following places: https://github.com/Xilinx/embeddedsw/blob/release-2020.2/lib/bsp/standalone/src/arm/ARMv8/64bit/armclang/boot.S#L139 https://github.com/Xilinx/embeddedsw/blob/release-2020.2/lib/bsp/standalone/src/arm/ARMv8/64bit/gcc/boot.S#L183 Acked-by: Alistair Francis Reviewed-by: Edgar E. Iglesias Signed-off-by: Tong Ho Message-id: 20210823173818.201259-3-tong.ho@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zynqmp.c | 32 ++++++++++++++++++++++++++++++++ include/hw/arm/xlnx-zynqmp.h | 7 +++++++ 2 files changed, 39 insertions(+) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 6cfce26210..4e5a471e30 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -20,6 +20,7 @@ #include "qemu/module.h" #include "hw/arm/xlnx-zynqmp.h" #include "hw/intc/arm_gic_common.h" +#include "hw/misc/unimp.h" #include "hw/boards.h" #include "sysemu/kvm.h" #include "sysemu/sysemu.h" @@ -56,6 +57,9 @@ #define DPDMA_ADDR 0xfd4c0000 #define DPDMA_IRQ 116 +#define APU_ADDR 0xfd5c0000 +#define APU_SIZE 0x100 + #define IPI_ADDR 0xFF300000 #define IPI_IRQ 64 @@ -222,6 +226,32 @@ static void xlnx_zynqmp_create_rpu(MachineState *ms, XlnxZynqMPState *s, qdev_realize(DEVICE(&s->rpu_cluster), NULL, &error_fatal); } +static void xlnx_zynqmp_create_unimp_mmio(XlnxZynqMPState *s) +{ + static const struct UnimpInfo { + const char *name; + hwaddr base; + hwaddr size; + } unimp_areas[ARRAY_SIZE(s->mr_unimp)] = { + { .name = "apu", APU_ADDR, APU_SIZE }, + }; + unsigned int nr; + + for (nr = 0; nr < ARRAY_SIZE(unimp_areas); nr++) { + const struct UnimpInfo *info = &unimp_areas[nr]; + DeviceState *dev = qdev_new(TYPE_UNIMPLEMENTED_DEVICE); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + assert(info->name && info->base && info->size > 0); + qdev_prop_set_string(dev, "name", info->name); + qdev_prop_set_uint64(dev, "size", info->size); + object_property_add_child(OBJECT(s), info->name, OBJECT(dev)); + + sysbus_realize_and_unref(sbd, &error_fatal); + sysbus_mmio_map(sbd, 0, info->base); + } +} + static void xlnx_zynqmp_init(Object *obj) { MachineState *ms = MACHINE(qdev_get_machine()); @@ -596,6 +626,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, RTC_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, gic_spi[RTC_IRQ]); + xlnx_zynqmp_create_unimp_mmio(s); + for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { if (!object_property_set_uint(OBJECT(&s->gdma[i]), "bus-width", 128, errp)) { diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index d3e2ef97f6..c84fe15996 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -79,6 +79,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) #define XLNX_ZYNQMP_MAX_RAM_SIZE (XLNX_ZYNQMP_MAX_LOW_RAM_SIZE + \ XLNX_ZYNQMP_MAX_HIGH_RAM_SIZE) +/* + * Unimplemented mmio regions needed to boot some images. + */ +#define XLNX_ZYNQMP_NUM_UNIMP_AREAS 1 + struct XlnxZynqMPState { /*< private >*/ DeviceState parent_obj; @@ -96,6 +101,8 @@ struct XlnxZynqMPState { MemoryRegion *ddr_ram; MemoryRegion ddr_ram_low, ddr_ram_high; + MemoryRegion mr_unimp[XLNX_ZYNQMP_NUM_UNIMP_AREAS]; + CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS]; CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS]; XlnxZynqMPCANState can[XLNX_ZYNQMP_NUM_CAN]; From 585edbb0a1eaaf950ea276d47dbc81cff1869620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 9 Aug 2021 10:52:27 +0200 Subject: [PATCH 146/493] xive: Remove extra '0x' prefix in trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: thuth@redhat.com Fixes: 4e960974d4ee ("xive: Add trace events") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/519 Signed-off-by: Cédric Le Goater Message-Id: <20210809085227.288523-1-clg@kaod.org> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Signed-off-by: David Gibson --- hw/intc/trace-events | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index e56e7dd3b6..6a17d38998 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -219,14 +219,14 @@ kvm_xive_source_reset(uint32_t srcno) "IRQ 0x%x" xive_tctx_accept(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x ACK" xive_tctx_notify(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x raise !" xive_tctx_set_cppr(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x new CPPR=0x%02x NSR=0x%02x" -xive_source_esb_read(uint64_t addr, uint32_t srcno, uint64_t value) "@0x0x%"PRIx64" IRQ 0x%x val=0x0x%"PRIx64 -xive_source_esb_write(uint64_t addr, uint32_t srcno, uint64_t value) "@0x0x%"PRIx64" IRQ 0x%x val=0x0x%"PRIx64 +xive_source_esb_read(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64 +xive_source_esb_write(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64 xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "END 0x%02x/0x%04x -> enqueue 0x%08x" xive_router_end_escalate(uint8_t end_blk, uint32_t end_idx, uint8_t esc_blk, uint32_t esc_idx, uint32_t end_data) "END 0x%02x/0x%04x -> escalate END 0x%02x/0x%04x data 0x%08x" -xive_tctx_tm_write(uint64_t offset, unsigned int size, uint64_t value) "@0x0x%"PRIx64" sz=%d val=0x%" PRIx64 -xive_tctx_tm_read(uint64_t offset, unsigned int size, uint64_t value) "@0x0x%"PRIx64" sz=%d val=0x%" PRIx64 +xive_tctx_tm_write(uint64_t offset, unsigned int size, uint64_t value) "@0x%"PRIx64" sz=%d val=0x%" PRIx64 +xive_tctx_tm_read(uint64_t offset, unsigned int size, uint64_t value) "@0x%"PRIx64" sz=%d val=0x%" PRIx64 xive_presenter_notify(uint8_t nvt_blk, uint32_t nvt_idx, uint8_t ring) "found NVT 0x%x/0x%x ring=0x%x" -xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x/0x%x @0x0x%"PRIx64 +xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x/0x%x @0x%"PRIx64 # pnv_xive.c pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64 From a4e4c4b45f39082f581e8bf71fb1cb06bdb8a4c6 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 10 Aug 2021 14:28:19 +1000 Subject: [PATCH 147/493] spapr_pci: Fix leak in spapr_phb_vfio_get_loc_code() with g_autofree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This uses g_autofree to simplify logic in spapr_phb_vfio_get_loc_code(), in the process fixing a leak in one of the paths. I'm told this fixes Coverity error CID 1460454 Reported-by: Peter Maydell Fixes: 16b0ea1d852 ("spapr_pci: populate ibm,loc-code") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/ppc/spapr_pci.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 7a725855f9..7430bd6314 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -782,33 +782,29 @@ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) static char *spapr_phb_vfio_get_loc_code(SpaprPhbState *sphb, PCIDevice *pdev) { - char *path = NULL, *buf = NULL, *host = NULL; + g_autofree char *path = NULL; + g_autofree char *host = NULL; + g_autofree char *devspec = NULL; + char *buf = NULL; /* Get the PCI VFIO host id */ host = object_property_get_str(OBJECT(pdev), "host", NULL); if (!host) { - goto err_out; + return NULL; } /* Construct the path of the file that will give us the DT location */ path = g_strdup_printf("/sys/bus/pci/devices/%s/devspec", host); - g_free(host); - if (!g_file_get_contents(path, &buf, NULL, NULL)) { - goto err_out; + if (!g_file_get_contents(path, &devspec, NULL, NULL)) { + return NULL; } - g_free(path); /* Construct and read from host device tree the loc-code */ - path = g_strdup_printf("/proc/device-tree%s/ibm,loc-code", buf); - g_free(buf); + path = g_strdup_printf("/proc/device-tree%s/ibm,loc-code", devspec); if (!g_file_get_contents(path, &buf, NULL, NULL)) { - goto err_out; + return NULL; } return buf; - -err_out: - g_free(path); - return NULL; } static char *spapr_phb_get_loc_code(SpaprPhbState *sphb, PCIDevice *pdev) From 5118ebe8396d2b98217b3d4719e3a420dfb0a929 Mon Sep 17 00:00:00 2001 From: "Lucas Mateus Castro (alqotel)" Date: Fri, 23 Jul 2021 14:56:25 -0300 Subject: [PATCH 148/493] target/ppc: divided mmu_helper.c in 2 files Divided mmu_helper.c in 2 files, functions inside #ifdef CONFIG_SOFTMMU stayed in mmu_helper.c, other functions moved to mmu_common.c. Updated meson.build to compile mmu_common.c and only compile mmu_helper.c when CONFIG_TCG is set. Moved function declarations, #define and structs used by both files to internal.h except for functions that use structures defined in cpu.h, those were moved to cpu.h. Signed-off-by: Lucas Mateus Castro (alqotel) Message-Id: <20210723175627.72847-2-lucas.araujo@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/cpu.h | 9 + target/ppc/internal.h | 39 + target/ppc/meson.build | 8 +- target/ppc/mmu_common.c | 1604 +++++++++++++++++++++++++++++++++++++++ target/ppc/mmu_helper.c | 1590 +------------------------------------- 5 files changed, 1658 insertions(+), 1592 deletions(-) create mode 100644 target/ppc/mmu_common.c diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 93d308ac8f..500205229c 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1330,6 +1330,15 @@ void store_booke_tsr(CPUPPCState *env, target_ulong val); void ppc_tlb_invalidate_all(CPUPPCState *env); void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr); void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, + hwaddr *raddrp, target_ulong address, + uint32_t pid); +int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddrp, + target_ulong address, uint32_t pid, int ext, + int i); +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, + ppcmas_tlb_t *tlb); #endif #endif diff --git a/target/ppc/internal.h b/target/ppc/internal.h index f1fd3c8d04..b71406fa46 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -245,4 +245,43 @@ static inline int prot_for_access_type(MMUAccessType access_type) g_assert_not_reached(); } +/* PowerPC MMU emulation */ + +typedef struct mmu_ctx_t mmu_ctx_t; +bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible); +int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, + MMUAccessType access_type, int type, + int mmu_idx); +/* Software driven TLB helpers */ +int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, + int way, int is_code); +/* Context used internally during MMU translations */ +struct mmu_ctx_t { + hwaddr raddr; /* Real address */ + hwaddr eaddr; /* Effective address */ + int prot; /* Protection bits */ + hwaddr hash[2]; /* Pagetable hash values */ + target_ulong ptem; /* Virtual segment ID | API */ + int key; /* Access key */ + int nx; /* Non-execute area */ +}; + +/* Common routines used by software and hardware TLBs emulation */ +static inline int pte_is_valid(target_ulong pte0) +{ + return pte0 & 0x80000000 ? 1 : 0; +} + +static inline void pte_invalidate(target_ulong *pte0) +{ + *pte0 &= ~0x80000000; +} + +#define PTE_PTEM_MASK 0x7FFFFFBF +#define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) + + #endif /* PPC_INTERNAL_H */ diff --git a/target/ppc/meson.build b/target/ppc/meson.build index a4f18ff414..b85f295703 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -37,11 +37,13 @@ ppc_softmmu_ss.add(files( 'arch_dump.c', 'machine.c', 'mmu-hash32.c', - 'mmu_helper.c', + 'mmu_common.c', 'monitor.c', )) -ppc_softmmu_ss.add(when: 'CONFIG_TCG', if_false: files( - 'tcg-stub.c' +ppc_softmmu_ss.add(when: 'CONFIG_TCG', if_true: files( + 'mmu_helper.c', +), if_false: files( + 'tcg-stub.c', )) ppc_softmmu_ss.add(when: 'TARGET_PPC64', if_true: files( diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c new file mode 100644 index 0000000000..ec4dce4ddc --- /dev/null +++ b/target/ppc/mmu_common.c @@ -0,0 +1,1604 @@ +/* + * PowerPC MMU, TLB, SLB and BAT emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "cpu.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "mmu-hash64.h" +#include "mmu-hash32.h" +#include "exec/exec-all.h" +#include "exec/log.h" +#include "helper_regs.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qemu/qemu-print.h" +#include "internal.h" +#include "mmu-book3s-v3.h" +#include "mmu-radix64.h" + +/* #define DEBUG_MMU */ +/* #define DEBUG_BATS */ +/* #define DEBUG_SOFTWARE_TLB */ +/* #define DUMP_PAGE_TABLES */ +/* #define FLUSH_ALL_TLBS */ + +#ifdef DEBUG_MMU +# define LOG_MMU_STATE(cpu) log_cpu_state_mask(CPU_LOG_MMU, (cpu), 0) +#else +# define LOG_MMU_STATE(cpu) do { } while (0) +#endif + +#ifdef DEBUG_SOFTWARE_TLB +# define LOG_SWTLB(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) +#else +# define LOG_SWTLB(...) do { } while (0) +#endif + +#ifdef DEBUG_BATS +# define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) +#else +# define LOG_BATS(...) do { } while (0) +#endif + +/*****************************************************************************/ +/* PowerPC MMU emulation */ + +static int pp_check(int key, int pp, int nx) +{ + int access; + + /* Compute access rights */ + access = 0; + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + access |= PAGE_WRITE; + /* fall through */ + case 0x3: + access |= PAGE_READ; + break; + } + } else { + switch (pp) { + case 0x0: + access = 0; + break; + case 0x1: + case 0x3: + access = PAGE_READ; + break; + case 0x2: + access = PAGE_READ | PAGE_WRITE; + break; + } + } + if (nx == 0) { + access |= PAGE_EXEC; + } + + return access; +} + +static int check_prot(int prot, MMUAccessType access_type) +{ + return prot & prot_for_access_type(access_type) ? 0 : -2; +} + +int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, + int way, int is_code) +{ + int nr; + + /* Select TLB num in a way from address */ + nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1); + /* Select TLB way */ + nr += env->tlb_per_way * way; + /* 6xx have separate TLBs for instructions and data */ + if (is_code && env->id_tlbs == 1) { + nr += env->nb_tlb; + } + + return nr; +} + +static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, + MMUAccessType access_type) +{ + target_ulong ptem, mmask; + int access, ret, pteh, ptev, pp; + + ret = -1; + /* Check validity and table match */ + ptev = pte_is_valid(pte0); + pteh = (pte0 >> 6) & 1; + if (ptev && h == pteh) { + /* Check vsid & api */ + ptem = pte0 & PTE_PTEM_MASK; + mmask = PTE_CHECK_MASK; + pp = pte1 & 0x00000003; + if (ptem == ctx->ptem) { + if (ctx->raddr != (hwaddr)-1ULL) { + /* all matches should have equal RPN, WIMG & PP */ + if ((ctx->raddr & mmask) != (pte1 & mmask)) { + qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); + return -3; + } + } + /* Compute access rights */ + access = pp_check(ctx->key, pp, ctx->nx); + /* Keep the matching PTE information */ + ctx->raddr = pte1; + ctx->prot = access; + ret = check_prot(ctx->prot, access_type); + if (ret == 0) { + /* Access granted */ + qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); + } else { + /* Access right violation */ + qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); + } + } + } + + return ret; +} + +static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, + int ret, MMUAccessType access_type) +{ + int store = 0; + + /* Update page flags */ + if (!(*pte1p & 0x00000100)) { + /* Update accessed flag */ + *pte1p |= 0x00000100; + store = 1; + } + if (!(*pte1p & 0x00000080)) { + if (access_type == MMU_DATA_STORE && ret == 0) { + /* Update changed flag */ + *pte1p |= 0x00000080; + store = 1; + } else { + /* Force page fault for first write access */ + ctx->prot &= ~PAGE_WRITE; + } + } + + return store; +} + +/* Software driven TLB helpers */ + +static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, MMUAccessType access_type) +{ + ppc6xx_tlb_t *tlb; + int nr, best, way; + int ret; + + best = -1; + ret = -1; /* No TLB found */ + for (way = 0; way < env->nb_ways; way++) { + nr = ppc6xx_tlb_getnum(env, eaddr, way, access_type == MMU_INST_FETCH); + tlb = &env->tlb.tlb6[nr]; + /* This test "emulates" the PTE index match for hardware TLBs */ + if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { + LOG_SWTLB("TLB %d/%d %s [" TARGET_FMT_lx " " TARGET_FMT_lx + "] <> " TARGET_FMT_lx "\n", nr, env->nb_tlb, + pte_is_valid(tlb->pte0) ? "valid" : "inval", + tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE, eaddr); + continue; + } + LOG_SWTLB("TLB %d/%d %s " TARGET_FMT_lx " <> " TARGET_FMT_lx " " + TARGET_FMT_lx " %c %c\n", nr, env->nb_tlb, + pte_is_valid(tlb->pte0) ? "valid" : "inval", + tlb->EPN, eaddr, tlb->pte1, + access_type == MMU_DATA_STORE ? 'S' : 'L', + access_type == MMU_INST_FETCH ? 'I' : 'D'); + switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, + 0, access_type)) { + case -3: + /* TLB inconsistency */ + return -1; + case -2: + /* Access violation */ + ret = -2; + best = nr; + break; + case -1: + default: + /* No match */ + break; + case 0: + /* access granted */ + /* + * XXX: we should go on looping to check all TLBs + * consistency but we can speed-up the whole thing as + * the result would be undefined if TLBs are not + * consistent. + */ + ret = 0; + best = nr; + goto done; + } + } + if (best != -1) { + done: + LOG_SWTLB("found TLB at addr " TARGET_FMT_plx " prot=%01x ret=%d\n", + ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); + /* Update page flags */ + pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, access_type); + } + + return ret; +} + +/* Perform BAT hit & translation */ +static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, + int *validp, int *protp, target_ulong *BATu, + target_ulong *BATl) +{ + target_ulong bl; + int pp, valid, prot; + + bl = (*BATu & 0x00001FFC) << 15; + valid = 0; + prot = 0; + if (((msr_pr == 0) && (*BATu & 0x00000002)) || + ((msr_pr != 0) && (*BATu & 0x00000001))) { + valid = 1; + pp = *BATl & 0x00000003; + if (pp != 0) { + prot = PAGE_READ | PAGE_EXEC; + if (pp == 0x2) { + prot |= PAGE_WRITE; + } + } + } + *blp = bl; + *validp = valid; + *protp = prot; +} + +static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong virtual, MMUAccessType access_type) +{ + target_ulong *BATlt, *BATut, *BATu, *BATl; + target_ulong BEPIl, BEPIu, bl; + int i, valid, prot; + int ret = -1; + bool ifetch = access_type == MMU_INST_FETCH; + + LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, + ifetch ? 'I' : 'D', virtual); + if (ifetch) { + BATlt = env->IBAT[1]; + BATut = env->IBAT[0]; + } else { + BATlt = env->DBAT[1]; + BATut = env->DBAT[0]; + } + for (i = 0; i < env->nb_BATs; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); + LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n", __func__, + ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl); + if ((virtual & 0xF0000000) == BEPIu && + ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { + /* BAT matches */ + if (valid != 0) { + /* Get physical address */ + ctx->raddr = (*BATl & 0xF0000000) | + ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | + (virtual & 0x0001F000); + /* Compute access rights */ + ctx->prot = prot; + ret = check_prot(ctx->prot, access_type); + if (ret == 0) { + LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", + i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', + ctx->prot & PAGE_WRITE ? 'W' : '-'); + } + break; + } + } + } + if (ret < 0) { +#if defined(DEBUG_BATS) + if (qemu_log_enabled()) { + LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", virtual); + for (i = 0; i < 4; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + bl = (*BATu & 0x00001FFC) << 15; + LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " + TARGET_FMT_lx " " TARGET_FMT_lx "\n", + __func__, ifetch ? 'I' : 'D', i, virtual, + *BATu, *BATl, BEPIu, BEPIl, bl); + } + } +#endif + } + /* No hit */ + return ret; +} + +/* Perform segment based translation */ +static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, MMUAccessType access_type, + int type) +{ + PowerPCCPU *cpu = env_archcpu(env); + hwaddr hash; + target_ulong vsid; + int ds, pr, target_page_bits; + int ret; + target_ulong sr, pgidx; + + pr = msr_pr; + ctx->eaddr = eaddr; + + sr = env->sr[eaddr >> 28]; + ctx->key = (((sr & 0x20000000) && (pr != 0)) || + ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; + ds = sr & 0x80000000 ? 1 : 0; + ctx->nx = sr & 0x10000000 ? 1 : 0; + vsid = sr & 0x00FFFFFF; + target_page_bits = TARGET_PAGE_BITS; + qemu_log_mask(CPU_LOG_MMU, + "Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx + " nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx + " ir=%d dr=%d pr=%d %d t=%d\n", + eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, + (int)msr_dr, pr != 0 ? 1 : 0, access_type == MMU_DATA_STORE, type); + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; + hash = vsid ^ pgidx; + ctx->ptem = (vsid << 7) | (pgidx >> 10); + + qemu_log_mask(CPU_LOG_MMU, + "pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", + ctx->key, ds, ctx->nx, vsid); + ret = -1; + if (!ds) { + /* Check if instruction fetch is allowed, if needed */ + if (type != ACCESS_CODE || ctx->nx == 0) { + /* Page address translation */ + qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx + " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); + ctx->hash[0] = hash; + ctx->hash[1] = ~hash; + + /* Initialize real address with an invalid value */ + ctx->raddr = (hwaddr)-1ULL; + /* Software TLB search */ + ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); +#if defined(DUMP_PAGE_TABLES) + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + CPUState *cs = env_cpu(env); + hwaddr curaddr; + uint32_t a0, a1, a2, a3; + + qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx + "\n", ppc_hash32_hpt_base(cpu), + ppc_hash32_hpt_mask(cpu) + 0x80); + for (curaddr = ppc_hash32_hpt_base(cpu); + curaddr < (ppc_hash32_hpt_base(cpu) + + ppc_hash32_hpt_mask(cpu) + 0x80); + curaddr += 16) { + a0 = ldl_phys(cs->as, curaddr); + a1 = ldl_phys(cs->as, curaddr + 4); + a2 = ldl_phys(cs->as, curaddr + 8); + a3 = ldl_phys(cs->as, curaddr + 12); + if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { + qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); + } + } + } +#endif + } else { + qemu_log_mask(CPU_LOG_MMU, "No access allowed\n"); + ret = -3; + } + } else { + target_ulong sr; + + qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); + /* Direct-store segment : absolutely *BUGGY* for now */ + + /* + * Direct-store implies a 32-bit MMU. + * Check the Segment Register's bus unit ID (BUID). + */ + sr = env->sr[eaddr >> 28]; + if ((sr & 0x1FF00000) >> 20 == 0x07f) { + /* + * Memory-forced I/O controller interface access + * + * If T=1 and BUID=x'07F', the 601 performs a memory + * access to SR[28-31] LA[4-31], bypassing all protection + * mechanisms. + */ + ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); + ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return 0; + } + + switch (type) { + case ACCESS_INT: + /* Integer load/store : only access allowed */ + break; + case ACCESS_CODE: + /* No code fetch is allowed in direct-store areas */ + return -4; + case ACCESS_FLOAT: + /* Floating point load/store */ + return -4; + case ACCESS_RES: + /* lwarx, ldarx or srwcx. */ + return -4; + case ACCESS_CACHE: + /* + * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi + * + * Should make the instruction do no-op. As it already do + * no-op, it's quite easy :-) + */ + ctx->raddr = eaddr; + return 0; + case ACCESS_EXT: + /* eciwx or ecowx */ + return -4; + default: + qemu_log_mask(CPU_LOG_MMU, "ERROR: instruction should not need " + "address translation\n"); + return -4; + } + if ((access_type == MMU_DATA_STORE || ctx->key != 1) && + (access_type == MMU_DATA_LOAD || ctx->key != 0)) { + ctx->raddr = eaddr; + ret = 2; + } else { + ret = -2; + } + } + + return ret; +} + +/* Generic TLB check function for embedded PowerPC implementations */ +int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddrp, + target_ulong address, uint32_t pid, int ext, + int i) +{ + target_ulong mask; + + /* Check valid flag */ + if (!(tlb->prot & PAGE_VALID)) { + return -1; + } + mask = ~(tlb->size - 1); + LOG_SWTLB("%s: TLB %d address " TARGET_FMT_lx " PID %u <=> " TARGET_FMT_lx + " " TARGET_FMT_lx " %u %x\n", __func__, i, address, pid, tlb->EPN, + mask, (uint32_t)tlb->PID, tlb->prot); + /* Check PID */ + if (tlb->PID != 0 && tlb->PID != pid) { + return -1; + } + /* Check effective address */ + if ((address & mask) != tlb->EPN) { + return -1; + } + *raddrp = (tlb->RPN & mask) | (address & ~mask); + if (ext) { + /* Extend the physical address to 36 bits */ + *raddrp |= (uint64_t)(tlb->RPN & 0xF) << 32; + } + + return 0; +} + +static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong address, + MMUAccessType access_type) +{ + ppcemb_tlb_t *tlb; + hwaddr raddr; + int i, ret, zsel, zpr, pr; + + ret = -1; + raddr = (hwaddr)-1ULL; + pr = msr_pr; + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (ppcemb_tlb_check(env, tlb, &raddr, address, + env->spr[SPR_40x_PID], 0, i) < 0) { + continue; + } + zsel = (tlb->attr >> 4) & 0xF; + zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; + LOG_SWTLB("%s: TLB %d zsel %d zpr %d ty %d attr %08x\n", + __func__, i, zsel, zpr, access_type, tlb->attr); + /* Check execute enable bit */ + switch (zpr) { + case 0x2: + if (pr != 0) { + goto check_perms; + } + /* fall through */ + case 0x3: + /* All accesses granted */ + ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + ret = 0; + break; + case 0x0: + if (pr != 0) { + /* Raise Zone protection fault. */ + env->spr[SPR_40x_ESR] = 1 << 22; + ctx->prot = 0; + ret = -2; + break; + } + /* fall through */ + case 0x1: + check_perms: + /* Check from TLB entry */ + ctx->prot = tlb->prot; + ret = check_prot(ctx->prot, access_type); + if (ret == -2) { + env->spr[SPR_40x_ESR] = 0; + } + break; + } + if (ret >= 0) { + ctx->raddr = raddr; + LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, ctx->raddr, ctx->prot, + ret); + return 0; + } + } + LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, raddr, ctx->prot, ret); + + return ret; +} + +void store_40x_sler(CPUPPCState *env, uint32_t val) +{ + /* XXX: TO BE FIXED */ + if (val != 0x00000000) { + cpu_abort(env_cpu(env), + "Little-endian regions are not supported by now\n"); + } + env->spr[SPR_405_SLER] = val; +} + +static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddr, int *prot, target_ulong address, + MMUAccessType access_type, int i) +{ + int prot2; + + if (ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID], + !env->nb_pids, i) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1], 0, i) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2], 0, i) >= 0) { + goto found_tlb; + } + + LOG_SWTLB("%s: TLB entry not found\n", __func__); + return -1; + +found_tlb: + + if (msr_pr != 0) { + prot2 = tlb->prot & 0xF; + } else { + prot2 = (tlb->prot >> 4) & 0xF; + } + + /* Check the address space */ + if ((access_type == MMU_INST_FETCH ? msr_ir : msr_dr) != (tlb->attr & 1)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = prot2; + if (prot2 & prot_for_access_type(access_type)) { + LOG_SWTLB("%s: good TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: no prot match: %x\n", __func__, prot2); + return access_type == MMU_INST_FETCH ? -3 : -2; +} + +static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong address, + MMUAccessType access_type) +{ + ppcemb_tlb_t *tlb; + hwaddr raddr; + int i, ret; + + ret = -1; + raddr = (hwaddr)-1ULL; + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, + access_type, i); + if (ret != -1) { + break; + } + } + + if (ret >= 0) { + ctx->raddr = raddr; + LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, ctx->raddr, ctx->prot, + ret); + } else { + LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, raddr, ctx->prot, ret); + } + + return ret; +} + +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, + ppcmas_tlb_t *tlb) +{ + int tlbm_size; + + tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + + return 1024ULL << tlbm_size; +} + +/* TLB check function for MAS based SoftTLBs */ +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, + hwaddr *raddrp, target_ulong address, + uint32_t pid) +{ + hwaddr mask; + uint32_t tlb_pid; + + if (!msr_cm) { + /* In 32bit mode we can only address 32bit EAs */ + address = (uint32_t)address; + } + + /* Check valid flag */ + if (!(tlb->mas1 & MAS1_VALID)) { + return -1; + } + + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + LOG_SWTLB("%s: TLB ADDR=0x" TARGET_FMT_lx " PID=0x%x MAS1=0x%x MAS2=0x%" + PRIx64 " mask=0x%" HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" + PRIx32 "\n", __func__, address, pid, tlb->mas1, tlb->mas2, mask, + tlb->mas7_3, tlb->mas8); + + /* Check PID */ + tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; + if (tlb_pid != 0 && tlb_pid != pid) { + return -1; + } + + /* Check effective address */ + if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { + return -1; + } + + if (raddrp) { + *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); + } + + return 0; +} + +static bool is_epid_mmu(int mmu_idx) +{ + return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD; +} + +static uint32_t mmubooke206_esr(int mmu_idx, MMUAccessType access_type) +{ + uint32_t esr = 0; + if (access_type == MMU_DATA_STORE) { + esr |= ESR_ST; + } + if (is_epid_mmu(mmu_idx)) { + esr |= ESR_EPID; + } + return esr; +} + +/* + * Get EPID register given the mmu_idx. If this is regular load, + * construct the EPID access bits from current processor state + * + * Get the effective AS and PR bits and the PID. The PID is returned + * only if EPID load is requested, otherwise the caller must detect + * the correct EPID. Return true if valid EPID is returned. + */ +static bool mmubooke206_get_as(CPUPPCState *env, + int mmu_idx, uint32_t *epid_out, + bool *as_out, bool *pr_out) +{ + if (is_epid_mmu(mmu_idx)) { + uint32_t epidr; + if (mmu_idx == PPC_TLB_EPID_STORE) { + epidr = env->spr[SPR_BOOKE_EPSC]; + } else { + epidr = env->spr[SPR_BOOKE_EPLC]; + } + *epid_out = (epidr & EPID_EPID) >> EPID_EPID_SHIFT; + *as_out = !!(epidr & EPID_EAS); + *pr_out = !!(epidr & EPID_EPR); + return true; + } else { + *as_out = msr_ds; + *pr_out = msr_pr; + return false; + } +} + +/* Check if the tlb found by hashing really matches */ +static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, + hwaddr *raddr, int *prot, + target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + int prot2 = 0; + uint32_t epid; + bool as, pr; + bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); + + if (!use_epid) { + if (ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2]) >= 0) { + goto found_tlb; + } + } else { + if (ppcmas_tlb_check(env, tlb, raddr, address, epid) >= 0) { + goto found_tlb; + } + } + + LOG_SWTLB("%s: TLB entry not found\n", __func__); + return -1; + +found_tlb: + + if (pr) { + if (tlb->mas7_3 & MAS3_UR) { + prot2 |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_UW) { + prot2 |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_UX) { + prot2 |= PAGE_EXEC; + } + } else { + if (tlb->mas7_3 & MAS3_SR) { + prot2 |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_SW) { + prot2 |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_SX) { + prot2 |= PAGE_EXEC; + } + } + + /* Check the address space and permissions */ + if (access_type == MMU_INST_FETCH) { + /* There is no way to fetch code using epid load */ + assert(!use_epid); + as = msr_ir; + } + + if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = prot2; + if (prot2 & prot_for_access_type(access_type)) { + LOG_SWTLB("%s: good TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: no prot match: %x\n", __func__, prot2); + return access_type == MMU_INST_FETCH ? -3 : -2; +} + +static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong address, + MMUAccessType access_type, + int mmu_idx) +{ + ppcmas_tlb_t *tlb; + hwaddr raddr; + int i, j, ret; + + ret = -1; + raddr = (hwaddr)-1ULL; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + if (!tlb) { + continue; + } + ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, + access_type, mmu_idx); + if (ret != -1) { + goto found_tlb; + } + } + } + +found_tlb: + + if (ret >= 0) { + ctx->raddr = raddr; + LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, ctx->raddr, ctx->prot, + ret); + } else { + LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, raddr, ctx->prot, ret); + } + + return ret; +} + +static const char *book3e_tsize_to_str[32] = { + "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K", + "1M", "2M", "4M", "8M", "16M", "32M", "64M", "128M", "256M", "512M", + "1G", "2G", "4G", "8G", "16G", "32G", "64G", "128G", "256G", "512G", + "1T", "2T" +}; + +static void mmubooke_dump_mmu(CPUPPCState *env) +{ + ppcemb_tlb_t *entry; + int i; + + if (kvm_enabled() && !env->kvm_sw_tlb) { + qemu_printf("Cannot access KVM TLB\n"); + return; + } + + qemu_printf("\nTLB:\n"); + qemu_printf("Effective Physical Size PID Prot " + "Attr\n"); + + entry = &env->tlb.tlbe[0]; + for (i = 0; i < env->nb_tlb; i++, entry++) { + hwaddr ea, pa; + target_ulong mask; + uint64_t size = (uint64_t)entry->size; + char size_buf[20]; + + /* Check valid flag */ + if (!(entry->prot & PAGE_VALID)) { + continue; + } + + mask = ~(entry->size - 1); + ea = entry->EPN & mask; + pa = entry->RPN & mask; + /* Extend the physical address to 36 bits */ + pa |= (hwaddr)(entry->RPN & 0xF) << 32; + if (size >= 1 * MiB) { + snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "M", size / MiB); + } else { + snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "k", size / KiB); + } + qemu_printf("0x%016" PRIx64 " 0x%016" PRIx64 " %s %-5u %08x %08x\n", + (uint64_t)ea, (uint64_t)pa, size_buf, (uint32_t)entry->PID, + entry->prot, entry->attr); + } + +} + +static void mmubooke206_dump_one_tlb(CPUPPCState *env, int tlbn, int offset, + int tlbsize) +{ + ppcmas_tlb_t *entry; + int i; + + qemu_printf("\nTLB%d:\n", tlbn); + qemu_printf("Effective Physical Size TID TS SRWX" + " URWX WIMGE U0123\n"); + + entry = &env->tlb.tlbm[offset]; + for (i = 0; i < tlbsize; i++, entry++) { + hwaddr ea, pa, size; + int tsize; + + if (!(entry->mas1 & MAS1_VALID)) { + continue; + } + + tsize = (entry->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + size = 1024ULL << tsize; + ea = entry->mas2 & ~(size - 1); + pa = entry->mas7_3 & ~(size - 1); + + qemu_printf("0x%016" PRIx64 " 0x%016" PRIx64 " %4s %-5u %1u S%c%c%c" + "U%c%c%c %c%c%c%c%c U%c%c%c%c\n", + (uint64_t)ea, (uint64_t)pa, + book3e_tsize_to_str[tsize], + (entry->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT, + (entry->mas1 & MAS1_TS) >> MAS1_TS_SHIFT, + entry->mas7_3 & MAS3_SR ? 'R' : '-', + entry->mas7_3 & MAS3_SW ? 'W' : '-', + entry->mas7_3 & MAS3_SX ? 'X' : '-', + entry->mas7_3 & MAS3_UR ? 'R' : '-', + entry->mas7_3 & MAS3_UW ? 'W' : '-', + entry->mas7_3 & MAS3_UX ? 'X' : '-', + entry->mas2 & MAS2_W ? 'W' : '-', + entry->mas2 & MAS2_I ? 'I' : '-', + entry->mas2 & MAS2_M ? 'M' : '-', + entry->mas2 & MAS2_G ? 'G' : '-', + entry->mas2 & MAS2_E ? 'E' : '-', + entry->mas7_3 & MAS3_U0 ? '0' : '-', + entry->mas7_3 & MAS3_U1 ? '1' : '-', + entry->mas7_3 & MAS3_U2 ? '2' : '-', + entry->mas7_3 & MAS3_U3 ? '3' : '-'); + } +} + +static void mmubooke206_dump_mmu(CPUPPCState *env) +{ + int offset = 0; + int i; + + if (kvm_enabled() && !env->kvm_sw_tlb) { + qemu_printf("Cannot access KVM TLB\n"); + return; + } + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int size = booke206_tlb_size(env, i); + + if (size == 0) { + continue; + } + + mmubooke206_dump_one_tlb(env, i, offset, size); + offset += size; + } +} + +static void mmu6xx_dump_BATs(CPUPPCState *env, int type) +{ + target_ulong *BATlt, *BATut, *BATu, *BATl; + target_ulong BEPIl, BEPIu, bl; + int i; + + switch (type) { + case ACCESS_CODE: + BATlt = env->IBAT[1]; + BATut = env->IBAT[0]; + break; + default: + BATlt = env->DBAT[1]; + BATut = env->DBAT[0]; + break; + } + + for (i = 0; i < env->nb_BATs; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + bl = (*BATu & 0x00001FFC) << 15; + qemu_printf("%s BAT%d BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " + TARGET_FMT_lx " " TARGET_FMT_lx "\n", + type == ACCESS_CODE ? "code" : "data", i, + *BATu, *BATl, BEPIu, BEPIl, bl); + } +} + +static void mmu6xx_dump_mmu(CPUPPCState *env) +{ + PowerPCCPU *cpu = env_archcpu(env); + ppc6xx_tlb_t *tlb; + target_ulong sr; + int type, way, entry, i; + + qemu_printf("HTAB base = 0x%"HWADDR_PRIx"\n", ppc_hash32_hpt_base(cpu)); + qemu_printf("HTAB mask = 0x%"HWADDR_PRIx"\n", ppc_hash32_hpt_mask(cpu)); + + qemu_printf("\nSegment registers:\n"); + for (i = 0; i < 32; i++) { + sr = env->sr[i]; + if (sr & 0x80000000) { + qemu_printf("%02d T=%d Ks=%d Kp=%d BUID=0x%03x " + "CNTLR_SPEC=0x%05x\n", i, + sr & 0x80000000 ? 1 : 0, sr & 0x40000000 ? 1 : 0, + sr & 0x20000000 ? 1 : 0, (uint32_t)((sr >> 20) & 0x1FF), + (uint32_t)(sr & 0xFFFFF)); + } else { + qemu_printf("%02d T=%d Ks=%d Kp=%d N=%d VSID=0x%06x\n", i, + sr & 0x80000000 ? 1 : 0, sr & 0x40000000 ? 1 : 0, + sr & 0x20000000 ? 1 : 0, sr & 0x10000000 ? 1 : 0, + (uint32_t)(sr & 0x00FFFFFF)); + } + } + + qemu_printf("\nBATs:\n"); + mmu6xx_dump_BATs(env, ACCESS_INT); + mmu6xx_dump_BATs(env, ACCESS_CODE); + + if (env->id_tlbs != 1) { + qemu_printf("ERROR: 6xx MMU should have separated TLB" + " for code and data\n"); + } + + qemu_printf("\nTLBs [EPN EPN + SIZE]\n"); + + for (type = 0; type < 2; type++) { + for (way = 0; way < env->nb_ways; way++) { + for (entry = env->nb_tlb * type + env->tlb_per_way * way; + entry < (env->nb_tlb * type + env->tlb_per_way * (way + 1)); + entry++) { + + tlb = &env->tlb.tlb6[entry]; + qemu_printf("%s TLB %02d/%02d way:%d %s [" + TARGET_FMT_lx " " TARGET_FMT_lx "]\n", + type ? "code" : "data", entry % env->nb_tlb, + env->nb_tlb, way, + pte_is_valid(tlb->pte0) ? "valid" : "inval", + tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE); + } + } + } +} + +void dump_mmu(CPUPPCState *env) +{ + switch (env->mmu_model) { + case POWERPC_MMU_BOOKE: + mmubooke_dump_mmu(env); + break; + case POWERPC_MMU_BOOKE206: + mmubooke206_dump_mmu(env); + break; + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + mmu6xx_dump_mmu(env); + break; +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: + dump_slb(env_archcpu(env)); + break; + case POWERPC_MMU_3_00: + if (ppc64_v3_radix(env_archcpu(env))) { + qemu_log_mask(LOG_UNIMP, "%s: the PPC64 MMU is unsupported\n", + __func__); + } else { + dump_slb(env_archcpu(env)); + } + break; +#endif + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented\n", __func__); + } +} + +static int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, + MMUAccessType access_type) +{ + int in_plb, ret; + + ctx->raddr = eaddr; + ctx->prot = PAGE_READ | PAGE_EXEC; + ret = 0; + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_REAL: + case POWERPC_MMU_BOOKE: + ctx->prot |= PAGE_WRITE; + break; + + case POWERPC_MMU_SOFT_4xx_Z: + if (unlikely(msr_pe != 0)) { + /* + * 403 family add some particular protections, using + * PBL/PBU registers for accesses with no translation. + */ + in_plb = + /* Check PLB validity */ + (env->pb[0] < env->pb[1] && + /* and address in plb area */ + eaddr >= env->pb[0] && eaddr < env->pb[1]) || + (env->pb[2] < env->pb[3] && + eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0; + if (in_plb ^ msr_px) { + /* Access in protected area */ + if (access_type == MMU_DATA_STORE) { + /* Access is not allowed */ + ret = -2; + } + } else { + /* Read-write access is allowed */ + ctx->prot |= PAGE_WRITE; + } + } + break; + + default: + /* Caller's checks mean we should never get here for other models */ + abort(); + return -1; + } + + return ret; +} + +int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, + MMUAccessType access_type, int type, + int mmu_idx) +{ + int ret = -1; + bool real_mode = (type == ACCESS_CODE && msr_ir == 0) + || (type != ACCESS_CODE && msr_dr == 0); + + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + if (real_mode) { + ret = check_physical(env, ctx, eaddr, access_type); + } else { + /* Try to find a BAT */ + if (env->nb_BATs != 0) { + ret = get_bat_6xx_tlb(env, ctx, eaddr, access_type); + } + if (ret < 0) { + /* We didn't match any BAT entry or don't have BATs */ + ret = get_segment_6xx_tlb(env, ctx, eaddr, access_type, type); + } + } + break; + + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + if (real_mode) { + ret = check_physical(env, ctx, eaddr, access_type); + } else { + ret = mmu40x_get_physical_address(env, ctx, eaddr, access_type); + } + break; + case POWERPC_MMU_BOOKE: + ret = mmubooke_get_physical_address(env, ctx, eaddr, access_type); + break; + case POWERPC_MMU_BOOKE206: + ret = mmubooke206_get_physical_address(env, ctx, eaddr, access_type, + mmu_idx); + break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(env_cpu(env), "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_REAL: + if (real_mode) { + ret = check_physical(env, ctx, eaddr, access_type); + } else { + cpu_abort(env_cpu(env), + "PowerPC in real mode do not do any translation\n"); + } + return -1; + default: + cpu_abort(env_cpu(env), "Unknown or invalid MMU model\n"); + return -1; + } + + return ret; +} + +static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + uint32_t epid; + bool as, pr; + uint32_t missed_tid = 0; + bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); + + if (access_type == MMU_INST_FETCH) { + as = msr_ir; + } + env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; + env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; + env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; + env->spr[SPR_BOOKE_MAS3] = 0; + env->spr[SPR_BOOKE_MAS6] = 0; + env->spr[SPR_BOOKE_MAS7] = 0; + + /* AS */ + if (as) { + env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; + env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; + } + + env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; + env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; + + if (!use_epid) { + switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { + case MAS4_TIDSELD_PID0: + missed_tid = env->spr[SPR_BOOKE_PID]; + break; + case MAS4_TIDSELD_PID1: + missed_tid = env->spr[SPR_BOOKE_PID1]; + break; + case MAS4_TIDSELD_PID2: + missed_tid = env->spr[SPR_BOOKE_PID2]; + break; + } + env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; + } else { + missed_tid = epid; + env->spr[SPR_BOOKE_MAS6] |= missed_tid << 16; + } + env->spr[SPR_BOOKE_MAS1] |= (missed_tid << MAS1_TID_SHIFT); + + + /* next victim logic */ + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; + env->last_way++; + env->last_way &= booke206_tlb_ways(env, 0) - 1; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; +} + +/* Perform address translation */ +/* TODO: Split this by mmu_model. */ +static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + mmu_ctx_t ctx; + int type; + int ret; + + if (access_type == MMU_INST_FETCH) { + /* code access */ + type = ACCESS_CODE; + } else if (guest_visible) { + /* data access */ + type = env->access_type; + } else { + type = ACCESS_INT; + } + + ret = get_physical_address_wtlb(env, &ctx, eaddr, access_type, + type, mmu_idx); + if (ret == 0) { + *raddrp = ctx.raddr; + *protp = ctx.prot; + *psizep = TARGET_PAGE_BITS; + return true; + } + + if (guest_visible) { + LOG_MMU_STATE(cs); + if (type == ACCESS_CODE) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + cs->exception_index = POWERPC_EXCP_IFTLB; + env->error_code = 1 << 18; + env->spr[SPR_IMISS] = eaddr; + env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; + goto tlb_miss; + case POWERPC_MMU_SOFT_74xx: + cs->exception_index = POWERPC_EXCP_IFTLB; + goto tlb_miss_74xx; + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + env->spr[SPR_40x_ESR] = 0x00000000; + break; + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, eaddr, 2, mmu_idx); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); + break; + case POWERPC_MMU_MPC8xx: + cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); + case POWERPC_MMU_REAL: + cpu_abort(cs, "PowerPC in real mode should never raise " + "any MMU exceptions\n"); + default: + cpu_abort(cs, "Unknown or invalid MMU model\n"); + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + break; + case -3: + /* No execute protection violation */ + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->spr[SPR_BOOKE_ESR] = 0x00000000; + } + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + break; + case -4: + /* Direct store exception */ + /* No code fetch is allowed in direct-store areas */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + break; + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + if (access_type == MMU_DATA_STORE) { + cs->exception_index = POWERPC_EXCP_DSTLB; + env->error_code = 1 << 16; + } else { + cs->exception_index = POWERPC_EXCP_DLTLB; + env->error_code = 0; + } + env->spr[SPR_DMISS] = eaddr; + env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; + tlb_miss: + env->error_code |= ctx.key << 19; + env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, ctx.hash[0]); + env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, ctx.hash[1]); + break; + case POWERPC_MMU_SOFT_74xx: + if (access_type == MMU_DATA_STORE) { + cs->exception_index = POWERPC_EXCP_DSTLB; + } else { + cs->exception_index = POWERPC_EXCP_DLTLB; + } + tlb_miss_74xx: + /* Implement LRU algorithm */ + env->error_code = ctx.key << 19; + env->spr[SPR_TLBMISS] = (eaddr & ~((target_ulong)0x3)) | + ((env->last_way + 1) & (env->nb_ways - 1)); + env->spr[SPR_PTEHI] = 0x80000000 | ctx.ptem; + break; + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] = 0x00800000; + } else { + env->spr[SPR_40x_ESR] = 0x00000000; + } + break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + case POWERPC_MMU_REAL: + cpu_abort(cs, "PowerPC in real mode should never raise " + "any MMU exceptions\n"); + default: + cpu_abort(cs, "Unknown or invalid MMU model\n"); + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + if (env->mmu_model == POWERPC_MMU_SOFT_4xx + || env->mmu_model == POWERPC_MMU_SOFT_4xx_Z) { + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] |= 0x00800000; + } + } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + } else { + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x0A000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + } + break; + case -4: + /* Direct store exception */ + switch (type) { + case ACCESS_FLOAT: + /* Floating point load/store */ + cs->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = POWERPC_EXCP_ALIGN_FP; + env->spr[SPR_DAR] = eaddr; + break; + case ACCESS_RES: + /* lwarx, ldarx or stwcx. */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x06000000; + } else { + env->spr[SPR_DSISR] = 0x04000000; + } + break; + case ACCESS_EXT: + /* eciwx or ecowx */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x06100000; + } else { + env->spr[SPR_DSISR] = 0x04100000; + } + break; + default: + printf("DSI: invalid exception (%d)\n", ret); + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = + POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; + env->spr[SPR_DAR] = eaddr; + break; + } + break; + } + } + } + return false; +} + +/*****************************************************************************/ + +bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) +{ + switch (cpu->env.mmu_model) { +#if defined(TARGET_PPC64) + case POWERPC_MMU_3_00: + if (ppc64_v3_radix(cpu)) { + return ppc_radix64_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); + } + /* fall through */ + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: + return ppc_hash64_xlate(cpu, eaddr, access_type, + raddrp, psizep, protp, mmu_idx, guest_visible); +#endif + + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + return ppc_hash32_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); + + default: + return ppc_jumbo_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); + } +} + +hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + hwaddr raddr; + int s, p; + + /* + * Some MMUs have separate TLBs for code and data. If we only + * try an MMU_DATA_LOAD, we may not be able to read instructions + * mapped by code TLBs, so we also try a MMU_INST_FETCH. + */ + if (ppc_xlate(cpu, addr, MMU_DATA_LOAD, &raddr, &s, &p, + cpu_mmu_index(&cpu->env, false), false) || + ppc_xlate(cpu, addr, MMU_INST_FETCH, &raddr, &s, &p, + cpu_mmu_index(&cpu->env, true), false)) { + return raddr & TARGET_PAGE_MASK; + } + return -1; +} diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 869d24d301..2cb98c5169 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -33,23 +33,14 @@ #include "internal.h" #include "mmu-book3s-v3.h" #include "mmu-radix64.h" - -#ifdef CONFIG_TCG #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" -#endif -/* #define DEBUG_MMU */ + /* #define DEBUG_BATS */ /* #define DEBUG_SOFTWARE_TLB */ /* #define DUMP_PAGE_TABLES */ /* #define FLUSH_ALL_TLBS */ -#ifdef DEBUG_MMU -# define LOG_MMU_STATE(cpu) log_cpu_state_mask(CPU_LOG_MMU, (cpu), 0) -#else -# define LOG_MMU_STATE(cpu) do { } while (0) -#endif - #ifdef DEBUG_SOFTWARE_TLB # define LOG_SWTLB(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) #else @@ -65,161 +56,7 @@ /*****************************************************************************/ /* PowerPC MMU emulation */ -/* Context used internally during MMU translations */ -typedef struct mmu_ctx_t mmu_ctx_t; -struct mmu_ctx_t { - hwaddr raddr; /* Real address */ - hwaddr eaddr; /* Effective address */ - int prot; /* Protection bits */ - hwaddr hash[2]; /* Pagetable hash values */ - target_ulong ptem; /* Virtual segment ID | API */ - int key; /* Access key */ - int nx; /* Non-execute area */ -}; - -/* Common routines used by software and hardware TLBs emulation */ -static inline int pte_is_valid(target_ulong pte0) -{ - return pte0 & 0x80000000 ? 1 : 0; -} - -static inline void pte_invalidate(target_ulong *pte0) -{ - *pte0 &= ~0x80000000; -} - -#define PTE_PTEM_MASK 0x7FFFFFBF -#define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) - -static int pp_check(int key, int pp, int nx) -{ - int access; - - /* Compute access rights */ - access = 0; - if (key == 0) { - switch (pp) { - case 0x0: - case 0x1: - case 0x2: - access |= PAGE_WRITE; - /* fall through */ - case 0x3: - access |= PAGE_READ; - break; - } - } else { - switch (pp) { - case 0x0: - access = 0; - break; - case 0x1: - case 0x3: - access = PAGE_READ; - break; - case 0x2: - access = PAGE_READ | PAGE_WRITE; - break; - } - } - if (nx == 0) { - access |= PAGE_EXEC; - } - - return access; -} - -static int check_prot(int prot, MMUAccessType access_type) -{ - return prot & prot_for_access_type(access_type) ? 0 : -2; -} - -static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, - MMUAccessType access_type) -{ - target_ulong ptem, mmask; - int access, ret, pteh, ptev, pp; - - ret = -1; - /* Check validity and table match */ - ptev = pte_is_valid(pte0); - pteh = (pte0 >> 6) & 1; - if (ptev && h == pteh) { - /* Check vsid & api */ - ptem = pte0 & PTE_PTEM_MASK; - mmask = PTE_CHECK_MASK; - pp = pte1 & 0x00000003; - if (ptem == ctx->ptem) { - if (ctx->raddr != (hwaddr)-1ULL) { - /* all matches should have equal RPN, WIMG & PP */ - if ((ctx->raddr & mmask) != (pte1 & mmask)) { - qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); - return -3; - } - } - /* Compute access rights */ - access = pp_check(ctx->key, pp, ctx->nx); - /* Keep the matching PTE information */ - ctx->raddr = pte1; - ctx->prot = access; - ret = check_prot(ctx->prot, access_type); - if (ret == 0) { - /* Access granted */ - qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); - } else { - /* Access right violation */ - qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); - } - } - } - - return ret; -} - -static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, - int ret, MMUAccessType access_type) -{ - int store = 0; - - /* Update page flags */ - if (!(*pte1p & 0x00000100)) { - /* Update accessed flag */ - *pte1p |= 0x00000100; - store = 1; - } - if (!(*pte1p & 0x00000080)) { - if (access_type == MMU_DATA_STORE && ret == 0) { - /* Update changed flag */ - *pte1p |= 0x00000080; - store = 1; - } else { - /* Force page fault for first write access */ - ctx->prot &= ~PAGE_WRITE; - } - } - - return store; -} - /* Software driven TLB helpers */ -static inline int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, - int way, int is_code) -{ - int nr; - - /* Select TLB num in a way from address */ - nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1); - /* Select TLB way */ - nr += env->tlb_per_way * way; - /* 6xx have separate TLBs for instructions and data */ - if (is_code && env->id_tlbs == 1) { - nr += env->nb_tlb; - } - - return nr; -} - static inline void ppc6xx_tlb_invalidate_all(CPUPPCState *env) { ppc6xx_tlb_t *tlb; @@ -270,7 +107,6 @@ static inline void ppc6xx_tlb_invalidate_virt(CPUPPCState *env, ppc6xx_tlb_invalidate_virt2(env, eaddr, is_code, 0); } -#ifdef CONFIG_TCG static void ppc6xx_tlb_store(CPUPPCState *env, target_ulong EPN, int way, int is_code, target_ulong pte0, target_ulong pte1) { @@ -289,348 +125,7 @@ static void ppc6xx_tlb_store(CPUPPCState *env, target_ulong EPN, int way, /* Store last way for LRU mechanism */ env->last_way = way; } -#endif -static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, MMUAccessType access_type) -{ - ppc6xx_tlb_t *tlb; - int nr, best, way; - int ret; - - best = -1; - ret = -1; /* No TLB found */ - for (way = 0; way < env->nb_ways; way++) { - nr = ppc6xx_tlb_getnum(env, eaddr, way, access_type == MMU_INST_FETCH); - tlb = &env->tlb.tlb6[nr]; - /* This test "emulates" the PTE index match for hardware TLBs */ - if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { - LOG_SWTLB("TLB %d/%d %s [" TARGET_FMT_lx " " TARGET_FMT_lx - "] <> " TARGET_FMT_lx "\n", nr, env->nb_tlb, - pte_is_valid(tlb->pte0) ? "valid" : "inval", - tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE, eaddr); - continue; - } - LOG_SWTLB("TLB %d/%d %s " TARGET_FMT_lx " <> " TARGET_FMT_lx " " - TARGET_FMT_lx " %c %c\n", nr, env->nb_tlb, - pte_is_valid(tlb->pte0) ? "valid" : "inval", - tlb->EPN, eaddr, tlb->pte1, - access_type == MMU_DATA_STORE ? 'S' : 'L', - access_type == MMU_INST_FETCH ? 'I' : 'D'); - switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, - 0, access_type)) { - case -3: - /* TLB inconsistency */ - return -1; - case -2: - /* Access violation */ - ret = -2; - best = nr; - break; - case -1: - default: - /* No match */ - break; - case 0: - /* access granted */ - /* - * XXX: we should go on looping to check all TLBs - * consistency but we can speed-up the whole thing as - * the result would be undefined if TLBs are not - * consistent. - */ - ret = 0; - best = nr; - goto done; - } - } - if (best != -1) { - done: - LOG_SWTLB("found TLB at addr " TARGET_FMT_plx " prot=%01x ret=%d\n", - ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); - /* Update page flags */ - pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, access_type); - } - - return ret; -} - -/* Perform BAT hit & translation */ -static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, - int *validp, int *protp, target_ulong *BATu, - target_ulong *BATl) -{ - target_ulong bl; - int pp, valid, prot; - - bl = (*BATu & 0x00001FFC) << 15; - valid = 0; - prot = 0; - if (((msr_pr == 0) && (*BATu & 0x00000002)) || - ((msr_pr != 0) && (*BATu & 0x00000001))) { - valid = 1; - pp = *BATl & 0x00000003; - if (pp != 0) { - prot = PAGE_READ | PAGE_EXEC; - if (pp == 0x2) { - prot |= PAGE_WRITE; - } - } - } - *blp = bl; - *validp = valid; - *protp = prot; -} - -static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong virtual, MMUAccessType access_type) -{ - target_ulong *BATlt, *BATut, *BATu, *BATl; - target_ulong BEPIl, BEPIu, bl; - int i, valid, prot; - int ret = -1; - bool ifetch = access_type == MMU_INST_FETCH; - - LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', virtual); - if (ifetch) { - BATlt = env->IBAT[1]; - BATut = env->IBAT[0]; - } else { - BATlt = env->DBAT[1]; - BATut = env->DBAT[0]; - } - for (i = 0; i < env->nb_BATs; i++) { - BATu = &BATut[i]; - BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); - LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx - " BATl " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl); - if ((virtual & 0xF0000000) == BEPIu && - ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { - /* BAT matches */ - if (valid != 0) { - /* Get physical address */ - ctx->raddr = (*BATl & 0xF0000000) | - ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | - (virtual & 0x0001F000); - /* Compute access rights */ - ctx->prot = prot; - ret = check_prot(ctx->prot, access_type); - if (ret == 0) { - LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", - i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', - ctx->prot & PAGE_WRITE ? 'W' : '-'); - } - break; - } - } - } - if (ret < 0) { -#if defined(DEBUG_BATS) - if (qemu_log_enabled()) { - LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", virtual); - for (i = 0; i < 4; i++) { - BATu = &BATut[i]; - BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bl = (*BATu & 0x00001FFC) << 15; - LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx - " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " - TARGET_FMT_lx " " TARGET_FMT_lx "\n", - __func__, ifetch ? 'I' : 'D', i, virtual, - *BATu, *BATl, BEPIu, BEPIl, bl); - } - } -#endif - } - /* No hit */ - return ret; -} - -/* Perform segment based translation */ -static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, MMUAccessType access_type, - int type) -{ - PowerPCCPU *cpu = env_archcpu(env); - hwaddr hash; - target_ulong vsid; - int ds, pr, target_page_bits; - int ret; - target_ulong sr, pgidx; - - pr = msr_pr; - ctx->eaddr = eaddr; - - sr = env->sr[eaddr >> 28]; - ctx->key = (((sr & 0x20000000) && (pr != 0)) || - ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; - ds = sr & 0x80000000 ? 1 : 0; - ctx->nx = sr & 0x10000000 ? 1 : 0; - vsid = sr & 0x00FFFFFF; - target_page_bits = TARGET_PAGE_BITS; - qemu_log_mask(CPU_LOG_MMU, - "Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx - " nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx - " ir=%d dr=%d pr=%d %d t=%d\n", - eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, - (int)msr_dr, pr != 0 ? 1 : 0, access_type == MMU_DATA_STORE, type); - pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; - hash = vsid ^ pgidx; - ctx->ptem = (vsid << 7) | (pgidx >> 10); - - qemu_log_mask(CPU_LOG_MMU, - "pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", - ctx->key, ds, ctx->nx, vsid); - ret = -1; - if (!ds) { - /* Check if instruction fetch is allowed, if needed */ - if (type != ACCESS_CODE || ctx->nx == 0) { - /* Page address translation */ - qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx - " htab_mask " TARGET_FMT_plx - " hash " TARGET_FMT_plx "\n", - ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; - - /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; - /* Software TLB search */ - ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); -#if defined(DUMP_PAGE_TABLES) - if (qemu_loglevel_mask(CPU_LOG_MMU)) { - CPUState *cs = env_cpu(env); - hwaddr curaddr; - uint32_t a0, a1, a2, a3; - - qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx - "\n", ppc_hash32_hpt_base(cpu), - ppc_hash32_hpt_mask(cpu) + 0x80); - for (curaddr = ppc_hash32_hpt_base(cpu); - curaddr < (ppc_hash32_hpt_base(cpu) - + ppc_hash32_hpt_mask(cpu) + 0x80); - curaddr += 16) { - a0 = ldl_phys(cs->as, curaddr); - a1 = ldl_phys(cs->as, curaddr + 4); - a2 = ldl_phys(cs->as, curaddr + 8); - a3 = ldl_phys(cs->as, curaddr + 12); - if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { - qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", - curaddr, a0, a1, a2, a3); - } - } - } -#endif - } else { - qemu_log_mask(CPU_LOG_MMU, "No access allowed\n"); - ret = -3; - } - } else { - target_ulong sr; - - qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); - /* Direct-store segment : absolutely *BUGGY* for now */ - - /* - * Direct-store implies a 32-bit MMU. - * Check the Segment Register's bus unit ID (BUID). - */ - sr = env->sr[eaddr >> 28]; - if ((sr & 0x1FF00000) >> 20 == 0x07f) { - /* - * Memory-forced I/O controller interface access - * - * If T=1 and BUID=x'07F', the 601 performs a memory - * access to SR[28-31] LA[4-31], bypassing all protection - * mechanisms. - */ - ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); - ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return 0; - } - - switch (type) { - case ACCESS_INT: - /* Integer load/store : only access allowed */ - break; - case ACCESS_CODE: - /* No code fetch is allowed in direct-store areas */ - return -4; - case ACCESS_FLOAT: - /* Floating point load/store */ - return -4; - case ACCESS_RES: - /* lwarx, ldarx or srwcx. */ - return -4; - case ACCESS_CACHE: - /* - * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi - * - * Should make the instruction do no-op. As it already do - * no-op, it's quite easy :-) - */ - ctx->raddr = eaddr; - return 0; - case ACCESS_EXT: - /* eciwx or ecowx */ - return -4; - default: - qemu_log_mask(CPU_LOG_MMU, "ERROR: instruction should not need " - "address translation\n"); - return -4; - } - if ((access_type == MMU_DATA_STORE || ctx->key != 1) && - (access_type == MMU_DATA_LOAD || ctx->key != 0)) { - ctx->raddr = eaddr; - ret = 2; - } else { - ret = -2; - } - } - - return ret; -} - -/* Generic TLB check function for embedded PowerPC implementations */ -static int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddrp, - target_ulong address, uint32_t pid, int ext, - int i) -{ - target_ulong mask; - - /* Check valid flag */ - if (!(tlb->prot & PAGE_VALID)) { - return -1; - } - mask = ~(tlb->size - 1); - LOG_SWTLB("%s: TLB %d address " TARGET_FMT_lx " PID %u <=> " TARGET_FMT_lx - " " TARGET_FMT_lx " %u %x\n", __func__, i, address, pid, tlb->EPN, - mask, (uint32_t)tlb->PID, tlb->prot); - /* Check PID */ - if (tlb->PID != 0 && tlb->PID != pid) { - return -1; - } - /* Check effective address */ - if ((address & mask) != tlb->EPN) { - return -1; - } - *raddrp = (tlb->RPN & mask) | (address & ~mask); - if (ext) { - /* Extend the physical address to 36 bits */ - *raddrp |= (uint64_t)(tlb->RPN & 0xF) << 32; - } - - return 0; -} - -#ifdef CONFIG_TCG /* Generic TLB search function for PowerPC embedded implementations */ static int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid) @@ -651,7 +146,6 @@ static int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, return ret; } -#endif /* Helpers specific to PowerPC 40x implementations */ static inline void ppc4xx_tlb_invalidate_all(CPUPPCState *env) @@ -666,166 +160,6 @@ static inline void ppc4xx_tlb_invalidate_all(CPUPPCState *env) tlb_flush(env_cpu(env)); } -static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, - MMUAccessType access_type) -{ - ppcemb_tlb_t *tlb; - hwaddr raddr; - int i, ret, zsel, zpr, pr; - - ret = -1; - raddr = (hwaddr)-1ULL; - pr = msr_pr; - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, address, - env->spr[SPR_40x_PID], 0, i) < 0) { - continue; - } - zsel = (tlb->attr >> 4) & 0xF; - zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; - LOG_SWTLB("%s: TLB %d zsel %d zpr %d ty %d attr %08x\n", - __func__, i, zsel, zpr, access_type, tlb->attr); - /* Check execute enable bit */ - switch (zpr) { - case 0x2: - if (pr != 0) { - goto check_perms; - } - /* fall through */ - case 0x3: - /* All accesses granted */ - ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - ret = 0; - break; - case 0x0: - if (pr != 0) { - /* Raise Zone protection fault. */ - env->spr[SPR_40x_ESR] = 1 << 22; - ctx->prot = 0; - ret = -2; - break; - } - /* fall through */ - case 0x1: - check_perms: - /* Check from TLB entry */ - ctx->prot = tlb->prot; - ret = check_prot(ctx->prot, access_type); - if (ret == -2) { - env->spr[SPR_40x_ESR] = 0; - } - break; - } - if (ret >= 0) { - ctx->raddr = raddr; - LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, ctx->raddr, ctx->prot, - ret); - return 0; - } - } - LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, raddr, ctx->prot, ret); - - return ret; -} - -void store_40x_sler(CPUPPCState *env, uint32_t val) -{ - /* XXX: TO BE FIXED */ - if (val != 0x00000000) { - cpu_abort(env_cpu(env), - "Little-endian regions are not supported by now\n"); - } - env->spr[SPR_405_SLER] = val; -} - -static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddr, int *prot, target_ulong address, - MMUAccessType access_type, int i) -{ - int prot2; - - if (ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID], - !env->nb_pids, i) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID1] && - ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1], 0, i) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID2] && - ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2], 0, i) >= 0) { - goto found_tlb; - } - - LOG_SWTLB("%s: TLB entry not found\n", __func__); - return -1; - -found_tlb: - - if (msr_pr != 0) { - prot2 = tlb->prot & 0xF; - } else { - prot2 = (tlb->prot >> 4) & 0xF; - } - - /* Check the address space */ - if ((access_type == MMU_INST_FETCH ? msr_ir : msr_dr) != (tlb->attr & 1)) { - LOG_SWTLB("%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = prot2; - if (prot2 & prot_for_access_type(access_type)) { - LOG_SWTLB("%s: good TLB!\n", __func__); - return 0; - } - - LOG_SWTLB("%s: no prot match: %x\n", __func__, prot2); - return access_type == MMU_INST_FETCH ? -3 : -2; -} - -static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, - MMUAccessType access_type) -{ - ppcemb_tlb_t *tlb; - hwaddr raddr; - int i, ret; - - ret = -1; - raddr = (hwaddr)-1ULL; - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, - access_type, i); - if (ret != -1) { - break; - } - } - - if (ret >= 0) { - ctx->raddr = raddr; - LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, ctx->raddr, ctx->prot, - ret); - } else { - LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, raddr, ctx->prot, ret); - } - - return ret; -} - -#ifdef CONFIG_TCG static void booke206_flush_tlb(CPUPPCState *env, int flags, const int check_iprot) { @@ -847,879 +181,16 @@ static void booke206_flush_tlb(CPUPPCState *env, int flags, tlb_flush(env_cpu(env)); } -#endif -static hwaddr booke206_tlb_to_page_size(CPUPPCState *env, - ppcmas_tlb_t *tlb) -{ - int tlbm_size; - - tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; - - return 1024ULL << tlbm_size; -} - -/* TLB check function for MAS based SoftTLBs */ -static int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, - hwaddr *raddrp, target_ulong address, - uint32_t pid) -{ - hwaddr mask; - uint32_t tlb_pid; - - if (!msr_cm) { - /* In 32bit mode we can only address 32bit EAs */ - address = (uint32_t)address; - } - - /* Check valid flag */ - if (!(tlb->mas1 & MAS1_VALID)) { - return -1; - } - - mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); - LOG_SWTLB("%s: TLB ADDR=0x" TARGET_FMT_lx " PID=0x%x MAS1=0x%x MAS2=0x%" - PRIx64 " mask=0x%" HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" - PRIx32 "\n", __func__, address, pid, tlb->mas1, tlb->mas2, mask, - tlb->mas7_3, tlb->mas8); - - /* Check PID */ - tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; - if (tlb_pid != 0 && tlb_pid != pid) { - return -1; - } - - /* Check effective address */ - if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { - return -1; - } - - if (raddrp) { - *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); - } - - return 0; -} - -static bool is_epid_mmu(int mmu_idx) -{ - return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD; -} - -static uint32_t mmubooke206_esr(int mmu_idx, MMUAccessType access_type) -{ - uint32_t esr = 0; - if (access_type == MMU_DATA_STORE) { - esr |= ESR_ST; - } - if (is_epid_mmu(mmu_idx)) { - esr |= ESR_EPID; - } - return esr; -} - -/* - * Get EPID register given the mmu_idx. If this is regular load, - * construct the EPID access bits from current processor state - * - * Get the effective AS and PR bits and the PID. The PID is returned - * only if EPID load is requested, otherwise the caller must detect - * the correct EPID. Return true if valid EPID is returned. - */ -static bool mmubooke206_get_as(CPUPPCState *env, - int mmu_idx, uint32_t *epid_out, - bool *as_out, bool *pr_out) -{ - if (is_epid_mmu(mmu_idx)) { - uint32_t epidr; - if (mmu_idx == PPC_TLB_EPID_STORE) { - epidr = env->spr[SPR_BOOKE_EPSC]; - } else { - epidr = env->spr[SPR_BOOKE_EPLC]; - } - *epid_out = (epidr & EPID_EPID) >> EPID_EPID_SHIFT; - *as_out = !!(epidr & EPID_EAS); - *pr_out = !!(epidr & EPID_EPR); - return true; - } else { - *as_out = msr_ds; - *pr_out = msr_pr; - return false; - } -} - -/* Check if the tlb found by hashing really matches */ -static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, - hwaddr *raddr, int *prot, - target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - int prot2 = 0; - uint32_t epid; - bool as, pr; - bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); - - if (!use_epid) { - if (ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID]) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID1] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1]) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID2] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2]) >= 0) { - goto found_tlb; - } - } else { - if (ppcmas_tlb_check(env, tlb, raddr, address, epid) >= 0) { - goto found_tlb; - } - } - - LOG_SWTLB("%s: TLB entry not found\n", __func__); - return -1; - -found_tlb: - - if (pr) { - if (tlb->mas7_3 & MAS3_UR) { - prot2 |= PAGE_READ; - } - if (tlb->mas7_3 & MAS3_UW) { - prot2 |= PAGE_WRITE; - } - if (tlb->mas7_3 & MAS3_UX) { - prot2 |= PAGE_EXEC; - } - } else { - if (tlb->mas7_3 & MAS3_SR) { - prot2 |= PAGE_READ; - } - if (tlb->mas7_3 & MAS3_SW) { - prot2 |= PAGE_WRITE; - } - if (tlb->mas7_3 & MAS3_SX) { - prot2 |= PAGE_EXEC; - } - } - - /* Check the address space and permissions */ - if (access_type == MMU_INST_FETCH) { - /* There is no way to fetch code using epid load */ - assert(!use_epid); - as = msr_ir; - } - - if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { - LOG_SWTLB("%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = prot2; - if (prot2 & prot_for_access_type(access_type)) { - LOG_SWTLB("%s: good TLB!\n", __func__); - return 0; - } - - LOG_SWTLB("%s: no prot match: %x\n", __func__, prot2); - return access_type == MMU_INST_FETCH ? -3 : -2; -} - -static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, - MMUAccessType access_type, - int mmu_idx) -{ - ppcmas_tlb_t *tlb; - hwaddr raddr; - int i, j, ret; - - ret = -1; - raddr = (hwaddr)-1ULL; - - for (i = 0; i < BOOKE206_MAX_TLBN; i++) { - int ways = booke206_tlb_ways(env, i); - - for (j = 0; j < ways; j++) { - tlb = booke206_get_tlbm(env, i, address, j); - if (!tlb) { - continue; - } - ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, - access_type, mmu_idx); - if (ret != -1) { - goto found_tlb; - } - } - } - -found_tlb: - - if (ret >= 0) { - ctx->raddr = raddr; - LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, ctx->raddr, ctx->prot, - ret); - } else { - LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, raddr, ctx->prot, ret); - } - - return ret; -} - -static const char *book3e_tsize_to_str[32] = { - "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K", - "1M", "2M", "4M", "8M", "16M", "32M", "64M", "128M", "256M", "512M", - "1G", "2G", "4G", "8G", "16G", "32G", "64G", "128G", "256G", "512G", - "1T", "2T" -}; - -static void mmubooke_dump_mmu(CPUPPCState *env) -{ - ppcemb_tlb_t *entry; - int i; - - if (kvm_enabled() && !env->kvm_sw_tlb) { - qemu_printf("Cannot access KVM TLB\n"); - return; - } - - qemu_printf("\nTLB:\n"); - qemu_printf("Effective Physical Size PID Prot " - "Attr\n"); - - entry = &env->tlb.tlbe[0]; - for (i = 0; i < env->nb_tlb; i++, entry++) { - hwaddr ea, pa; - target_ulong mask; - uint64_t size = (uint64_t)entry->size; - char size_buf[20]; - - /* Check valid flag */ - if (!(entry->prot & PAGE_VALID)) { - continue; - } - - mask = ~(entry->size - 1); - ea = entry->EPN & mask; - pa = entry->RPN & mask; - /* Extend the physical address to 36 bits */ - pa |= (hwaddr)(entry->RPN & 0xF) << 32; - if (size >= 1 * MiB) { - snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "M", size / MiB); - } else { - snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "k", size / KiB); - } - qemu_printf("0x%016" PRIx64 " 0x%016" PRIx64 " %s %-5u %08x %08x\n", - (uint64_t)ea, (uint64_t)pa, size_buf, (uint32_t)entry->PID, - entry->prot, entry->attr); - } - -} - -static void mmubooke206_dump_one_tlb(CPUPPCState *env, int tlbn, int offset, - int tlbsize) -{ - ppcmas_tlb_t *entry; - int i; - - qemu_printf("\nTLB%d:\n", tlbn); - qemu_printf("Effective Physical Size TID TS SRWX" - " URWX WIMGE U0123\n"); - - entry = &env->tlb.tlbm[offset]; - for (i = 0; i < tlbsize; i++, entry++) { - hwaddr ea, pa, size; - int tsize; - - if (!(entry->mas1 & MAS1_VALID)) { - continue; - } - - tsize = (entry->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; - size = 1024ULL << tsize; - ea = entry->mas2 & ~(size - 1); - pa = entry->mas7_3 & ~(size - 1); - - qemu_printf("0x%016" PRIx64 " 0x%016" PRIx64 " %4s %-5u %1u S%c%c%c" - "U%c%c%c %c%c%c%c%c U%c%c%c%c\n", - (uint64_t)ea, (uint64_t)pa, - book3e_tsize_to_str[tsize], - (entry->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT, - (entry->mas1 & MAS1_TS) >> MAS1_TS_SHIFT, - entry->mas7_3 & MAS3_SR ? 'R' : '-', - entry->mas7_3 & MAS3_SW ? 'W' : '-', - entry->mas7_3 & MAS3_SX ? 'X' : '-', - entry->mas7_3 & MAS3_UR ? 'R' : '-', - entry->mas7_3 & MAS3_UW ? 'W' : '-', - entry->mas7_3 & MAS3_UX ? 'X' : '-', - entry->mas2 & MAS2_W ? 'W' : '-', - entry->mas2 & MAS2_I ? 'I' : '-', - entry->mas2 & MAS2_M ? 'M' : '-', - entry->mas2 & MAS2_G ? 'G' : '-', - entry->mas2 & MAS2_E ? 'E' : '-', - entry->mas7_3 & MAS3_U0 ? '0' : '-', - entry->mas7_3 & MAS3_U1 ? '1' : '-', - entry->mas7_3 & MAS3_U2 ? '2' : '-', - entry->mas7_3 & MAS3_U3 ? '3' : '-'); - } -} - -static void mmubooke206_dump_mmu(CPUPPCState *env) -{ - int offset = 0; - int i; - - if (kvm_enabled() && !env->kvm_sw_tlb) { - qemu_printf("Cannot access KVM TLB\n"); - return; - } - - for (i = 0; i < BOOKE206_MAX_TLBN; i++) { - int size = booke206_tlb_size(env, i); - - if (size == 0) { - continue; - } - - mmubooke206_dump_one_tlb(env, i, offset, size); - offset += size; - } -} - -static void mmu6xx_dump_BATs(CPUPPCState *env, int type) -{ - target_ulong *BATlt, *BATut, *BATu, *BATl; - target_ulong BEPIl, BEPIu, bl; - int i; - - switch (type) { - case ACCESS_CODE: - BATlt = env->IBAT[1]; - BATut = env->IBAT[0]; - break; - default: - BATlt = env->DBAT[1]; - BATut = env->DBAT[0]; - break; - } - - for (i = 0; i < env->nb_BATs; i++) { - BATu = &BATut[i]; - BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bl = (*BATu & 0x00001FFC) << 15; - qemu_printf("%s BAT%d BATu " TARGET_FMT_lx - " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " - TARGET_FMT_lx " " TARGET_FMT_lx "\n", - type == ACCESS_CODE ? "code" : "data", i, - *BATu, *BATl, BEPIu, BEPIl, bl); - } -} - -static void mmu6xx_dump_mmu(CPUPPCState *env) -{ - PowerPCCPU *cpu = env_archcpu(env); - ppc6xx_tlb_t *tlb; - target_ulong sr; - int type, way, entry, i; - - qemu_printf("HTAB base = 0x%"HWADDR_PRIx"\n", ppc_hash32_hpt_base(cpu)); - qemu_printf("HTAB mask = 0x%"HWADDR_PRIx"\n", ppc_hash32_hpt_mask(cpu)); - - qemu_printf("\nSegment registers:\n"); - for (i = 0; i < 32; i++) { - sr = env->sr[i]; - if (sr & 0x80000000) { - qemu_printf("%02d T=%d Ks=%d Kp=%d BUID=0x%03x " - "CNTLR_SPEC=0x%05x\n", i, - sr & 0x80000000 ? 1 : 0, sr & 0x40000000 ? 1 : 0, - sr & 0x20000000 ? 1 : 0, (uint32_t)((sr >> 20) & 0x1FF), - (uint32_t)(sr & 0xFFFFF)); - } else { - qemu_printf("%02d T=%d Ks=%d Kp=%d N=%d VSID=0x%06x\n", i, - sr & 0x80000000 ? 1 : 0, sr & 0x40000000 ? 1 : 0, - sr & 0x20000000 ? 1 : 0, sr & 0x10000000 ? 1 : 0, - (uint32_t)(sr & 0x00FFFFFF)); - } - } - - qemu_printf("\nBATs:\n"); - mmu6xx_dump_BATs(env, ACCESS_INT); - mmu6xx_dump_BATs(env, ACCESS_CODE); - - if (env->id_tlbs != 1) { - qemu_printf("ERROR: 6xx MMU should have separated TLB" - " for code and data\n"); - } - - qemu_printf("\nTLBs [EPN EPN + SIZE]\n"); - - for (type = 0; type < 2; type++) { - for (way = 0; way < env->nb_ways; way++) { - for (entry = env->nb_tlb * type + env->tlb_per_way * way; - entry < (env->nb_tlb * type + env->tlb_per_way * (way + 1)); - entry++) { - - tlb = &env->tlb.tlb6[entry]; - qemu_printf("%s TLB %02d/%02d way:%d %s [" - TARGET_FMT_lx " " TARGET_FMT_lx "]\n", - type ? "code" : "data", entry % env->nb_tlb, - env->nb_tlb, way, - pte_is_valid(tlb->pte0) ? "valid" : "inval", - tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE); - } - } - } -} - -void dump_mmu(CPUPPCState *env) -{ - switch (env->mmu_model) { - case POWERPC_MMU_BOOKE: - mmubooke_dump_mmu(env); - break; - case POWERPC_MMU_BOOKE206: - mmubooke206_dump_mmu(env); - break; - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_74xx: - mmu6xx_dump_mmu(env); - break; -#if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_03: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_07: - dump_slb(env_archcpu(env)); - break; - case POWERPC_MMU_3_00: - if (ppc64_v3_radix(env_archcpu(env))) { - qemu_log_mask(LOG_UNIMP, "%s: the PPC64 MMU is unsupported\n", - __func__); - } else { - dump_slb(env_archcpu(env)); - } - break; -#endif - default: - qemu_log_mask(LOG_UNIMP, "%s: unimplemented\n", __func__); - } -} - -static int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, - MMUAccessType access_type) -{ - int in_plb, ret; - - ctx->raddr = eaddr; - ctx->prot = PAGE_READ | PAGE_EXEC; - ret = 0; - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_74xx: - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_REAL: - case POWERPC_MMU_BOOKE: - ctx->prot |= PAGE_WRITE; - break; - - case POWERPC_MMU_SOFT_4xx_Z: - if (unlikely(msr_pe != 0)) { - /* - * 403 family add some particular protections, using - * PBL/PBU registers for accesses with no translation. - */ - in_plb = - /* Check PLB validity */ - (env->pb[0] < env->pb[1] && - /* and address in plb area */ - eaddr >= env->pb[0] && eaddr < env->pb[1]) || - (env->pb[2] < env->pb[3] && - eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0; - if (in_plb ^ msr_px) { - /* Access in protected area */ - if (access_type == MMU_DATA_STORE) { - /* Access is not allowed */ - ret = -2; - } - } else { - /* Read-write access is allowed */ - ctx->prot |= PAGE_WRITE; - } - } - break; - - default: - /* Caller's checks mean we should never get here for other models */ - abort(); - return -1; - } - - return ret; -} - -static int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, - MMUAccessType access_type, int type, - int mmu_idx) -{ - int ret = -1; - bool real_mode = (type == ACCESS_CODE && msr_ir == 0) - || (type != ACCESS_CODE && msr_dr == 0); - - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_74xx: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - /* Try to find a BAT */ - if (env->nb_BATs != 0) { - ret = get_bat_6xx_tlb(env, ctx, eaddr, access_type); - } - if (ret < 0) { - /* We didn't match any BAT entry or don't have BATs */ - ret = get_segment_6xx_tlb(env, ctx, eaddr, access_type, type); - } - } - break; - - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_SOFT_4xx_Z: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - ret = mmu40x_get_physical_address(env, ctx, eaddr, access_type); - } - break; - case POWERPC_MMU_BOOKE: - ret = mmubooke_get_physical_address(env, ctx, eaddr, access_type); - break; - case POWERPC_MMU_BOOKE206: - ret = mmubooke206_get_physical_address(env, ctx, eaddr, access_type, - mmu_idx); - break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env_cpu(env), "MPC8xx MMU model is not implemented\n"); - break; - case POWERPC_MMU_REAL: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - cpu_abort(env_cpu(env), - "PowerPC in real mode do not do any translation\n"); - } - return -1; - default: - cpu_abort(env_cpu(env), "Unknown or invalid MMU model\n"); - return -1; - } - - return ret; -} - -#ifdef CONFIG_TCG static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, MMUAccessType access_type, int type) { return get_physical_address_wtlb(env, ctx, eaddr, access_type, type, 0); } -#endif - -static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - uint32_t epid; - bool as, pr; - uint32_t missed_tid = 0; - bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); - - if (access_type == MMU_INST_FETCH) { - as = msr_ir; - } - env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; - env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; - env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; - env->spr[SPR_BOOKE_MAS3] = 0; - env->spr[SPR_BOOKE_MAS6] = 0; - env->spr[SPR_BOOKE_MAS7] = 0; - - /* AS */ - if (as) { - env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; - env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; - } - - env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; - env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; - - if (!use_epid) { - switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { - case MAS4_TIDSELD_PID0: - missed_tid = env->spr[SPR_BOOKE_PID]; - break; - case MAS4_TIDSELD_PID1: - missed_tid = env->spr[SPR_BOOKE_PID1]; - break; - case MAS4_TIDSELD_PID2: - missed_tid = env->spr[SPR_BOOKE_PID2]; - break; - } - env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; - } else { - missed_tid = epid; - env->spr[SPR_BOOKE_MAS6] |= missed_tid << 16; - } - env->spr[SPR_BOOKE_MAS1] |= (missed_tid << MAS1_TID_SHIFT); - /* next victim logic */ - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; - env->last_way++; - env->last_way &= booke206_tlb_ways(env, 0) - 1; - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; -} -/* Perform address translation */ -/* TODO: Split this by mmu_model. */ -static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, - int mmu_idx, bool guest_visible) -{ - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - mmu_ctx_t ctx; - int type; - int ret; - - if (access_type == MMU_INST_FETCH) { - /* code access */ - type = ACCESS_CODE; - } else if (guest_visible) { - /* data access */ - type = env->access_type; - } else { - type = ACCESS_INT; - } - - ret = get_physical_address_wtlb(env, &ctx, eaddr, access_type, - type, mmu_idx); - if (ret == 0) { - *raddrp = ctx.raddr; - *protp = ctx.prot; - *psizep = TARGET_PAGE_BITS; - return true; - } - - if (guest_visible) { - LOG_MMU_STATE(cs); - if (type == ACCESS_CODE) { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - cs->exception_index = POWERPC_EXCP_IFTLB; - env->error_code = 1 << 18; - env->spr[SPR_IMISS] = eaddr; - env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; - goto tlb_miss; - case POWERPC_MMU_SOFT_74xx: - cs->exception_index = POWERPC_EXCP_IFTLB; - goto tlb_miss_74xx; - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_SOFT_4xx_Z: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - env->spr[SPR_40x_ESR] = 0x00000000; - break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, 2, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); - break; - case POWERPC_MMU_MPC8xx: - cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); - default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); - } - break; - case -2: - /* Access rights violation */ - cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x08000000; - break; - case -3: - /* No execute protection violation */ - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_ESR] = 0x00000000; - } - cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x10000000; - break; - case -4: - /* Direct store exception */ - /* No code fetch is allowed in direct-store areas */ - cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x10000000; - break; - } - } else { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - if (access_type == MMU_DATA_STORE) { - cs->exception_index = POWERPC_EXCP_DSTLB; - env->error_code = 1 << 16; - } else { - cs->exception_index = POWERPC_EXCP_DLTLB; - env->error_code = 0; - } - env->spr[SPR_DMISS] = eaddr; - env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; - tlb_miss: - env->error_code |= ctx.key << 19; - env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[0]); - env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[1]); - break; - case POWERPC_MMU_SOFT_74xx: - if (access_type == MMU_DATA_STORE) { - cs->exception_index = POWERPC_EXCP_DSTLB; - } else { - cs->exception_index = POWERPC_EXCP_DLTLB; - } - tlb_miss_74xx: - /* Implement LRU algorithm */ - env->error_code = ctx.key << 19; - env->spr[SPR_TLBMISS] = (eaddr & ~((target_ulong)0x3)) | - ((env->last_way + 1) & (env->nb_ways - 1)); - env->spr[SPR_PTEHI] = 0x80000000 | ctx.ptem; - break; - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_SOFT_4xx_Z: - cs->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] = 0x00800000; - } else { - env->spr[SPR_40x_ESR] = 0x00000000; - } - break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); - default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); - } - break; - case -2: - /* Access rights violation */ - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - if (env->mmu_model == POWERPC_MMU_SOFT_4xx - || env->mmu_model == POWERPC_MMU_SOFT_4xx_Z) { - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] |= 0x00800000; - } - } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - } else { - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x0A000000; - } else { - env->spr[SPR_DSISR] = 0x08000000; - } - } - break; - case -4: - /* Direct store exception */ - switch (type) { - case ACCESS_FLOAT: - /* Floating point load/store */ - cs->exception_index = POWERPC_EXCP_ALIGN; - env->error_code = POWERPC_EXCP_ALIGN_FP; - env->spr[SPR_DAR] = eaddr; - break; - case ACCESS_RES: - /* lwarx, ldarx or stwcx. */ - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x06000000; - } else { - env->spr[SPR_DSISR] = 0x04000000; - } - break; - case ACCESS_EXT: - /* eciwx or ecowx */ - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x06100000; - } else { - env->spr[SPR_DSISR] = 0x04100000; - } - break; - default: - printf("DSI: invalid exception (%d)\n", ret); - cs->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = - POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; - env->spr[SPR_DAR] = eaddr; - break; - } - break; - } - } - } - return false; -} - -#ifdef CONFIG_TCG /*****************************************************************************/ /* BATs management */ #if !defined(FLUSH_ALL_TLBS) @@ -1901,9 +372,7 @@ void helper_store_601_batl(CPUPPCState *env, uint32_t nr, target_ulong value) #endif } } -#endif -#ifdef CONFIG_TCG /*****************************************************************************/ /* TLB management */ void ppc_tlb_invalidate_all(CPUPPCState *env) @@ -1947,9 +416,7 @@ void ppc_tlb_invalidate_all(CPUPPCState *env) break; } } -#endif -#ifdef CONFIG_TCG void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) { #if !defined(FLUSH_ALL_TLBS) @@ -2891,62 +1358,8 @@ void helper_check_tlb_flush_global(CPUPPCState *env) { check_tlb_flush(env, true); } -#endif /* CONFIG_TCG */ -/*****************************************************************************/ -static bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, - int mmu_idx, bool guest_visible) -{ - switch (cpu->env.mmu_model) { -#if defined(TARGET_PPC64) - case POWERPC_MMU_3_00: - if (ppc64_v3_radix(cpu)) { - return ppc_radix64_xlate(cpu, eaddr, access_type, - raddrp, psizep, protp, mmu_idx, guest_visible); - } - /* fall through */ - case POWERPC_MMU_64B: - case POWERPC_MMU_2_03: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_07: - return ppc_hash64_xlate(cpu, eaddr, access_type, - raddrp, psizep, protp, mmu_idx, guest_visible); -#endif - - case POWERPC_MMU_32B: - case POWERPC_MMU_601: - return ppc_hash32_xlate(cpu, eaddr, access_type, - raddrp, psizep, protp, mmu_idx, guest_visible); - - default: - return ppc_jumbo_xlate(cpu, eaddr, access_type, raddrp, - psizep, protp, mmu_idx, guest_visible); - } -} - -hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) -{ - PowerPCCPU *cpu = POWERPC_CPU(cs); - hwaddr raddr; - int s, p; - - /* - * Some MMUs have separate TLBs for code and data. If we only - * try an MMU_DATA_LOAD, we may not be able to read instructions - * mapped by code TLBs, so we also try a MMU_INST_FETCH. - */ - if (ppc_xlate(cpu, addr, MMU_DATA_LOAD, &raddr, &s, &p, - cpu_mmu_index(&cpu->env, false), false) || - ppc_xlate(cpu, addr, MMU_INST_FETCH, &raddr, &s, &p, - cpu_mmu_index(&cpu->env, true), false)) { - return raddr & TARGET_PAGE_MASK; - } - return -1; -} - -#ifdef CONFIG_TCG bool ppc_cpu_tlb_fill(CPUState *cs, vaddr eaddr, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) @@ -2967,4 +1380,3 @@ bool ppc_cpu_tlb_fill(CPUState *cs, vaddr eaddr, int size, raise_exception_err_ra(&cpu->env, cs->exception_index, cpu->env.error_code, retaddr); } -#endif From d6ae8ec6ef2635e521e89fc8708b84245cf00013 Mon Sep 17 00:00:00 2001 From: "Lucas Mateus Castro (alqotel)" Date: Fri, 23 Jul 2021 14:56:26 -0300 Subject: [PATCH 149/493] target/ppc: moved ppc_store_sdr1 to mmu_common.c ppc_store_sdr1 was at first in mmu_helper.c and was moved as part the patches to enable the disable-tcg option, now it's being moved back to a file that will be compiled with that option Signed-off-by: Lucas Mateus Castro (alqotel) Message-Id: <20210723175627.72847-3-lucas.araujo@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/cpu.c | 28 ---------------------------- target/ppc/mmu_common.c | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c index a29299882a..7ad9bd6044 100644 --- a/target/ppc/cpu.c +++ b/target/ppc/cpu.c @@ -67,34 +67,6 @@ uint32_t ppc_get_vscr(CPUPPCState *env) return env->vscr | (sat << VSCR_SAT); } -#ifdef CONFIG_SOFTMMU -void ppc_store_sdr1(CPUPPCState *env, target_ulong value) -{ - PowerPCCPU *cpu = env_archcpu(env); - qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); - assert(!cpu->env.has_hv_mode || !cpu->vhyp); -#if defined(TARGET_PPC64) - if (mmu_is_64bit(env->mmu_model)) { - target_ulong sdr_mask = SDR_64_HTABORG | SDR_64_HTABSIZE; - target_ulong htabsize = value & SDR_64_HTABSIZE; - - if (value & ~sdr_mask) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid bits 0x"TARGET_FMT_lx - " set in SDR1", value & ~sdr_mask); - value &= sdr_mask; - } - if (htabsize > 28) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid HTABSIZE 0x" TARGET_FMT_lx - " stored in SDR1", htabsize); - return; - } - } -#endif /* defined(TARGET_PPC64) */ - /* FIXME: Should check for valid HTABMASK values in 32-bit case */ - env->spr[SPR_SDR1] = value; -} -#endif /* CONFIG_SOFTMMU */ - /* GDBstub can read and write MSR... */ void ppc_store_msr(CPUPPCState *env, target_ulong value) { diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index ec4dce4ddc..a0518f611b 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -58,6 +58,32 @@ # define LOG_BATS(...) do { } while (0) #endif +void ppc_store_sdr1(CPUPPCState *env, target_ulong value) +{ + PowerPCCPU *cpu = env_archcpu(env); + qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); + assert(!cpu->env.has_hv_mode || !cpu->vhyp); +#if defined(TARGET_PPC64) + if (mmu_is_64bit(env->mmu_model)) { + target_ulong sdr_mask = SDR_64_HTABORG | SDR_64_HTABSIZE; + target_ulong htabsize = value & SDR_64_HTABSIZE; + + if (value & ~sdr_mask) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid bits 0x"TARGET_FMT_lx + " set in SDR1", value & ~sdr_mask); + value &= sdr_mask; + } + if (htabsize > 28) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid HTABSIZE 0x" TARGET_FMT_lx + " stored in SDR1", htabsize); + return; + } + } +#endif /* defined(TARGET_PPC64) */ + /* FIXME: Should check for valid HTABMASK values in 32-bit case */ + env->spr[SPR_SDR1] = value; +} + /*****************************************************************************/ /* PowerPC MMU emulation */ From c06ba89293adc95612bc8ece74c09dce6b402cd1 Mon Sep 17 00:00:00 2001 From: "Lucas Mateus Castro (alqotel)" Date: Fri, 23 Jul 2021 14:56:27 -0300 Subject: [PATCH 150/493] target/ppc: moved store_40x_sler to helper_regs.c moved store_40x_sler from mmu_common.c to helper_regs.c as it is a function to store a value in a special purpose register, so moving it to a file focused in special register manipulation is more appropriate. Signed-off-by: Lucas Mateus Castro (alqotel) Message-Id: <20210723175627.72847-4-lucas.araujo@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/helper_regs.c | 12 ++++++++++++ target/ppc/mmu_common.c | 10 ---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 3723872aa6..405450d863 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -258,6 +258,18 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) return excp; } +#ifdef CONFIG_SOFTMMU +void store_40x_sler(CPUPPCState *env, uint32_t val) +{ + /* XXX: TO BE FIXED */ + if (val != 0x00000000) { + cpu_abort(env_cpu(env), + "Little-endian regions are not supported by now\n"); + } + env->spr[SPR_405_SLER] = val; +} +#endif /* CONFIG_SOFTMMU */ + #ifndef CONFIG_USER_ONLY void check_tlb_flush(CPUPPCState *env, bool global) { diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index a0518f611b..754509e556 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -622,16 +622,6 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -void store_40x_sler(CPUPPCState *env, uint32_t val) -{ - /* XXX: TO BE FIXED */ - if (val != 0x00000000) { - cpu_abort(env_cpu(env), - "Little-endian regions are not supported by now\n"); - } - env->spr[SPR_405_SLER] = val; -} - static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, hwaddr *raddr, int *prot, target_ulong address, MMUAccessType access_type, int i) From 1d76437b45ab9982307b95d325d627f7b6f06088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 6 Aug 2021 20:00:40 +0200 Subject: [PATCH 151/493] ppc/pnv: update skiboot to commit 820d43c0a775. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It includes support for the POWER10 processor and the QEMU platform. Built from submodule. Signed-off-by: Cédric Le Goater Message-Id: <20210806180040.156999-1-clg@kaod.org> Signed-off-by: David Gibson --- pc-bios/skiboot.lid | Bin 1667280 -> 2528128 bytes roms/skiboot | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/skiboot.lid b/pc-bios/skiboot.lid index 504b95e8b6611aff3a934ff10f789934680591f9..8a3c278512a428a034ed5b1ddbed017ae8c0a9d0 100644 GIT binary patch literal 2528128 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¥BDM;uIZ@8K0DxvwB4~2=O#?WdMa#dzR2qD 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

r6eqj7dKPKf`uQo!AZCTX>nj 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˖qM4Mp0*;MS0#QEBdh97&0Uf@2< zo>DDqDUW=Km&XKdQB$xts9;U08XQXP7J5L3@+D1G92+{Zu=5sG5*sxZdm4gV&)66B zoh3S@g(u7YlRM8jJ3lB>UeDY z`-@}VU2n#`DDP-_VdKB3LDl8wNAe_?F!ew^hEzqfMuMvuz0rIpv{(-sE!;TnsU z6EUwn7#kG;Y(HRERK&&twimFy=g(mrF8BnLKn5Rd%va92gF)wh$|;7A5OD0H|C7{A0{0`o+ZvFm&ij}5wMbng3$2{&3}2rn1~2^ou+ z2J{Qs`Rf`#DunL^^B3tZujee*`~*HC|8mR)^l!^%r_V=h@(@uAA!F$eN?Z?m4w-Lz9$^areowu}aT6}F zXP|YeD!Uc5z}j7zN1X}N(V2l|o6ii?Z1!L-RMpg?23PvcU}cfH6LlGY^U?IUi*d%9 z!;cQ7k|)9_%xde+`2AVd@7=oZ{I;dBOch#tHs+&U-PGyFHNlSrYqZ#J8ng3o91>ZI zJ)rpRrp?hPs9VkNTpt2GbZnvz;73zXC-e~h&wE+yY!6Mwq!)Z}jAvJGgiJNNuhF{S zU%n&^Yra2;dp~KH;h1eG|0aF|4Rpb=Z`bz3AYpisIb~?17tk*%# z`+ATbX&$uF0HR#!xWehZLL8?PuUv<^C~J(K2ivw&+aEfb!^lS4gSJD;UUsZcd?dUJ zdcscln}wjEic}^)0?r4e6t=guu4lnq6f2vE{iTWF&^K~1HiyV>c+SJv1o^Q{>;agv z=J?uF9&myF{>l&N&k-_rKSLZ)*59$UK0|$A7TXK46g6+qKAC!4+6%O%T&P03X&XWl zU?yH^L|w)ZKKmMJfOku}XCdAb(&3x*NBkC>4CMVb&dcEU&h_@Abx&dbpz$3Lye6=>A9?-ocUz-|$I#CD3zT8(< zvo3lK{e7X0?l(_AfPEz?;)9qB2Q@xlvCXSc1TS4bo?m*;@WR++PX)%@9Oxkh;A`>I zZ1_Z}M$8EtC!?L`s@!OP5ITSw**HKsFt$3PPU-~U`EMw74SX_|eplyOhl#-?`DUNk*WXsilZ>L>-MZ2y}4H zq@HZN3i4~PN`O~A)M2sqn1pWRh+Wk|dgg-m9iYYAT*H73IJd6p+R?GHj*6%^$vL+j z8-o4kc|%=sR~T3#{gb{;IEU^c?CXJTOwU78H)O>gG#XZ@pNW4eY>`I6&!pd5a&)w| z1HcFJd5$|L#q6mFrG9#d<;2R8zlqlEFC@O8H{%SArtPKdCCK+ZD?XM!0~;N1`%=p~ zeO?3b%OgE-r~gD7@vBeIJ)g=0XkNMTd_9KPYf1DIh^7Me++rT;Xzwi&`l7$iwd~GC zkYQC^8>XKb^q!e`AEDj*VyQ`73CzBSZvI79exZzsuMs{9lnJax7 zWdo0}Eq@L5?YUtjLU}WJx60!_YrDMD?gvL%rsiYa>VhE{VUz(*4k;G^5APyAhNFbLiotmJKAB3LhRf_%W6!l zjf^_lH+F6zz*09kj3PYvqmNLWHih1P9qF?|F)nPWHq^I-H^$wnT+{`8mA4zPC!HTw zY1yq~kR-OOf(`N=zNZfw`hjm;eA3ly%W&fRSHT`Mncrlst3bz5puKR~ZF!|k1G}2o zA*|=AOsnwKauD=qe7kC8C|kG`lPV^UYTRD^&Z+nm{q0W3HXAqa<*PRts$>`OMt!9H zv&yW;H(e*JS%4Rc`HlS+`IfU!;rnu(*X#fYUKu|mtOwhE$a)-Y_RvosFffm}tJUJq zdaYZ$enl9WEnXbTHqbPmSI_sFrp-^I&mROH;1#+4Pi1wlP;Jn|w}#BuM8C7j97%sk z_(Qj4%Knc5VH9JPU2>|j?S6(hBEkn2JjAh4XZR}r3p&LQrQK{A*!9=-er-I9 z2XqC`y!w)X&Rh{oly>Pe0#-E*-e>O83>iW!kvvvbSZ|->}-9 zWy443GFxU?Hr%b(;@z4sfE)QIHt_ZY>X z#%x~PT8lD7CIN33>3*#9_A0~m#Ckm^ynP1XCN)^V?c=XT{O$Vs5?deZ&0l}KG>nXP zTU&Jn|8Ip|`%>Y5j{oB)G8WRxV4zK@3sVj^^Y|9rdysek@F!a8%;E4MmHkCiIG?b_ zho?^$zr!lC0sMe@io?xyM#PROF$(545-xGZcvC`H)IY{8P%b;IFLAq_R^$Z6(MzpZ zOEM>AA4DDAL>*_eQ^Gu z_{~JY%W{rs5-K_`29fTjC_*iM+nf@|6|Q zd*L5-UwNU;3r4}bcfvr1?d}gk&xHS0cK=5iNBQpBj9%U!-bt3hGgxh1g6FVO50rge zS$?omr%@;m{zz!YRd$`Ki(wnVJBg23Eb%cPAkOvWsMDx!)+rjh5I$3*#6DVO*C#eI z6ye%*to&#p@kD(@#mqj{pyyK;tQ^O#0DpuhX#iIr2v-O7HuvLa1uH8`!Gq3-T1`NT4+n7*NM<&13l z+wXdni}qqUS}*Qh7UZfgV6IHZCNU5;0@bkPr1){7UKkU$LBA>%`Jb*Y>dE>VuE%)h zKwtWaJ_376Uh;A551owp%G{IBI3TS=PH{hm_;{x}P&}`UeCIdV3~YH0T8y@7C4Doo zZB8iFc3T*kh{r>nE2ro-SRRMGTtxYcJg_08PG~&E#sR;WZ?@`ju=NJ!9rqGwvjy#d zaRl>k_z-TZru@LUwIUC=Mul%ZzUFOl`u9^mfex{Vd9&KafXqnX6|Zz14A-(nu&?>< zZ(V6H9u?(PU=RB7c)^YFoNf9owD_-tc6@70D5)=1c^1!Ld;or7%-a*&{>xAopC3j+ zsP97B2~f@*j1PJLV`xKY?&v?`xV|u6WWnv8Sea45{O5aflgR%Z;N;gW|2UF-3Ous< zlEsGl#v6o(I<$n$GlUm1c$iOT@GzT@<^d$-CH`*~?-^OVCm+dshU?KsSZC58(2Diz z@yf2}6s@-^A9>MNxpE5m@l^Z*pH|wAJQfYemwSQpkT6Oj4Ny)MdCvL{8_H|WvNq+8 zH2>~bhPvnw)4?8`hkc-w5%wHC59uzz_Z|HH(1*-#^%JzYFYH#n=GS1uYn{jMY%B6q z_gu(F*to!3cAEg7*Cqo_qn+-dZ7ZjMPIFLSiN(yZJKhZbjH5rCqEBdj!Mr?E$JeR= z^9sVGEs6b4z9n8;0^g@R;5C=uP!9gfCH5cU30Y%Ud_(?^uq|x)f;|CuS$(4rv`g!h zc>i|x@2vJrkGU2M`#f>6`@=5k#2ORi9@hd?oOt$NUhl`>(qXC^zY&8ccV*)!^cSqF z#JuOik9J^bbWjoPC8KF0nm%G+=|p9AbPhWy=15^E^@FnkTG1AOy3&TcFkANF6R$Lm zhHEK(Bxi|FJL1$D1-A)5M{D!fvTsl(gpmPrqRG5lv=BX5uMNDmn*GC2BR1%C$0fg& z&gMb%!`F5r%_B@h-2?Nh1pN7gC#|+TK;G^$SB5S7xb{mg6CC=ig|-(#_Iw4HU-&9t z$KKt;0_Ta1Du?sen${7;fmZk?ZroppeRNn@Nb??e_;sv-k8lwW(56Y|w5}!7MqdCw zD~s24yZN)=J`lwjN} z;y5Zkb67Jm3TB_+cZ)U$V`T?v=Nk|F_EF~~ztX2()}vti0j`7#+76#7`2u2J)3R5_ zo7b1)+XQ}-yfWu}{e9$tU4FRNe$U^4g;?do1@BCs+3v$TNAGmoe_*I#pY?h7Fy8Iz z^A2;bVSVO5+YjR1n%?Q&xBz`>VI$g+eGGDGbhIoPB#p?+w6|LFoqd+R&9H-t9wWS? z+v1IDG#!RbCv5OyTjMR@BkYmX>!gT(YNqDk6R%ACX^x5TUyeSRy=UlzeZEt8Oc`U( z^|-C^<8=I1j5$fE=C?Oyhf;@DhfxsQ)hL*C1D|bMD|iZe&3}&uzLYhYIk+_sqpoPU z9?`acJ;=qQ3OeJg2qkr1p~N?GO&Qj?THkmYxSmfqdQ3hx3-frydWVkKhIg>DE~$f!dXDfi zc<#bZruCN|giRmqQExtgvZ}1IjszZetEFXbl;xJPe0A&V4D`Jvb-E18$CC9ri{(b_L`!PU&iGQf8&!Yi(a;!k{_HjVT%mhd9_E@4At&RetbbX! z`~t960nSvcSL|fGE(?yxB@M@HBR!6-UKvK(wS4aMt_)MhgMJsb`rl}aW^*$%Ge?tV zBCisE>{<#R0`&9t6M2+&T`O;_e|QJq@9w5v z@9+zHgDt1z6JR6Vv1!@qsDGeOI&2-pP^}{O(8g)~FA!eDo#SZ#2|-e!>Ob&~Fz$%{Grs z{yxUg_WQy2==bLh;dk&~2X4G%-Z47QG;O=XX!tyFEs-+7raj7XTfXlFPWXp) z+;bn+*q0%`D$2ZmnR{&Z7!_z!XGU-DD5=}Szk}VhRY6}@b*~vd#^{ai$2y_vE|&Gl zmsCM4*mG&{CB$>#w?V-m`sv$c=z{_OqX$Yq!de$_9($zyxAlJThxP%vsd6sbZCpo5 zHr%@V{sFk4JMdG1W$h<4Jc)b2J*jPm>pyp6D7v-g_wM4#q8nA*M0uTSOQw7CYq3Xx zy1~fr6v^0M9Dfe=hres>zUM7JgCRGNru6Ti4+q+DS}66>Jif#8`m!d#-3hp@Hr&mG z3%z+KW$(!EKp(_i!-Z}o@iRpB4iWu_`WNX4SW^Jk=9TpT&&v@X3|!GKW3p!YV5?p; zP09K_bqw+w_e}W`J3`!3ShL$RiF{W(v`zy)kvCBh@OX~iQMy7SdAFaZfgwO%T!_Q<^xw@db z{GNTdJ8K&&;47DVoP0!n3dC`S(GS@-Us=xbYR47@qY=!-7OUCySE*vy#|DBXGvE(> zRQoA`A10bd{oWlfm*QRR*YAmpZNvCFOhu5!T<|66AEbV^ref^(c6>T^%#O$LEq>jR zB46#1{I&IGlSb{ZojJNucSq?*^pQF-WbBcZRa%Dc_>%rTq14)H;-l-6)5ba?zBlTY zGv83VMTgeoM$T-eN10eZTDO{c*w;XtR~mh)(~|pc?q!(;y|D*()J@{Z1<+>Tr_sb= z?2$(g)PIM(-3Hup))N=)!}KQna(+9NaJgn2Trlu*v=wxkoIHuIF#CO3UxEHa8NJI; zIS22)Z~6l6x0+s_pITa>lCT;0{N?a{C^9rJlWp-^!MGsHZt@U*>W6npChd zF*we1Jt7J0rBTm~VH&)v_pMM#zSC!Z^lXqgv#GUr@;l^gvU>7|dx2xY-4C}1?|!%> z=Pv9q)xYOVy!+uLY!mEdpkLpKC-Lm2{Og1@BeTY>!;9pIBT;5zA{h{kElJNy=n zt{DZtFUQz-AzN+~oGa>PmaqKDWURGMdyDJ;m*ZL2dA#sc4ta{S#JZ*6tH-DlchpSV z`V#AYcjJe9vuKoO)5xZeuIukUWBK6S9O$TnNJD(r^}n08niHHa;2eUU&w`6jczji= z6W<>ZoUuO)v@NI^1YhT&jNROe=nch6xP+$L&WsW|Nvhp~5Rm)dS_vU;rHcHLs_(Q)`@;use zwAJUFq)#7Mm}9?r3t+7#tkU87jE%8lR#xHtvf2^%LpHi$k7>D!-)oi~yr&F(C~)J_ zNx*%6yqfarXFK?fvP%DsOy+OONAinazkTy*#0;5NKUQ>DoXwUKhYkweAa`gd&K-;T z#W!at( z*z(qc)#JL)z;{3EO<8wrmTCdr9H8GF8GosqW#c_+5M>+W>hMLQRxTOu$&tCsEUz0e zuvg-{{rfD1-%b3DZ*j{exP-bN^Fto3!uKabh|vIh)hhB4%1Q)0kVp857Z?(y02f> za>lgA{1<(3%-a)23CERa;^WBdgvtpg@@SVg*pJZvU+9g;Ug9z4AIIZN>Hq!UF&n&+ zWAWBmy?Lvo4{wdh;;oW6WZaXS4+#C5_qF&EZ^^z9L!CLEb++gVS@za*c<1H2N9$D4 zLv@IQj6Hyyr)k^AVHbHni{`x1v|60VI+5q0|ryqZD*Z&JxcUrLS1gya)%at#@ zgasW5ZK+Vd$EAKXn->aR#3_=f%JEh11e`nm6F6PeKb+6^YTXENCQu)we;{N&B6bJr zsnk&^(?hAJ2Z0>u(+-}6lC?)`5cVVgtMEAbS2y}ao9%;u{LFekAM?gG%p2E7JA>%c$35x(spkrq>$xr!TpWmVy;p5be^2)j zmy6#4-wpUXE%*;Y9_^HQR{YrZKtvS@KP7(mAM!o5`W1ocLwrZ{X`F$5TK_Yh=P4Oa zLDvG5(?Xj7`A_SUhZ-U0MpIs8;V?*-`S$$+7F+xd^g02^^{s!vL{39hdw4;#V1r`MLl8 zXd&@0U4VQaAzvHqgo3+6_ALMw=N_!b*oXhTckGdlhP&T|%=^ikUyfbZK4t9p{`_Jn zwL#=A=PQ2ipBGZE;rv7M=YWx*Rf~)-@GG_Zy+wDc-yb}idJFX67RGU=?gG8O+aFXe z@KgukN(>grUcYzWR_x8rYn%){WdF1)W9320+Cumu4~=g_yvy=$M;753Hf$%>Vs@kK zWVQFl>|<`snTO8udekD^rL8cIjcdwLYy(66=3dgeH(q~3Je*Xb>m2B;;8F5d$o$wy;`&RosVqq^#{)}~aUh7LEIM+=@S&!220L;_gxw|wzz){+SzqSDm(u&{4 zc5obhXFb$Ib?Z;Y<=^`7xPkcAz;Cy%8<*?%wokeB-Emx>;=B*Ee&;GYbL?yv{7{dO z`Poe323{&ezg}230QN%}8waf+etw^{eQwMru@|G*xr)B<=u`A7+G@+1 zvG2b76?ibIWlY!ETAw@Cl#J*;6Ec6>O#Czr%-wt+GS_e5yJ+stAmge)589kuVp4Wm zW6J)e7R|D3$A8ATV*@{9KFEfkm0rdO#Qeq_ZNC+F95F65ERnB zi*MK~xnE+G;oB0>w%uymB>X9NLhtoKm&0EP+V$vmi!Wi<{Id_Z$UGbAlE~MQ^(~BV zR})W;E53+XWaPXqZOHYX#isID@-HgLu?7|8Kwjj_=u*;_qX>*2S+!*zZxsKtts% zV!kZhY>NLoj-r4^jp10sJhiW-#~V*_HFSH(2IkfD%A3mkq*uVtaf0S7OWx}yE?J&O`$)Q2eQYno#X|KCrzj;_QE-u>eKrfstZvZuUtD`4t=D2 zO|b89FIGHlE4TL3t5tpvcygAUZ;rLg$=)zB<6X{Dj6cz;@2v!VdA33vePRvk(NiY% z>3d!R&QG~oWIS!hsJ}8?k+%VgSK4QYcx2%)mE~G{Rs;{Dz*rqdhEtS32Icot?j#vc zQEm&%O=kJ-0u(Pt9RENo-qM#p2pieIaQzTtX89*AJ%-3WiKoc+ zioLX{Rq2Mq18w_;>!&X4**(j$)vqTXN8u+1JFP2FGu0R9z+SL`DwyuwOMe_SA+8o; z%@TXnZOX>EvrZq&mHn&pMVw^wrSf*f%D>8SV5{V4d}%Fv|6V|7Gq5 zpIM2^fj*^U&|G&mzoVZ-7z4VY2Isq&di^(2neU0#7F9$lvF{&ymqMxUEM;2Ez50nk zF>!#v8Rj^s*N5-A14((MW8HOwzp)OrsGD*Rf8CqZ`tH4|tp{T|WKMg}1ho_N;@LK* zU4Kl&saLBAi+-Viw+ek|z$sup`1fG4-@wOq6~_%5KKMuWSO6~c!$W6*AI67Kp}w%- zYvl(0PkZJ$@@^+Fa7mw*byUbVjXQhyTuSpr;a8cl0{sdQSid+gF&i7(;rei`x1KgJn$S1C8r6{iMP$$5F6?bP5s1VtxGK^K zSe-xAer!qPyEdLTEXnk;r`22qK6Te^#Tp#O#k%jl6_Gfgut9;Io%97lAH}$~rU~Oh zG5T~A>()W>qk@l$Q7}p9jCPw1a`Vz>daaUkyv*n?M#%cD@Ln6*qj^me+KRH;9>pnDbDow}P%U(i5e5~b$|&0lOq8$1X*njRa%;&aifKUn;BTDt)G?IfS){~JCX z7CsZg$Zz=s^@mT<>Gmd^3Lm>4*u0Or1@G@99gp9c(LoPAOCJ#E$*siUC!Mk{ztG69 z6dD=%Kk!@csV|O$d^90b-B@N;_8JY^2fEfk`q}~ZiFxclz57lo$#;gUYA3Ssl0Ni* zXupG?*AC!%Rv17|Loe~kK9%3;Y3XJAUpxk%Gw$`oo&$@1^Omes=Qe&<4T%nxGhsX; zUv7hZOdRT=9D^<^d=2`T@NFxq*@`%d1FwoqFF*Jq{XnD63d)B#^Th^S73py>-p}X5 zth^%s9{muu;7uyhU_wr9R6{cD^f}JO(!Q41k5f!NHZ6DbbFjD1%V8&E&LZFjne+M2 zL#fv`kY>!sGBD;h0ggxO!|Lzu`=nnF{C!%iGqvfXFZ~ZbRxM~-_-p)oH^B2s`_S?b^n80{<;-ZI@3ts5j&b ze33d=GENJ}hZ3tEKJb*pC~7QMu6dw2;Q!saQPy8pO5Q!TM*Q4T|0DAP*qNNHFJRDb zcwt7LzVqOO#gNO8hx9?}M4WAjy?Ap*PNGc_&!&4!7z6>ottVk{<;L?g?x3Ztqu72c zR=;xe?pMD<{D*;K*spAv1|B#IxTIwSaL)Lqqkw+;ywjpt+D8iX>$5g{n7+8!t0=s( zB9z+tAc|z%K{v<1c(xhNBLRm;>cVTxRh8g{^8f-ra?Fa_$HD@}Z5Y5btJju7ud?Cg%EX z;a9jn;ax6%7wA|J=6J%wZ>RP9Qo_OSJnQ%Q?lZNYuJo8{yph?XZ zJsA1|<3`_e6=JkL*Y)2uPl%7r0P@Drn3{dP8N4i7*$!Qn?^B;K9%>wYqRxCPa=jV9 zzz6--`o{AU_^sgj*5GAGyQ!`fHVU&V^3XA?LD$OOm1nFpdDpkLVcjORd|GAv0;a=v z^raN_&8BIUEVB!=&fn4(d>|yBV@GD{eoTy;KuXIRUh!vw$ z9^;S2%F7F*l@m$VeHI<$rrT>S&$~TK;wW=I03RW1Ub=+x4|$eHMj&T-$KkWA^}u_aVrA;fBuH{(La2PRsOZ`;~_ly z&^b}w_i;Dy`#PhbVl}d<72spqM*(+%4HMs?V^%cU-&sDsIZ^(NEEC`FZrY>HK$_c_ z26RJ=e5~Qb(XZ;%j74QCY82?RN>N_CYyo^5mNk3m7mIH*l2PO-Rc<`b!MzaoJ8&oc zs&F5Qx-JWB#F}}Tnm^uiHEBBq&r4RI{f^bCrZr{iv5>he#E3+cq5r_M3R>Asc&c0bGnrfhOPzJ|T|;>eOMJjl33rFW_%8{t$<8;KACqnm%c4heI()aSOwAd3ZGq$INI=6BCc!3tATZ`9~Cw| zN55yE$o>^$A?I#H>@@ZP^!=r{qpvRM28|a2He|?VJRe>Ndnw-E1bK2$)^%Jz!00TO zBR)vRzu}f%u@=5|BN;yq_JRW1WzdhBC?n|006F8f$LB>Ouz!-{rdM>m3dSRa9_oZ0 z$;s!Q!CD9Fqq~*#(cL=0kBGf>c&@MV@LkyRcOne5z>AbQo0Cs~m*aYWkWp}jjMb<^ zS|{Mx!?fH##Al^1nJ!a5%8fKG;z{a5*v50-&o|)3p(J(a+1z*5Ouu5s$(_kI3$eHqP3lGiY!eW zzCV=u#Y}mJ{jz8;(f6(Pn1sDP%73A9gV&wd$L6WoraTWlu15NUpz(#M55BRUD3|gs zdru1XUH}&QJnB=77^W&VaO$Y&r{61#mG3Xa?;_aAoZX*-7N*$JU@em?JwEoCV`@7_ z;xP8laSrg;y8!+qI|k_P^n(wINBO|(TG#a@z8r)0+)jStSQQ&wUI@M@VB8MI?1Lz`b5b z6Xn=Ic>D%PxYvsK+e!Q|tq~Px>(Rc*3b4 zHK#%N730_X-u0p_hA!%tH0KR z-j+|qmvOB9Iat54m-Kj!eGS+<`sE{{Go>13JQ;T@h6ssk@N2}JJHC$T_4oVU;4^TE z8?IRg@W@N%LgW|f`D_DcSaTf|Mj?IsK4k;= zq5z+GNx#?nOqj97tEt^t_rcp`(8IhR zaU7*Qjsp!|Y1?M+cp9mB-76gJ-J2XcyZ1UedK``>ZkXWCcZjH z*Khv^VE~2Qiaz>TzAfseP-=8Dzxg&f+PSw2V=C7~jv56)(XT`vQO8JZns32cB(g9EMY=tFQgMo~>WgkHEO0RcV^|5#m^|9)~a#;`7Pw)T^tS9S7+_LLu z6qIcU1L(=Pb$-eB><^j`j!Bz?9zjVfyz^B6>v@7V&p)xxKIw-xn15d)d{5krf>C=3 zoAzuwZV>D}t63KA=f~@0Uhsmh_sy9;fO!}BkA2722AR^Ng1)#f=&Ss7dfbHH>~g|p zUm{2eyG0(~>^k&pXvF^=i;ukg(CuK+{!@Eu@gje|(7@iAO8J7qjm zc~2)lhHZ2If=GXHaHAK;gOmwn&?5tTevN(IU)@I;0z7Z(J#HT{P`3%b#8j16aWbmG@_j5MB;lDb#qU*@~H8TAJr&4hf)vUBI^ z`fx?NMdH2(K~M*M*b z<>2Kf)-CPe-`Wsgk!;up`K!s!uU~Oz4Ke;#WLmHvu<*^*Q zKFo_UpUaeY&yFxMo?Om}9acGK^SiFsO_$@5SGpX-^>1?iAMI8whjaHptEQbE=e~Lk z@x$*7BU3+R+&Q`w3mZAgg*_tlrFHq8a~Ar`=QM$L=0iuZ>=D0aoDx?L=3kw~bC%%l zJl2WvDmiBf#tc`dcTSU~PmZ?6jgiT@fH$1uNhW3^_LWAByo2?`MUT%3Kc6-K{97;^ z7Rn#ma|>`ThOU$@PxPII$xi66r4l2hfNNjiyLO~e&|jUvnYcN_6Q#a_C%)fgmjL4d%ATcc3-nRy9;kb9*9YM9(j@hquoYiz2KKH>8eNt$x^9N; zYOLr&N#e*gMYNL|kJz%ayc2`b%Ftq#mEC61%J&MVk67 z7VRweuvPAXPRu=JuWG&O=)zfhmT%|V!q0G_W*v`SfV2Hl3#9##z9{*awpYr&bo7x^ z*~XdIk_#uWAMg?4bPQ$GJuq=4+Gf6y_u377hvykqd5xL!>ZK07%De4-iA(zrly?B- zT_oj!j^GW6H55wK-^F*}6X+!~s_@7wy+2&jv$@3LuSMY3q}4~JomQXv!FlkDV_WDr zu#nq`gL32ywpYm1vKnRfnp2_f-Y^Qc#;77A?{Dk)+t;SH@*UQ>T6}H!?VydlE}hoB zY zN>x9|@4&BbjA`OGfZsF^T(=O9ywdow9c`V8Fyq^PeTUVa_IXhCzAdnSzO0HC;v3>x zdy28hMclth-5=$#&vX!geNiW}X-mGa=vfavhHDz9U4!(+%|jv& zK|knTD=b+?+n)n^w2$>*|1@0F!&Z4|dKs>1)O)5nXaPvz;~DV8_8btS2j;y8`)D(pah-(DNPe8BU<@egeBT{D&hF<9^&XJ*ZZe31Dz z>h_uRfz(fz6929h>ZeG10Ql1vZ=40|Pl$0_t?QR+XS<@Fm*bwpI-xF3+>23f{>Gjo zll!D#e=zr`Z z>Io@;e!B!PF(&0E>z0KUU${&SAl`u{()`5LfiTiRK7m(d%?7wjEHS1z6|KOtY)faL z4Y0tUbu~E8DYe@VP%0Ye2-dZJUI_b0A^Q;FWBh~DJ?bZX z%{yPbur)W?c?0Top_*}Qla9HKJnB?njsp`*$%d&jmyqGVWQ2 z?!Qy#<2A2z9CgDrbqLyS0^5x1D$)Okx_1GOvbxrQzexfaAc3H<#tJ$pXeJF96m=$( z%W%_425qpRAl2hwf)cB3IAS|FnUIN&m00zF#THu7pazqk+Ji0jpamTiDJUxTs0S@- zP}Bsxp!JdqGvEKdYwvGn62SKS&w2jm`R92cGvBu_Yp>g0d+oK?{uTCTw!k09_>F$V zZ=608zBqpn@Nr-NN{#bE@|DW>(0d8S0bcpr?f@G0L?sR)s{2ck!QOxI$8Qn6e5?%i z-h_Hzq74~8V2;wpWy~sQk@pMm<}a=>CPL1N;Fl|epKbword9EF@2Z6Eir;)6VNkY> z8A6-=Xe&A95#S5zN!XAaFLbV2x70j>vxvQIT4yqc0p^&WzR$Lp2MqKRJF4Ve?-iGgX{k;R<5#JgVxq_~cHE45Bf9@~h@CjzQ>r~&O zJ0mhW@>kkmTP>4KlJRN(=9B6AJtOI?LNk~GG6l&|bE*#jj# z#8+xQ5+zK80ettO=)tHN$Np89KfX-t2xSBEEWXgD8@wFHL$qECb%o zhYv>j5j+1O__ON}hw?Dam)jqTv5dlNUsh87mOW5pQ`7Gi@=#z>Utff-$GKcLey!1^` zc=@04Y!~%RP5Y~*AFN9`2cQh*$KMOz39f{$b7m!Q1sD~sZkfb-umSSJ_a|{hJcS~M za)S}#>VfqvqjB{E>K)bR&7$jASK}&(V*I4w$}jmq%CFUY)0oOPeK&HSi8fc$w`VGx zZMWIs|I10Wq5biH6`E@t?f^g02N3PvZp-Q3V6#JKA;x7%ls*BBJ&L(@3h@#u=;H5> zTEP?8>*e?Tc;^Te6p_~sh6*GX;QprJiF~1Z^T!)DjvaD&7rxVeTFp7sXJ0S4Bn;#g z(hu#eB>edPnLn=16wuapVO=mV-3Vg740CN@+5P_bo5KP?&T(w|fIoifRCzbTm=enG z;@W8rbsYGwQ1#!ol<)C79D448pnCR@$UC0h2pHn(EWyq5bh&lJ0p5KC`bU*qS9;L| zKeAq=T^s85;ND8HZL_rBdYZy#tv%R@{IQi7ud->!h5my1bR*_f_=F`EF+h0Y$e(MP zmnZktq=V2SD7Opc=|7I5jatY?fVl!3rno7RXbm#nI`0O_*y%grW zz~6H}!yn?GHj}ao#s2NXS+iHO`3^exYuJAd;CN^Z{2G7!lowg1)M3UvZ8-byyzL-; zx7hQ|9N2)I(;E!>njFv@hzo^H4!aV6SKu#gKiVri7ue>rS1e{bV#}sW9LYrRNs-8h zf@g!@{O=}~;haEtL|578ZsRl1q!zI&4o#cAzHHxSJmx3iBVt=kh!cZ8c^B^|=-ezV zWssw?8OZ-}^CP|GX+}14K@?vhbHG;~FJcGsmP9oUdx=AT{Kp3ZXqYnNb}re=ajAIm zzAx~7lltC&KE7{J-(!Ek_oy!O0>1A$yaqNc>V-<^L&{v)z12KaILthPG4q_=vQ*) zuY1)51OAyGiT*o$K73%9%)yhA50vL*T#^2nz)J@1w-Vju0dPLU_~_>i&_y1WHIJbE z;chSYUeY&uwiu4_BXOsE1@20*cTWk~JJ?R2;hLhh34{{VhhhRPd z4?(6o7@vvrYW2WFRK}y~cCtLiF|qI>)-l74d+{t+?6w}$m>2Q z2mF+|dF6JayAS76z0k|V3Ht{e+x_NCLVqIN;l znKK6T{D^m(xuWE&(?wRGYd#?^m#?nOh0pqQ(1 zHh1`pGv-dhzu}jZ&7E{f**xE@Df23JmQ^3xS=Qv78LaM}HKnN%vCGbkvU!ymWx<`9 zJDZ$yUk~m4POt&KsV3+7a$gbWQ0NBclX*A=?tfSKd{O96U7+M6me*9^eqGg)LE&MK=qT0x(cIqu!aC$t)aPH= zTFW2$k09RJeD=>;rH_>7KKqwaSMk%pzQgE8&L6{`C2YQ|+>cpj0Clj|-$H&PuO;|w zJM@Pod&X(QSXRS14)C{$t`uGO!ZyAmzA(lQ@NNzH)Ea;9|A-y;NsoRET!E8Uc;L73$6v^1oiOH`Fyld{SSt8?yof}qmZaG|MLc4=9GTK#XpfmcXe**jLR=*!veJX#+b;9eg zmEcS0aG(zZ^NwC8T^|deG4|07IIVk667Ay)+qdfKc2{vN0spm2ul0()X%7Z%587W5 z+~AxU=zi31?iT$I`UAh_zM=gpJjN&cvdbo&*gvrNwE!w=7%d;=@%#hzgZ%^7+gK0q zY5zc327dCBuvdNArR}mNei!RJu8A%GfUJq*a;Fm4|7uNK+a5q=y`HPJ;GwT;(3%+K zhxiKWQ`W@d_fq;QE_q=6=B^QJH|Z<5u7bY-xAqm3CfC0SU%_>gSO&KJlJEn@GRSZj zeFdaja{aqfbe+a=B@CfX@ox-n4Tr4hS+euuMxF|!t9B%>_i%(-2!9{Zt7tMqGCigzaG8z~A zlenl6JM5cr;kgoLQMa=W?6(!L3)f)%)uGobVsHEGzjU*GyWaK`z`l_CR(HOTZUE%y`6f`{U|dD)kBHPF)`MkXh*o(@uTv71lvt zTL$aT;Vb>|%(Z++{Rw|=9_InBk9**6D92yV!X?qQSQo;l=Ieccc7U%JIXdHbHWu?N zhX z6*!>H4O?H0Ckp;+!@r36j4G$(6S3}nt|)R(xxfDb+hJU`Zs$U_Q+};}-}wD6BDSO6@`^_s`qVHRcy&%#bH<&KtdM#3BEkHuKNWc#=?+#xYJQNg>=u>4x)FTL(9zCXOdX!A5a66N_+(5Oi3 z8^>WSbFjSwg4;S z4D?O)6fB9tzhW?6X5i40s6QSMo=4oM#I(}a#I*~4zl!!Z75Bkb*aDm)hmVf^qtE#<_HQcC2p@1+#{dNFVUvp6nf}h$rolWmid4=g2DI0kbA-f zUJ{;X?i^L7W*%Xn-wd)fB?Z5J;1_pLZJ*8eqy>3;pr^xO(Ob!UyOOsE&i5`pUF4YZ z?l)^?j+XXB77W-?R|-F+^l>lxK#X+s0Q=x`{swKg;#oo$0O!at z1EAk_Ig29kTR!_w?`AoRe!30laU%x$Eo+^QI1kt;0|VH99SM8aV@$*E)!=Qcea@uK zI09>=d$|q*U%FNmJaR0fX|`^am+ZscX=2wr79=UKJdT^;z2Lqp19oI zqvQN~iF3@Ol<_A?SAYBmO4nY?*pIq;!r^w-)#E%~7h?F~YxlA5tFYdUQx{;~+^fng z*{k2>b1l%lyhHBR0S@xuFGv1|>qSS&oLAQ2Qhi<308mrx&%Tf><5nL zZ`ht{{>z@a6+ih&>=Wtz?8IGPpkE1WNh=mMAKwMHcs>r_?RuS%gYVY4TIAwWzIOD3 znB1wMR2Z8-7(nil$Gw1YmV)u7gkHl~5huj>8I$Lr5Nl^f1vs&H%(5u#| zs}REk-}d4Q11O9>2d&)__3@>;?waM8515x6`RkMC8+^9xtB+R~Yv(@7=Rem**7kq4 z4fah~;By#mjRE*9#P3YNHqDnde+1iNUMr4=d(o+DUEaUq%Xn-R%K%6Fsn6v6MupJ@ z{Pi*427JQ1WQ~jOJ;%wHfqD<4IzNe6KwtKGbs}dtSDVx?X}o9F&U3c@c(vloCh;5Z z;(CSlZ8K~&v^QDX^^(7t^{FdB=l@()LSDQ=_?5h;d;{ls3(L*V=zkaf4ItN2ByBtN z)7Sp^_Z|xXFxbZMPn&yxvD|PBJcYAKTDAu6r~d395B(M8KT|UHebIl^Y1D6&E9k#4 z;thYaiGRo9FpDoYk5C1ltG9%cMW-+ua?mHSD1+_9JbE#9#Qy|Kh^{$Mi8bT@Y_k#|vLm%Ae0 z{{ZV6;fa)Yw6m~&`95NBeh_0l)G_f4_Q9V5;H3rr_}XD9&vAd(pS*a!&L3YRV@WAr zh360A`D-cV*-jCjKZNJo_#9)0o|&ABulyupHLMs$<>Tt8N4t8?Oh1P8B>qO`?`W!hg0WORO<4P~~n}2#SfC`jjw9%&GPzuMnhnX?lFlBQ)UyY7e)VAaGu)H@(<0Vg@ql4# z#hEzxP!U7%rmJY<@#>so9Tt5tuSi}k zvE}1yg%;FTe)I8H2#ef(5dU5SzhnRHs9v*8Bn_1hEPWSYKzZSDz(Kl%dpz)mwIMlr z1%l0RUfqqDRM=hK>0z_>c<>?K6Z`$&2-{Q(9#5O-*Z27wZJ*{^jgd@gQ0xAM8z!Grze55Kv2wrt4?GDy|Nol3qjixprn;Oua)L zLYvEPE_h7dQ%0ib7thg#+#l~Z=db5Gi*}6D=-v&y?KGA-b{gd(?>jR$t2#e8PtkFH zU9wK{j0)tcgz%sa@_yLY`pxf(Z0I$PBmAu4WqYcvdspeU#ZX#Yche6`3& z%5&OVH{y9V%PZZG&#(L>bVHNe|8njD#u#ue?`GvbIimk z`4}thU&K~ozjA?&wXRaR^9ZSKJQY~7oTd`iIG4*(1J^AC#y=$!il@V5<(d%nvb z`@+fh`5CeY>tOtdj6Z@r_o}{(5xgBu-1iyD_;Wt?#vo4-iMeZ&TnhELOV92b^+k;B z5q4>31MV)5WPFUaGS^Npmif)cM91U2e>KO!y?9xBMup~bpAX_>GvLFqhYlfzwDRoW zd3OR2_d`ckoR!;r=RU7@z4mWKhJ8!H_?XB)#>hSJap%F}@!oHpaUNERXZyd?it(*a zJi89hcDXCZnDZ))bDHnOo*d5ihn~M6*nG!cZ^I>6+uV=bfcIj3a}{7LsLZ8p12`pr z#mf5uYdidBrjdnv2OQY9pTqm0X>W2rS;dkj?mUIQANEVXx#_KdA-2d>)Fpr$w2jxV ze2dTGOTa6el6yqw*in?9^zd-d{^5dL@cLf%6>NRw*5HcaZSDZ%CL^&biiXN7Ld+`@L+pI%1s9en>aqG34r|tfKu)!yD?(+#HN> zZD_70&#_$&#QM0RQU@HK{NNe)SjHuvz=prGyO%ImM}|Mug!3h#+W;5lrqFG33x>9- z%9?SOZE!krR9W0(b;7c$-B3+c0o!hJ=DALkkB2rq*6?Ai%T@u-?bH>ZwaRC7?3xp? z1a_X6ae?kzT{XjY`{T{q0vH@Yz}5nOA>Nzod_|!V zy^J5{`3jszI{RSg0o?To{Y;#DaQ_tGn~Qut4&*%Wf(8d0O?!UhIy=``TRl<1eNume zH@Jpta^@F>T7AKW0{YXDSI~*Pg3z&vF&2m(I!<0edlANowY7}%g9MJ^%Vj42jPd%#;{)Qndgw7C?~$`v$`7x zPOWWjdNUXKh^_mYU*%n%ji&6m?`_-CzR?I9lpsmMO)!aI# zr~&Ieo-=|D%_4pI`-cY=PbhvExTSG7cr)@|(Bg-|yfDym9A21n99W_jFMvkRcWd@%(@vm1pl-xIUxqLHj8~fMvUkLWP^ zNalu(m!K*?2^(|5*~S@`T~7T_2OL@Ucq-52ryFMg_d{hk=xVdUh8LcJog0h=&^YNy z{ebbveX7)xuE_94;J2FC{@#z>@zle){(<5WRTHKdCz6wq8lYmv( zo5Sx)&L3V|00mIzAIz8IHc(F;J~+I}H+Rn|| z;$32|*Zz@yC-NcncEZOjZH_SXJ6Q)|?9hYbGU%%j{IrkgK)<&`cVRziqpa(=w#6Cm zdaj4?&wg8-VcgbgWZc$?@4Io%1@}+_JbuLAMH`hmb`I9T`NIvy+E2va4E)W-U+T0( zZ28LT)gHwr{dbN2E3N7Ol+AMbc+f!3f^nU%*Dfo-C-4P6`#8Fzk1No2Kl&geNEF{Tw{CifbBJg42RXGoHc=L{F7}8P0w48K>xEb z@7!ho$p|;g?QH$ z`)j@vdAiqkRk^le&O?7z|9#VqAYoAbe;_<>_1}YcLtr>%2n<)UAMLBiX>OvPG!dW1 zdsR5>+$a+Sf3_JOJNr6gFKBU|cPb5jNN) zmYzla$_DYB!!FdiV=3cFh$FXGY}N&hhj5m-K%ciz@XnI`89UAqAF`=+F;-il0uAVY?BIa_fK<51OU|*_&sbJBhIksh z7h(L}DnCfavG2>1Yo>&4Ec-S8-;MVsj^&%j``%~C@tzt+ael-ndxG&+1W*9@`KQL~ z%l_6Av`3gX3%FBjE%iNhu0kwERunqr$B?%puA2F;;q1~}_R}+Q_yXD*JUhqv4ziqc zxW|^KY)xCRwZs^fKSFe0E&dLNud%-SDai3r$nk#dyMWKa6&blTDZ?91W82hisdH$G zy>(Q>bFYGDQyX}PL`)je3q*Z_Za!iXITzEzh~jd37&QHFUA^1_6gXJ`x@kg z!`W$-Kg8U_T##xG@~tO4q92Jj_;pwhW7!2<8_C>fl`)p%{KaRSJM|p)KA(q9hh2yJ z6+&m^@h|c+S$jgKIC@Hk2dKlkTZac=la5t7-t=MbIREff!vl;_Do=yn!JeNd3Z3N! z?0F-n5f1FFA}{hj^2IFrTJU#I_mjhywDlt&=R)Ho|z(BthZwjuLcDEZNlXQGQ zcm)0KlkqVA3G;{Fyrh$G>v2!B{5XgkF~A#!+;1;Fu?NPoTyuONn1UP0`5>`W(pH6% z?>3Y}`$^nX>}K5-z&L-K$}1ZhFMCE?*xo~15hoM}-NL5HIIJ4X2Uz#ydm86o+_(;N z&$dtAB@VDY%$IqOv02ie?`pOiDrIi)_!xFf`A#IV=FyMS-(eh9L35VEvG@jQ9j6&W=e^HFA!4(kOM zvbF&)VEr}%c#@bw;Cp17iTc33@V?06C{Ie=yV*bWt@2Nwg!q_?1n0-pjf}cM)|%L> zV10o>=H&GiY=iAH&I9nR@y9QeITgI#fmi$_@-F(aPr49uApNzApfAR;P4Lh*+Qr?= zhgT^cHdZHPeAIucb<8Z`x0JOEbi(i|@SE5-mW<2#2r{H(yhO&=-1BX&W0y5JU%53^ z=Y-1hywGWdDY^$fgeN@1ahYyoFXr41VTHot5IuNlJs&%|M5A%FIY0C8z zaHsZu5Z9pMLb75QBmC>X8ODC#N9(8kbs z$x|IO8~B^!tFDc{#JapYmh`QS7-Iqvo17&EfAUVsW5r%G&4damh zlj9h(0>$}Btm6jLwp8eW@`L7kRV7JVU-sztDNi!4V`v%|9;5CQn#STHn+Y1nOyT3^ z9Y=Zv4=H!j&fl%*%XUd$>WGxPyQ;PCZYk_Xo{jSg9}_2tb8tnn!wM&BodE!R0-SjL zu{S^Fd&DNz&wnou@!g1B!}-*PO5E+}n{`o>-Gw`n%w0`0$6XYvak;FRCFqvTlo7=b zqw~=OKMC8`mz}Nrz@+stI62xEK;a|~Vqy!V;HaJN=RX{Mi8xXB2`gU&?i5ZVzpJqy zy+!LQ89T?zxF^Ni**pE_n#pVj_Gtfc?A=-Z*c+emJ;%(k%a|+1T?F4ix*D&qDpZGf zmPL$fVNUzXV5r7e7OZ*YR*au{j8bb;+!at-gVa ztte9qn*wK{ep$gW!?!)bYLj}8%WZc zC2`#U9DgTqJRrQBg5zOH9LM*h;uv?5wpH(lzLbhL@;31V{NSEaBNP0+j(AGN5%B~6 ztubRPYmzv+ZxY9liX+%o*dvG^^~d_&=ey*1`_%Xo zI(uLQ-_LK|F7e|fx7oxm<%*mvdb>J;y{L(2Yx(SdOv|U7i*3Fhb{>38N@tHgfc|*S z!A4tuEo1?<-8wYQPh$P9WNFNIeAy#tvxtvouq;g#pI2+Sk|m5YMV8KQ3ZOB_X_Rvc z+6X3O3G-`GmW<8*Snpz%m2q?KjrG1Izpc4A)<2fNMNflXsq?Uk$sb5T#bp*yLk zNk{TM*GP)*t5f*C?>>ba+Y-SG^)~oVk5_UFDW3mDo%AbuW9p;)4*o3C{2BDezLsZ6 zUD_+OO^&1QO4i}M?Bp}@8{w+1%X4X-gG`QGCwa^)xfDCL_e)K?T?x7&#s+;u?|i+T zZHf#CuD%w%lLCYA5cn}z(uSNsXWLjdOn!AcPd2J|Y#$Asv4T1G1W(Wf%Es(%p?wKG z5JuetjCKH1Zw32ez2nru-6rwPT<@+0kC4~+8{qSk z;4@!#<_PfDgz^WG*LB<4S-^c?svnU1mxrIlT|Y`+zv{W)=vB1JnIw8FK1JxzQf8C0 z_RvW;vJUmI*4I1yv9HAb@C4GkWv;_JJQMqwv=O;)&g&&!=l5hj<>|tCZ_`(lu=BtJ zVh5Q1;Ps|4x~AV=Sc7zE+{bHN2)@UDli&Qhl(^7%P;nJv59D~2PG5Ce00Sp)DY(xQ z{=ob=#HW`IUGUdeSQc||nCoidTIK=si&#n@iax53pMgKtp)H|HgY(n_(7kPH?)f67 zVXBt)xHm?gHoti61Z_Q!wyM}xgY!JLqvj{o&)+c?oZ(n0x`{X^U7><%S!wj9KpxQUm+ zbo@f-Z_%;3IrTWQ`2wDtubxcgX#4p^|nQ?6kLvd1btQ=i~GN~oS-~n z7~_s1xR>)F!0{r5d+jSo;Qq_8gK=^aa1vvzdM(crA?gm7j zySvwGn6VF?`5t&|SNJJ4HjW8pnUe=Gcz26Vz}cd(;8nf*6`o(s`W&C|1#m_9{pM#k z4JnJc{O+k%*_vAHgP|?jT)KSekn*?>?rzadurssFj)Lx10OWL&R zK9}R@$&x{Oa*yXJOHZEsiLxP`w+GOmCA0JgfW~VeTg-b|g#FGGooRIVV@F-#$%D+rkQvOyDQn$q1>a{)T2BfOCheu% zx!}QoD{wAiuYbOn?OHgLSP-T6ssO_zzEkp;yMeIq%oqBHzWn)kzMFq=usn(^Cg$Kz zCvXl{@<{nQ@-crOUlwzB5NCe#&ga-J^v_;Po^Xc;=3T&>@Ry>1`q;u5zzJi8vT)Zb z?uI%JFRkHxO5$__uROKO>7g& z6==9-@LAoALVxUqk6FL`T6Gr2)7XI+u~W{K{rC7eZ%Er^5O$q=hHc{gOeLqS!kemW zzRVvO^Ex%(ef=nL1{pu7et*`Zej5wa?=Q8kNngWp9xg-9p5$1k;Vh@NFHTw1d^U7s z{G7`Oi?wD1Jmb{!bA`uLn@1O+|7Dg8!B*iqajQS}ajT4jJ_xZnJ`q{hcZ|UX`Se10 z4?b1xd^(x9#u}oHyx4pPY>UbG4m<6gLz{dMe}EE8ZEqxxvt51 zc5t!NNLzGq#>tCZ85N7187KYQ*NDrT2pa{u824>R>>1=wV#%$x70ph0T+wWW=s%3b zfxcGqEaxHEgUVj`NMcV=U#%%LY`Q+r^hM#r!r4u%>09$$VpDY-yr^kp75o@RuKOy) za{re1if`iYWh@7~fqSB|=8`kZVwZeu*}uwXHmIL(X@AolLVu5e+^%9XqYm)q4QoH! z;5xL8CZez5_`62QxNQZ-TkFZ=TzkhV(f~M`pw?n$)E_(i%3!*ERKee3cMBaqZqmQ? z`u>wvMYmWt+qc&C;FT1+;*=2NbY1ms@vW?|)`z7|=pu|8w6iTL9{HO^B_r1@a*q5X z?Mk8DI@0d&FxVcc>-3a%zZFHhvq&$o6%Q!C0NXrlGgjb^^W7(|dn(Sb#_bl}2ou-H zqzUY|`cOwnl75$3^ox>y)yL2edpMd;E*#RI;uo>!Ec*Q{oA{HpL75HXJIXj5(q{|l z>FJ_Q>&wP-2lUtxnG>@H9H_#NEj-eS=UzJt=eqJz){}OOH%re8{N4p0RNv*{!EvOi zeqp}Xe$%OX-LZvmS?g7{t=Aq)!kb`EJcK({HyHK)*ddW!%;$1u#1%PpR_HoQ-`_qt z06~An;VuwFIOX}fl1@7ZGbt&fwwXcYC=coi-xn;1f1b$*Wx3j%ad6;wd zjmn?dBYg-@%383eon`R5UcEmgI@IDtSJ*h8{yE+c?plPiFr(g;`AU6%PUewNE#lz$ zF7v?+Y>RmJ$7jywcftw!EWq#A_?vvrXW;Q!+E&7T=cy}^mwYPUqugbwX5} zqS~GDv6Mqzarte2X32dP{f6MnUfus`y4*@t?xWL`44Yd4&pzf#Sb%t4w@hK{bPPGUiHUzJVw|D^TUVI2k5W;-5=h^GWf0ep?f!fpO_!I zPT_mS4=VO1$q#L!BNaZT3;!nhVftd$v+xKy&(U;N`$h5T!}uQf({%p0UDNNNqBG_i zq2brdP>i1w(lAvftvUNLoOL2CKm(qyKz!8`4$xthro$?K{Mu~+G>SgHc`qUBE1YYL zf0pWdXntqL#`(J=WzZ+zg&pL>-%4+bu>gHcsAkkaE1fJ!7ir3e|zzrb;9My*WLJbtA<98ay-p9RqSLB)j&%!+9zZ+i>K!=n)&RxDh!; zxNlhB<1k!1&y3Ei7+!`pYv=j?QdVd;=)3K}-Ni>6us??HL?)b46}L zCHuG{_vgC}FZ%JJ53yxa_dU_a*X#rD)*YvR?19QVj<+9p&GBr@WBgE+{+)*3xD(R; zE$F6|h%xAIMogc_-F`Cpj_YU7ILr&?*`k+q>>bWX=JBkOhq6=lF7?`3A`he)bE)$j zlhSJwL?4n4%BISjrJswwb(drZ%AP?UlLzq+W%?!~&rDXx{i#_5}xeIJJcCXR;-ON=sDQwiC`~h4$ zG+*?)_GgnXoQf`aSQEvUg!dtSOOGAn61{L+5dH={qwPq0fH=ZC(cw4ONz6gDqM z5$xIpOLiJ*EYI^;%&o#Y!JXXaneaY&7-h#54wrbkJZTp>3YDEP;W@rXo19nKW}a?y z)}S_#GbE)=(PJp<7}RF%Z>%<#>NXkAI1%M04l4KXe^}*2zL1YJU!9{y>|<1|jhTBy z!FQ&N8FG!>KFC4fjC!$m5E4FkkbE%iYW4+M4v>zZ*>lCKC;lt_lb~C-Ix{%%5p$8K z_Rc!M@}v=TdKk1>$2^^iN5+-%9qWQ$ceUgh;vAFRZAj7prxC`gfeJ8>D`GlZ@# zO;_Ok=t|Ug{Db;kL+a1g^&dk0s>7x9(HX{gk5NRq;F*TyZ~6+BKl{g^Z`9UK!m8!~ z$QbSr$oCktVSB$i9=Xn&aQA5{yiFz08+FDS<~_xjmF`mCQ+X3{u<18w82$=h${xux zh*)mlsJH9bAFANqY4F)kamMIh4adJ7Jhhf{o!`t`&-bBIqlUL7zNyKzvZ`^c_?xL~ zW^s)PnA@cc#-Z^2ZLyCyhERjpK+~2#_eU$o4c6t2q5~i!0oYv*)yAKdoHgcEfks=p z*FyeakEwUt7PEaVe}&;E3@`RR!_3Et_#GSg$Hu!Rn)7&`BWOB>Dnw$LBX z>XCOO$CveK7mDyBIY&4@;u&(A#pfzNd&e#{&)HnmgPd0p)3rRftL%P zYg&mi0abJ?F`e5dAcD<*C7$0}ZB zOcUR6A4B6HF-L(0sWE%d(dX@j9=O>RnUe0RnR1ptUMch|!CBz$NL?G@;rhX6zg=ji zVYkz#kC>=_&Xd&Lj1S}d2b~-v4m^$Lb$b@R_^`)4rP`A>i%`7+2I@Y zq|g^|2bc#TwCr@+;46HiqQWPw7a>Q6+TZBFTCAJrdVl>V-YZ!(7UN&CVY+c-Ly7#Z z(&cJFqseUFH)@N>6YtnW*(%B<)`H!@&t%XFzNet@D*CPxx=Nj=P6-%l{pG>AC-BWU z*ehQ&RnE;}UQ+yChc-TU;hduAaam)Zei`fF-InF6GdCB5Ka0Ia)}cQdBbWo!H|mKN zz7M6h8KIwKZpOU4Ut|LMOTqcfRKC~rjf!5AcGX?a`gwd4?ms9vhMRuv*CR47vNdE1GFCjow@~gvfFUir5AQkRns|@ zxlNgwo7YyD9iWTsp$H8kQ`Rck`l-OB^r+-+LG0BOtZ9yJ4fS|H1D@-&ZSrM(l!q?) zv4uxl@{>H1JV)EVjPVuPN2a`k_J^0hi~TgSV^^=0r_)sT^&me6bN{sIvhaV5emsE2 zDVH2CVnTP(ZwZ)1zbbh@?<~LhlPg(Ht&tAzDS$Ohm1{ z2kZ5a)4_9t77nnsErLyXi_jnI787={#MeS^?(J%ArU3X|* z%Q_jl&aWX;G9Qb-I8sF4F64Gu52{n%lJV>Kox1KUh3~ZIhaLOMpTWH=Z+CQAwB9hv z7`Xv=6zAKGa;#Uxj|SL2)$NK6gfV=jXxVcVwsbk~3vfk&2i)h!xu1Sko~sCj&kifti)*jHVGXMo?jzd+$>DrDkj zkxSwgcu{_b$kY+|p5If6+}*e*EmS}|!K_Z6MW1@M8u!g>Q4RAN_jycvPi3w_-KWdX z(S7*w?o|}yClQ-Q*VqHQxajB*6tRI~#>xFS>k>^J{ak6+HR!dKDlCC|1v>wd>^@qDkh#q5TR zyBf_iCnEpN-sboE`Z}EP#`{7yEXOhEHJJDTNehhSb&O>M zUJE@GAAY#3HN&uezAS+=g>ZHaBcwz?I|ur8x)i za{jC8&P4myBWKWGjGW%<=>~L4){5rhL0W*SNcVHSLA|IP-nZY6X85S*$3PX zsZ-N|yNh58kJ>zmWxAJls{3~w);xTHXH`Mn@BH!MFY-O-Cbm5g@3f=sD&`gGuIt3U z=kQ1Qo<4uJi}yLz?HP`ez|^R3)E{^AotoD=^W5GHJ;z_L_^}Gn0UVdi|FCJgP?R5u z*<_toXQ#yWSUKA;x4U{OJN7EvUhrSUi`F%(T5@o2;7Irc;D`5{H%Y(1`8WFYtoUfQ z$NrE5&EbmtJWsc|RINYkR|f!;vR#Z7y6<=VrhRtGbLLA#{oiy@N5V(d@2a`rui;KL zcH#>8ux<;s2lGpAFEV@UsC-^b@Bro~0-sM+*WG*6YSJZSmECsovnOEwV8eQZJ z8-3*+d;MnC?aB6xsKUjxQU3Vfg-?1=rl$hu%L~M=%3>dAs~2{a+*<@0&C>0r@D1cR zY8VgI86({|Pf7o2L&cPbOPy1rGYngl=V`FMdR1s6c~Gap5BEcn|MIRPIa6F?W890Nc~`^VR&^S=4M6I70zDZ+1K4v`?uLIVa^cbt^Bjqux#4bUGf-ssGOlpScjZ6F+Wtug$~@|i`-R46 zhko@m_|>C${(C&XDWyE0GnVT=@my?U&Zng9+Ja68c(~1%^-i$WiF}Z z5Mzk47A-wt@Y2F*KO#<%r!$xNIB}lLXzWz{1e^G(R^SD`P}={s%m+1ooRKGIqOi6^ zKf{7M4R6ymGjc=kOfSHl;`ZQ&hPU?+Vpm5PxqTxU`%_mzINc?%A`PUlyT-9JI>fu#3#qriW~(PM;7?mz~3A` z$KPD?I(+TkzO!+6LGrtUe2-_cM!}k{8*t>IJANczM^n)i8ylSS5#ML_hU%}tUd50- z(i=_>Lf>$$xC!!$yWi_JNuJoVVPEC*%o%*Y0ex36=k5_z)?L94mcw}WNpJc;+Pur^ z!bWh(x!0bz5&Vp{T{U%4-aTgNceLq+omK?jdEC`LW?ATGRz9f<|I=?a2|o_i_p0pZ zLiG14>W({3UC_YXky5T(gJcWii`e3@?|BZ*vT6OXm)Zk(r|pgt`J#}A%oBiYl(`}o zo#(2lK~757Sk@!|yDQhu>H+>6-#c?+OC|cL@dx$(MPB23=ODMv8B2mS!w!aO25`0k za|hNBzO4PpJutn;=*#+BAN2Ti`o?GF6Q+zt{M^nnI4c2%wf;G}kK>4*Xzc|j#>0N~T z_rjR#@V5`+M;>7t@ptTAn1M4Rk8I<+@~|Gt367k2uy1xYQd{vVNxeI|m&Y4&b59fM}`A0(gF9=R= zNn_6GkKC0{ZIwN~0`Bo$k+R2UgwW(Tw2ObXBK+$$j5PdAgWu8o5Hwqfwx7n`eKyd+ zTUbJUP97g!NI9~2)`Guz32^-geEYBuU6G4%E~f4v{H==r!&n0ej$`k`=FKfU7rbS9 zXfx-cuQ+wCT8F-gHAi}P)4Be?j&~yN`CO6NLal>fJ6?24=mQV^=FknbVjsA|(9Npt z*23~pTVS_t`bV&GO_hZ8q8)&BpWkc{9S*$LqfHCm3jyEfcov|}v-lJ90{09zl{y?_ z@cx5E&H?6>a+NyYL443xjPZMVdP_WN!N$8MVFUM+o`CmD!VVb&&f?Klo-~-J2eN~8 z-(^g*ERy2`A*G5P#pt---AO z`ozcK?|b-5Ixv1A3k<+dB0rKZYaiDyzX5+%lo!8^;p&I;auKM0a$!UW8NYRZ|sR&dm6{o{2uNggYOJHuX2;%o!n>9?m@1< zi?Ies?O-P*LOh z)qf7xeJ_fB3s494K5-m8+md)5SbqXMZ~8yN^CQ6Xj3k}|k8wQTfakEnbD)*wPK@UV zFH`SR@%-BFP=%iap0&=x{^7;DT{WF9J%_IH$6}`hP!RO_#`PKWwQF#_`1F5N5Bh_7 zSvO&xg&(*Mv17yX5wEua;E3QhDRQG@ZF8!9;_c9g3ip$ z_oA$e^S{=Kuq`j%mw?|?cVZBJ;3(dma*q^rcp=(#xY0iDtjw1_DvbQV49s1a^SNdq zf6}JHng?qsv4=P;eiC*^6LWcHy*gIi7m7I^`?t(FF;p+`o-cjxtK?OOeu4kW)}h9a zTqJeKZ-!?*v@_)XSKD2Uzdbj-)baMCi=0n=3j1(0b;A74bIZUd)1Xs%&c4^u!>HRw zxyEze(O1>@@b>)1ZSS5HTK8V?`gJ*DrksOy+`TI9Q|J|O%^=Ni4svP$1L7Rv4ck^) z?OAOBPdSaP=N@4D=&!!9wRVTwi@g))Lurk_HUbS5y`#C8(=^F1d)%V?%%iiFkHj{y z+!5xQaAcz!{)`RKVJNHi!uucBWq7wqeRUn?Yv(S~{?%YjhNHJp;9X(y7U2AtaDx8J zRDHw%tFyad&?iUo$skTa?0@J+tX+?rSBQNAe}$IrYVL+l2zpiPua&NdD+6?guDg0N z`=bx|I65rskKHdiF44|LoEvWUw4pwI)u?}!zzhAhgElkzoG7{*@$%H&z$fOHFCN3$ zVkGAf`ONja&3t3rlZs>hH8YwQ|1H=U!G5F64Sly$&tpzX+%Mc>-6@y#iskQ455wPS z*~941h8#JTO}v!&Po1kUe@w^N>bZAwoG~J&&HdTL0ptgMj91p-D?bVUH}-eZ@%)rj z+m`v`Lw~zq(42^|yQ=HXEz>-^20Smeb+GQ;vuIai?~OdWKe&8*e)HRB2d{rCXAFHY zj2A#%@i}XrovV1ZS8N^Hq2Srg7SB?CQuwy3vGZKo3Husb)1HH@RW-IhxI=O`-TobI z2luTfPJM=L8qPMnmcmbY!cRPJ*$x|yd5Zow{AAgE?Ve3Zep>ie0LVK5KjFOCx($4v z;3wLR?Vf0opBAp?dyWyj5p8Tue;=|EYHY9F(fFhh2(~|zM%fCr17_HNy`VMbHRh(^ zo^rLwVR(ofik;P*bp^pKvOHnGd5QARE{ivyg5p6(Ae!z>B4_tVBY?fA0sYh~kz zf8?b=0v7CVaNml#Jh&!fyYLh4)9>ZavG0#=9`qjbndEy)u4k8~%B|w}L`*XCw+*t# zA!l6g6_z?q%3kDbt59o8`2B!C$~*KEa#}p(U@j}@Q!mPkEeQL)bdfWs0yNuO>Nx9+ zMb0DqzM~{;jNv$lL#{P3o)OB{GLNSlp*PmPhAy<;co921@=53o%+*K5-xzr=%Y~bI zlQj0Y11PHLl26|1uIUWu-$j_inJa$~59oOxYa!Q^~rO@5qvmHTw$CIjqptI zoQuG3t#}tj5q=VLxG!tZh2R^TI!n{;klaJay;GiMN~}rQyNEG)2LE91GH#EtUYOGi zO%us==W$T(=w(%m{kn=#uRScgD$EnwycA=}Q#R&D{z8J*D z1Ky)=VSk8o`HhU%Q*-V-DF?q<&hm}_5d>dnVqYqUv8zqRmAUW{wjnKUF8pwL)Mtz@ zLp|o`a|Ay;kmfh%xe06c(?)*suCsZvjsx$^TG3)O=VusiqRp%DcQ4|H>fIB~^U%)- z_!nxGf8phE$M-MTu)ax~h2|MUyhMHHZ|k28cDd~c+C4;h#@|oj?^ZrSjK`YekW1F6 ztDtZF*qlHzkMjOz@G9_m6?|J=#IKSy`nOy-7aR{@jhY78|KGGJxpL8FfNesC=wnL% zU$#-L;i}bbEI}I?$7v(&1Y<*ts4YjgvE(>odl$Mof;NZ!@7grB>Namo8QbvxWg9`= zMt#cIGLO?n`U%FyJa+?L1w;_cA3{-~TcAy@PjuFMsdPn3Jw}gM8XD-3ZOA zh~R$gzu^AqbCAdUdqMP>J7j)fY%Tc+XZY!3J9;p|OIR-`-_6mzd|v{+qV;?jb4551 z38xi%vDOlK`}---A2?6`K!83rtYwBn4zeg$cjDeQ+zpVs{L)_pb)41k;6}()`-g}* zn)`mhKrGxf()SXPcZnZxKZ$p{`3`;$#z`5zter{U-e_gVTJ{CtlRXKkA8~(xy$P?L zH(Ka}^v6CCodp{P-#^{Xb}?6kyHnPDc*IXa?^|sr=AYQda|0+~%`r=KEYk7r9wrVk zztk^s8q1qX{m1y;@jhGD<<7d#xp#FL){)~Ym`{s-v(9?@9wE!zzmRySZtR&|KGc5? zJHYBk)?5{#(H5-iv(%Ax?IPzw#7?De#IjZS1tTS^lrF4Na|H%ujmH`X@=|YHinTQP zLgBMy6J&Z%LqX6Qk?ZY}k{q`I9zD9qy<9nO_o`dfl`guRrrqa zh;KyrW_p-@XTUxk`NIGs=|wnQk}^LFQ}D&@0aR3cUHS)YcYxOnSBtZg zxrt~Ss~GUuA1-9s)*|B+$|&R^uc>?n_SF7z7W{FviS5-jMhWxmSJ!MBJ+Htzw_8);C;}xwFB!JKErMVa^=DykJI9&enM7>uVQP8rX9+ z_cxlKo`v!`c(-@3F1hg@>JZw;gqJolaC@rX{QF||r(i70Q1~+!lsarv7da1V zvLduwa;!cn!~U*05pxCg#;fEZ{wAOJvbI;Fd43Y>0nRxVkKl~+qXE49NBrQ+iayG< zUipLY|6yOvv#qn<(*+!2=LR$oUp#dIbMRBQsEO)@Uq(aaH3u4;PtL&jbJa6Do}Gzjz4UeG zG@YHu2lp`dZYU2J`x^X(EpDz<{POffbiq%OUjkUSWpKju2kv#{=n$H8+J zWxI>JpzmUvrlOeOL&t;hY$)~*pIV1K_Ox5yfiLAvoMpovWP|gZgC5*J@98vhx)Ea- z+V*)aW5{7oF4i*ZMNUz>wrwD@6(Y~e<8S<)z7EgZ89CbDu|w8mvHyJB>YFrdE_>tm z_`46_@FQoR>7yIM_m2he@*nX%<8Vs7Mz|**wnwEguA%a_gQPWWKj32(Wt#7ly-+q< z*$IPWSKA3?Un>3Mn=Sp~zoC5}E7#?)A7LjrU#47d(Q>^9VDppE)v3C|lIx^>Qa)Yv zd+Cb-RKoXvrr!YjUFyt%T%EYz5~X+Ty3dxO^8@l~)Ui)Z(^OuEykA<%cPHR=oZoJ> z@jZQH&>Iovatzg-M!m}UFWmhNcBU1-RvtAn%DZe+Yu+{{)|iuxgp8)>tc#I{54uCa z7AbF4xVmJ6g6+6-1oqWeh|cDC)qd#5EiA9csdaTZd652^dX*DO-`kEmG^tvXxq$|@nUR4bhT}rKXyk&0G(3*z^7$+ z9E`Cz0{_=-2b(HBD+fd40(`ULk6_BOtK&Oz#xvMwWox}|yl=hzd5&Ol?H zJn#3f{n{ATbCuAeUbnLccNFH)jsX4g&onbM-o58E_xI<*r;wv)IqNmTgFdTEXDB~7 zec(Ed$%QuUp@J=JqY%3Lr19m(bjF8qy+AvVx{7Nq_@nI&BZuF^xVG-Mp|`ds^b_{D zXDwwP&}BLBE5Rl@yo$dyoyMiW_A3iEz*EaJOU;dlp(uwQU6;XluMthywuJ72?JTmG@o*q=<`Lk30?Iy~QdV-8 zpv}7m!!%@&~=`Vlsm+IJ%S z;AQB}>Ei=vkg!6R94F87tujW!z`VZSG#^-VqTl@Vt-^GDE*g57_NY}4@;HaO6a5?3 zv&)nIfy7+P^HtP2D3@l9ZHUbm;MgsjSJ``C$bDoOV}4R+)8_RG{Yzj!25R6p2RyLd zWUWCs8Vde`d7u8&ml<=oAJ1&S*$|z}^<&FxD9(BP8|OvZ=ahBMkBo`+n4R^_CSt4A zy1AwM0od)ZX{Xa}g^dpyowl;s$if*k#NC?1@{wy9zZr8mPR)CN@6ob?`^A-ylQ8(Q zHtz>G{3PZ!Yfh$)VVs!ekHp$CTm$%z;gP@3Q?i>_SI)xt>$whSm}KA{CxgB=`{GGa z@tLvBUbKNdlXDR#JQgt7w0y{(Cf2`t4Oq540O0Wc1iTf#o$oOgl|wvhb22U|P5H@a z7q-|tR}!Bb6V5NDTX<}(GI9Zz1HK*F>0Zp+vk41vqvYj}(c~J~+&_2?Z0^CD$`L}` zIAk$O{i}Hf>-U*hFP04>Y=d?2msesPcL4oN*W=xqBKwfxnb4!9V*ddz2XvV=L(2RR zWjfh5=>DiKGel?2ybomrHzG@rU5syt&YJl&$}D0#$JZ|y+aUcivj^p;s($19Q(%G~ zn3aQfqW?%M%wthB%TGej01u;EocF9C9cV9*&nTZ3Ex^kQxR*m5dfx6_gElLKcVlnJ zSO??ri?Ntr*W#a?pTXGjVZ(k2`qBsOQh0owv8GD- z-gI^4Mx5(*Q~zBC9Hru62<~_J&EoepjNhosExH39JM9_uFX5*h65}-6l5OC<*wPKh zv=5)uS%eX5>k)GRCvsgzfi8Xo_rbb%7#~4hX*6Br-?*&C@UmQ}Y34@$Bi&ip`snh7 z#0}S%Txa5Zhxn0Fzq3!pGsoWlN?iG}e*YHN?&Qlw@S{pT~JZkJxBoYdwu;^qbp}>&=eu+Es7f7BaeMFRh234*Y(Bk)!=LfPSUb;{U3ryuui#nw;JOI zuj%J^GOwb3C*x<|h!YfFxHqirmB?(Kfq@_9V$liUU+cU|6gn9>7mzdM>^GK+|IbsI z+k*Ho@CAKzJD^LSQ?Q#`*f!4H9zk11IqYgHrv&2Br+VC%oWpiJd9(ZHmA$dtA6qXv zjJVPMq#~ZBfwZxa&kNa}6?k5j>Z*s?w!rMi+u#ECUt)I;A zik{{i!1fl$ys}P>8FOzF>e%r27U{N@LfK}( z=2qi4bT|5uyIGOfd@667>MtX77NGonLY{jcDeTlhhkI%u(1|g3 zD?tz0b5e7Xjc0mouCRgwFkHs6CD$6G!!`FH|M4Ngdnst7dHE3clnx&;aBc;Bz`3st zc4Q0A*%V-%M4pPjA$52LmGpwY!ppjR2R^}Brv0a|4~cJ#>-+l92K5WqQ@Rb2AH|yn z&XpPx&&zke*bTViZ^~L*znA^%dpkQ;-$WSDH)tMzSo06;NFU~lscYF*%Cm`hrtBZIr*J!ChTk0a8Ov!sEwM`~ zJ|nZpwbQ?V^$`<;{B*twm7gwcJj*M5yy=#@2|31|5z0f32m^HaRKe-7ZQF2`%)f+u zdgimnk&-qeEzsV7q@>H%9c;htAlnGEhYiMLHNX9xdjlW(aL(1&jCO~ECwbRfKX`7G zXWcgBG4j2^vj={&Ncf5LFKIK+4zy#wK|4L5f3Q7sC+h#X7SGT|Cf_ygj*j+3VarX* zchTl;nDpRFy}#|my1r-;$K>k5Kit9k8~l?vY4Cx>7RL?|MHmMI9@%)@>bl z)}5JBH~l%jQ{~01ef5*x9<R!@`b1V6m zYe4GWRRx`R|LH0E*#(lv>2E1d2V{OB=e^Et-|UiBvF3Een@vBhbk56F+yl^zH5 z;KFSIf9&lEtOwrw*t4!;tg0X1k9pvTFY6&MUh!iK!!tn}KNLAX*O&ERjCMZfDxQZ3 zK7h3a^iKCy=r7!(5PB;SCoZ}h^TNlFo0UB!o%NvWILZyn;k%u4Q`02L+Z#r?=hy~$ zTy!tqm4~sfvTic`FlD3Rb77ec7h`V@dRW#Eu1G~0@D5&?Ec`{<9E)FDw?Fn{;T8CP z!dBaU^T5?88y*}pQ*k};w@KUEYf2uJv*ANvO1rjZ-9o^GxE%#kwuVXX4OGB?Vcs~n zuddII(-&f;%vs0A=TyMXF$;BkPDk^$H*?Fq#u!i7$ZXtZIBFKo@Fruit(cxl*i7-> zBf8f=6iKqTQoSUW;8fA!Dqraqe1(J8um&T+S$aix8QsX=IU;_%R$Fbqn!yj zr+RG-l3-tq`!GNUtKMAH3l1LhJF#QOTnT$1UWK-yKcIIu;69K<+wZ){`y}XxZL0KD zHC4oa(o}E-@~2l-LB4RF=z2$!^Oe8yEDhF6_aas;2j>raDR*d7?*q3b?TO)vXX&$H z9x3H>h)+;4t?CfFjz7Pn!HZ5olj5ju1k`QJU$SQL5=apqlyaW~E|z~{zt6I(KH z&in(&-cFobKL^hA;bw2^Nb^(Vv&qEwqiP?=aLh2^+de0sb*_R>a|LyK=((aQSLA_< z{P8{O!J^E!CC^<^wAaQt=>9{1!(^ZS_)E7BDTCYxurVS({{!CD@g4H`?V>*z`$r9K zufPZY?2Q-l9b)dfd|40dz*l|}J}f+|fZWcNv$Jm}WHd-T;B1tgd-Z^MS-!eM+=sth z2Up`S)>y`}Cg)kX>KxLK`mMY8AzZ-sl%;kk(8!1 zg|M~rUqRepperhlcFJ&r4IOAv>|oOk{is7HSX871aiBO_+T{Ivz0SQ!(^B2O&-2Uk zK$H9T+;h%7_uO;OJ=bv=&Rh{^pFS?Uzoq+?GbhSDd(ab5b3Ah)+vNTi^&YC+yK7KR z!#+#E&wfTho>+{)dhXk>hBm{O7IG!uOTYFvuVB66$_Xyemo_%l_F?K%o^?6U)$GNO z)WhbvljYEM7TaOFYzI7w`;SH(d}(YRbMIl6(e0#?w(30;&u-M)&N~NXjaK4uey*$A z@HJ*#Qc-|A-~~p~oPyJaze!^5OaIbu&~se(J)Z4iY|vxFTQcB#f;Lj|m-dHL;u`5| zre@wi9G>F4+xYuA@KWLlUp|m^)qFJFgh{nM*{a&k6_`=}8OZf5Trb?oPXthfpQzqV zx+9(UVCTqPw|;o!e;dIgPZ|pV(?m_D!dU(&+BhJOJmPP-rRf`Z1F^SCANHbS@&RJi zM9SX-`1VD31NF%p&D4+4mtFVeV_&|FyIPQzA-M0wAD2&IyP7|0vCgyvo-h&uRho`L zBVN()ufthR(@^{-6b&a2*SxSEcGVYH1~9nDPx!kXyk)^L$^4nj8kBSdQ`7M@D)}gKSk#^4JuRqJ&{|5dliQ%uDTw6EJv>0Ciyp;*wYNUND zT;D@~;Vq2CDfHL8MSCK6CjDKG?^@hVgZ@|EQ)lvx;2GA5jQFQf!K#v_=}UaL7o%_kjJ{L$NJ<;)&~!5sYf2{ zFPLwkQeEB_QaQyehrd&lMeBZa;BAeBXVmz~dR{`p+qfLUfq?kQ_bCtt*r zt%=FQ8z$D+ym{j12|r$G5Bk16coXcABjKjtH3RgUO;u=PjjXQ#f^LzC0Td%>;nzQ$vY zX!oK+#Z!-uYtIFY@K@63(B43uky5?eMjx!A>g(`EBKbY^aw6-nEcu+aR*cKa`?#pv z@11VeRs3_)&pe?WLhEBY)tZc^_=(Q=C{uJz{wqHq4WHU1^RTKX@i) zUHSKMU1ohu8S+F~4~|YX>M}me+p^elk@XDwTo>joY&)dezt`&WZj>(v9~fng3EjYz zz3Nh9AJ%X?&nHG^8pC-n0DGK7tVL?CtMHF=?~8>uaPHdA`edYgLSE5#b0_dl!DXL1 z3U15t-{T&Qez!sv>{ER6SkFGm0$mL84*b~Puf*MRck*KwfPcnUjlkOS%Tc~7^C3@g z*MhX*J|A=MG~e0k?@qWs5?h07mEm=ONcKapLE|31*rq1JK7@Te#+@H+cC)DW6-5|mj`1k zXL-`soxJix0)|gi=R#Ty=eOwSSa*0YpK*qI@pou>l^2z0OTqfYJC1cueU(owNCW>DX4Q=iB-A=7ewO+dqc9Xjt>DrGLKtdavfmZ{gc7bqow6 zhHty5f5q_asn>h+?RL@8`&kRXw=-uEuCw{}(TGmlhi@O9_uunv%~ZDi|3BY83X6+; zV)*uvH#wK&9zSe^{;*T84S~13C<+?@`E(O}TO)kBo@<5Htuv@gBHu2 zby0?LzPOVgA)ho<{~5MY&NJ-qInQF}82PYgo`rU^uLy5)otpOSfIQX%f67@p^zu#6 z=PZPya_6gYTAzXqSlJ-Z96vHfd#mix{gR^pLmF*U^ZTS3(0kYSNkh4=+SoMiMRA6- z$zRNSO6P=c4j8uBe9CLCd%ff1Rg2!N>6rzcEerUeuL$=DX;qnLQfo_%AnriM`HPKl zMaAzg1?+zDdiHgHFCw_aCkJfWqc!>~W{9Oywi9NU`+ z8&`ym8SS%;daWM}<2`QdZ5Z2}Z!{&h{S0^D&Cl;pI+od^%VS;Q7}0=cqog{ou?Cmg`JuLmFI{&lmBLg%lHIS=Q6ZUY6L z)Y={9za+*9bXlD3j&$To-nmt|jBRP|$9h<(U`yzZp1+u1s5dJ?_ny1-|0B8q`Yel# zMc$E+c8}xS!ko_j)%dMGtrKgNa;!^m?Og{GqDY^>4wkPhxQSO2kHd z^Bi?9^}ENtXtGCF0_+>qTfC~@V^g)>Dd(QxOX&MI-^%GqF_Na*hAeWpe^J>y!O?faf5x=Z8OIt}63e;W4$I{Nx0R^Aoer{mB%* z3#fc%Hrg1^1->3#d9SK9AV1C+ng`xvT$j8F2*=f|>{hXIEBo{rg=EvvG( z-Lk=%_DFs9>RW!Beg7?QWZycw1^R*bfh;}zC-`N&DEOt`hIx2GuODT3%~*rbzWAA7 z%?2EubKkR#<2ux<{~5F2ayo7~<1}!$uoC^8p53zKJ*-LF3a#0zAABwQ{s(`Tee3O? zMf&7fI{Fk@ruvNA!+l9xBlaSR;G;m)9s-~3Lii}8Oz{Fin#bth!1&OJq4Ns;g>dG5 z@+Ri@q`$lm>DM9sYNnqpOXE*@LQ@Vg&lBEN$G@~WdP0jw%6HhkNNeCCHjYfX^2tc} z%-(XFwnZbpPw5p z`R&7{Z|oU5M`ACw%<>S|V2q>Q6KZrw9@q9haB)rmPI<2iviYmCVVnISd;*TM{*tjt zEuEu`S_kZ1j}?RdGamE-z6_+f@y|tg?8Qb(*L7xxdvMV_*5N+ug*?bIyS^(HFuEV} zfj&L9m2qf8*|cPCeYP?tuV=kbd5e1TV8@EcHp%TbqtO^pw9y!VeH{E44j*T%Ivr1EfX5til4EVdo!Oo^IztXHt?K}M zbDOC@cJJdj<-I`a6ya^czlt8=6VQcEc*3vbvJPqO36;Mq-}~pKRLx5-gI=4UlW!%D zC|dtocvzW zA8Ni8<^pG9gdTkJQRwmeC)kIghY8<3GpC1kUA5|sx|$pm;V0&ex*Em<#JzmzCxF|z zv+$=L^Cg*Ypa*;v?~^fc51@Q(WFLTaqsA7y4_Nma@P@g=Gk+D|Ws}Gi+`k1cG>WVd zI79bMLbI}`0E|6*f_U+Vq#w>EO$!_QzZrWY{>57HEq57SJj^yZcC2&nL;% zHOwIm^N$tG)6&q^LeFkwyX{kOelQmC@be98=WP7O_pWxs z+I7-!0mh8Fu|0b2(oqj${v4U3_D#Ru%lhae)wRmv;(ieOEW9zB)Ik5vid@8!9gnv> zXVfvye=^d#oilcC&%@Xx#@k$3|IavA8dI{H@vdh>z{p-bOC1$+a4Wvc*`1V|_?WM1 z%B`t&gN&fVMLucc8d(Ck6ByIS-_fzTvJLi&;dU3{<$DVi1<>7b{)gY?i1)>P@q)UM zMs?M>6H*yl66a1q)K6r4Y-7v_p4%E_6L2gjeL{vPzOag1bllFnzQEHU zL-sbH4cPa>yPd7z5wr!IE}(q_J}V3Kx%v94w8G6AW^2{#$a@fGedS@pMtP@b^XU`S zH^P>Sw|As}$QSev+$6o}HKg4D{_qnYI7iKuoMrH(3O4TR@x?J~V)bR*5!d^Zm z{oKif9y3YC$nz=0e%UW_6uNR%=7p(%!BUYShuHUa;|umkJ6jG#dV`b%KcaBomeFT3c+)KheJo;zL zojhf~^k)cGj2=B!Y&eh`kv7p*jgnDvx12H!_LRy~9B<4$E*J1+h5vUVeWtKa68|W> zH@;2$qY5rs<-e!%ZKUJPp2k*x@vW);%rw+7w;=9Kp<@*6!o?n+ulSr+^w$uDN!wh| z-$?45kZB7sM!)#_(Pw>qW1Gl~;BD`ydpJ+Y+*Gt^O5t6vGM}PO8^hp1E>S} z-ZM6HA<``5Yv663`rEw?dqMhS)#~!ho22t(oXA@+?ckw2yWJg+uy#0`PVc-;1#*MQZ*c7K-9? zzIoL8ZFnUO>oS`pW<%s2pH91geB%UEeeZ9gFYuyqjxKkh=oUyThHO-6jR%;#8h!f~ryVix9E+T)qlYKdT$L~&FOdI$@-cfjPXXAm$e2K`Gn%Ra6 zd-93?L2mXn4z`|T4NE<~Ptq{>VJn^$lPCTOkTpXhu=)qK39O*m*Tip4K~t)_OWc$~ zIpw$FO+d&l|DY3CPt6c$LeLj3+nTyPiSRl4A4gn}mvC1UZ{!vYP&R-k zOTB0WZS%ZD-w*xOi%cVq)f|4{9sJ}c%4hE62j&sa#79kLQ$_^883WHTv9?ksKxTK| zi@T(pM>!m0q%85(#&d(1bGcH)sNuOR&*!FQzQ1(s=%x!n>ovj&S@U_Ul)7ApBzSW5*OBa zaAO8#81C1JzaacShq(ujPh`HLk0p&T*OxXCZ=LoAyt5gcb|c~OgvLG0a<~g*n^VmQ-l@}oGgt9G_blJwb89AM86oCllS8V7d31m02a zv?<&}?goqMJmGK!>w=$@EfwceNT=SBfV^4E)4Z$pr_0O?@Me#_Os|;{{)RlK9W^S0 zDf^Jdbq9N%kJqw2;PPA7Ew%5t?>P58Jhy`#?k%m$wUK7Wc2k$NP+Ik!di})*UkHfUf~|>{-`Z zjM}y1ji0?R(s;-d-jT_^)t+VDdHfE!RoB%awzalC{@UQ%ycH}X^VK|c$Y0sHnYuXW zyucZd^Tjvt_iOy#jeTAg^)1=!)pM`6a4V|v6CbcD*^0D#DO2c6O?|>+o)8|@yf5)% zsoQA1CO|xs&k$pe@^&X}$iz8fM~m*FaRdAKza?%A_{65q$1+$JdIf2Ag%?1;u9xNa zcWC%KE)bYS*ZX=T`-k2sae*K!LM+d@O8M?iF6+imeq!wDWlvxnb13_cJtKRfH}dC# zZ{Ul9JCj=fEc!d?P9GG&d%YJ8IELfBx>uo>wKS#Xd<+?Wq{&9wLH?&WT-Z0k$KTnE zzXNc$!0PH)Xg!B}s6u@%DeoR)4r06=cWB$L)>lu>Qe&WwV1C4YF^;R*XV(Wx24Fl5 z7)!oVL2a66x);zsV zT4LuIWR1vGtT*T4UiZd+_?0#<@@s7jzt;8e>x&{+)HBfxQA` z5a$N_)P6DhnHKqdGj#FTI@I-89(dl5runf1@LxY=Fvr3Vu|VX*GF(U5W+aV&RozC~ z$>j{4aTIdqg2##*_eajpX&>rakK<-tzG;f}_l$kx)WM*E;ZD9+^uM-Jz!}~kb|tkUNp$^ zYP?O9&1IYe0!IVj3jW}KF}62Z4*I8^aU3{i|7bUDC;OCsHT71&`Y_9Y|3NG@M?K*t z!IiY_7r8P_moG*6SyEo+@q!Y4hGJc}!*KWDd+K)qqpXh#-#4D_!FNLq>vMmjY{6e% z%6*>#^^=?3>$fzYhLzqm3~*n;uKMpTEvl=);*icyt(il(=`6xH0&i2)gHBO-$C` z)5+721<;p`IKWcNHJGx5b#8{t(e>jf!Ji=-UpV62QsRenU4|3APvuKC{v zXC(en&d2bZ<=z20nf=J^5gSnr@l3o{Wdg4QjUiQ+C2a-FX~MI6N2CkCA{IDs=niaB z_nkls$l}{GJ>f59o+-XsI}x!^B$hOIk?+3n-GM^Vj=Th3jNX;!T!zf+wzu2~|M<9k zv^7=k%7vCsf5J}4ekdE! z{+)o?g_y!3uec5Y9<`rUWBH*cd{Sg8*Bt3P`L55q;4{Qn5!g>-FB8*OJPh7-NqKk= zvJ-NIc5=Suno==Q&850YYHo6F#qi3?Kq=eV|CSd3_thbQuL0z7)``qhtk1Pr-^+c) zZ!L?ND^VWQHe0?kkF)Jr_r=&Tl9jx?{XyUv?~vaPUFW38akh=V1MAwGyzbi^&7D7A`iOJe671<-{R)lm|T_To`N^Fg0hbV&YkO< za98p6ChisTZ3)ojAI|im6YfusnUA{ivHtVSq!`TMtbEkvoN@afD;-HK{`~#qlOWe` zlpW$U@3ys%Zd#-Hhi@4h!!wf*gXhcCPNY*WY0qt1qv6OdZFs(?iVD`2Gj-Rgg?*E!2s6H0U?a;yOwnDWH`XYJvSB}5D&+wW zWpq2^3Sr&=zhm&-s=wR5%{K~k-QrFzNk3*vyOF(F^vx^96VonqCl^QUf0PN%olRxR zcjrM__aoy+-?UX2gqL`**C{#f4_tPX>3C20gu44@o}m8@%it{pk@fz-w4(|a(=PLb zzgWSs2`=|)TsHsvXs+#tW&~$*GG8(6#nWd)$qt~ z!lV280m@&%^1v+S<;Tn`PAG|?xEQ<2S2RA+Q2&)q391G0kam9t&<(;7Y+nGe;W zE+2UUII-2jhbpjK(OT|o7EfF52_N6!MWdDNj~>E&nICwvSN`vjFSuRYZk|B??#1l^ zww0>k{uudlSbuT5D>l6g=`#Ms?Y5Zo=@%n?tSX-pn?4TdGXBNw;&Z6mpFSDs7FAyS z{37YNgZa7e{o?kx*mkOrE^=ydyCpXLF{Hn!+A#t>@K4`}bQ%BRc8P%(X@3{e>vVZr zO!@bbzEGz#j$WkvNu=Mwbfv$BZmP?R&c#&t2=S@<^*|EA+# zIsWl3nApOpkM#JrcQ?SV6EDKwcAgi7SjL?^`#Fubn>^v8rA+7g%5om_olE&!>R5g> zQ0Kj&epfxNfA3JV6geq+ENHQbW&Q&mf@qqb=z7;59wNFB)-*fxrqAt1ve#5(7`6&4 z?q%LMp{c4MJGE+3JYpCo*wB_8{|eD0GxV76${ay^52^MS zJWLqy+plQ_S2YU0s*FH4GPY>SpnImmYvIZS&D&w5x zyvLlajOa4B({$X6igkKT#ysDu;X(YUwkw#2J$SFZNJVCRoWq5CJ&2cBF=_83k{`P# z!8*lt$KI&qyuaZ}y?)%W$rC>Eet&JUjf%`H>~Hq<-rsz4TOXh;!J)Ektq^+kqI)kJ zRtLM~M|9b~`>Wbw$U*GEZ=!zaO29tKW-z8h)u=){&eeVW=!w!8V>okP+*bT3_PX?A ze$?e3#rxaxMv?4eYw!lLAFm&??Y3f}d8<^`|51n4x+dEad#$~gI^K*FO{aw&*spj( z%g1|Br#HTDIBLfko3Fj~Z)&f2<5A^j6|7)c-~)2)2kY^bAK?$K!S+&38}G$OeZ{|M zy}8f+lQ1bhm@(X)Jfod@swe!}a4#B(%K1tE={-Bh=vT290pCemEqA&g>z625kN50C zf0)DeqI&Bx=&hHSXY%Lb-vsKp&|Mufu5?z;Ds;}6af?5{RpC|iuo-Kd&5&cS7#WoZ za9;>MgYphA{Zqn+g>NhO!vA^T{wt}=RAs&z@i{pvygO;YP~x$wAd)_fc85Wp(5D9o zQ{Os^GG(1k=Q#t~7g>vWImTanG?n)R5o0Nr^=K1@Py7J*;>=fiqS@n1ZdJI0{@*}d zC$fKW*wk4VaHP+>UbVqi+MsEVx4i#S%CUx+As+;XzeY z{ZHDulc&Rw^y@W&^a54mh_hn!sq+dj@_7xcuF zIuYXj7VBXe^Px{a(@7bHemtQAyI2=-&Bt{8%CPcH2ILuC^ndH_PmcZyx~zKpjps?Q zdtT@XRm+&j6HJr6OepX?+l3FSioFN_EH&1;ZA@2VRX#{4KbCtT#30?32U(JRKkNoq zMeGJ~pJE>jyFmhN5jYc1JVCvL^MEp>B6Gn{U|Xv7wg2>?h%5md0~{Bb-SkIuxS)iz z83~*l_GZ3ofpz-E+kC!14;aew{?=QL=0a}5&+J3Y%Q5z5V*v5CbotYeU4Orxu(o|< zq>(4&PSxvI`8sw%u7!PHGf&Yt@kATaa$0fDBm3@Y*ZPh?ziWVRN_fSaEXVO^xdOk+ znZs}%egVfbepw>)PctmvYZCX+Cc}F6BQuA{8WH}9(B3)TsLe)0*}(CV`*443hLN&r zhIzshdQapl{HSZ|mzbxBn^oBy`>NEH*=C31D!Gp}wCXCkkF`g5vl(wdAm)>5f0?Y& z0(Ud`E_nNA=-;C9f{zn+T_+mrN#}{Ib{h%x*w?_$N}Ixr7d+usp*!|j>bx5~4<3Eb z6Rv%cZE3of36Hm;zg^(*2K)^e7rNtrukgJAx(axcybpRst(cK_;49*BaX2*NNALXj_t{``~ zcC@7vfAzZRjqtwk`CdbQ`CH_-`lueIGi}+#k=+PR*7AaOgy{ehRq=?%=d);b}zpld(DXR-*=I(=KDrMmy-enyciK_n0PR}caTiLvi0f;KDK|e-PU;Ob zrt^2M%cb-k*o|Y;9WI=uA`Lv~UV7bQKCW-*e2#k zUFiVphQ{jYs>?a3$JL_JlutpX~(>8mWEqxBC8!_flK%UTRpyGmg-jZ>oac zUZJ<1Q|^QWFhQiWifIxUCE=sA!#f0mf%wO8FI`XT(weSpzxLW!Pe5BtlZACt1Eza&# z8`T@x2J$%{#P`M?{NbN^|9JJ9zF~wu>|D6Yk`~nfS_2%>9PKE1<`Y=N-PBW%N^S+9Yv(;9tP_3UtTO;cu`xYOe*|$;-A=a;h@F6cC zaH%{Mt3CX|aOT0!I)HePrm`z8|5Tr+ik+Tqb)hc)VLY>gXcKbv{lijkB;O#Ueqb>= zbJYB;m=r&Teh-~a{6<<!_Wgg_Z5;cy#r*V{MnN#q+1mdkPjvzPMaDJ%ECx4)DYC-wrgP`d?%u$2B zbbt%I_K!E&cP;F2q@m~?8xZ4@HhAy~Wh&N-$U9UE$dg$AeR8)R^?fb#pv(P3WB_6= z1k`?Y<|4MKagaejFX8~@)c7}`Tn*ZgyZSRGu`K484?0RA*Ax7mUXzRZqSqSR0o%EN ztDd@?(ucb(8t3Z)Pd(s)pN^5l`~M1__d3}oV5w!h*gv_(2aI&i8NNA;Jw4aoRBgwc zsN2c`O!c6}i>w=go%<05`+Fh-(N{gn)ylh+p*tRCS;_~%%soau{<`qb4Vb?Jm>U4| z8U=ILO}d{fz}$d#*64eX8i(&icwi0cd(l<{{?3=Wp`7FFhkOUUZ2{mBp7(@u-etPz z&kfM^TD%8y0?;d9mx%H|(w*zQ$DNg4+_9N+xhHhT1*`+P#BqVIb5_c4@-+B;{IZZA zvfPXFy!q8X`NWsgfa`PB=p(>0!RZHBHrHmjm~Icqo@jbUwNdCEe9;i*wVB3rteJ(v zVqSR0x7EC7T#N>^ zW38F3&Nk`ejXCYcI%tJYX`#IV^Ce($xRc`(rS4rx+pv~oVC+`eGvn+GHe12Bo-dHY z@62LZHD#^y4r@;uK@#@3EB(6Sf0`y8CDynUGPLB?6Z_w@UQo@=RKnx|`= z$R$tLH9{+tNsQJH&N+tiNm4%i_68{*`s@4h*L-{}|5hJVeQMv=+Gt(Wvj^u6M&G3@ z2bq=unTB-{a}~apT(|I!!+Ff(Ty(CVdyOaL5E{nNZf0O->{#Z`L3;wHClsh-T`jA$ zzvf?$GmSE-_gZ3fctYQOoq60NfJU^RgI89*#Wc3TxdPbI6g}+~QdaS%4|@RXaw&g} z^a;5ZC@R@Cl)jZE!IX2nK@0qNXd4fohw&RM-+^_)6T0ROw$pYs)tgMhSisk@t=s z;|Zm|+%OT%@)Mm~?&Ry~`!I*Gm3{NDCwp=f@3-#rKP|D%0r%3Yy{JGQ;(7;}y$`fp z0lH9M%mVIGu!r2I#(1spVWgaiM)#7V) zdy`leZEbNUe@EcK_dRImx!egjpf8wj&yB-h>wH(+Q6r9H85@pUh`g6$~W%)8#dIGQf)>-5|FzDRm;h~+ESjnFb{-W-2L zH1AMMTR)b#9@0ngUZzK2Wt@@mtK#;PhIz1$fbLfO&Eg&>+XncG=fO{6^NXx=#nZsY zBd|L+AU%L_55Dkqru!BudAIb^2QlyF{n9S;?yoYI+)LoE=rT=5=3Rr9KCn*iJ&=an zqb$vQ(3kDP+nBCI)>t5XZW0|my2s*Iv*779@Xx`0(O-m?z()#WPjGJmJGRw5c!+~- z(B|%}G_2Fi0eSAhgGG)Y-hpN5dl#k#%E{NK+Upma-S`f>!>I%HKKYJw@KbNqSD4*x z;E_u?~iZ;S~Z(=^@ zhRp4QkJaloF-MF5(o+G$2z-~A=fo{|BN1|ebvx+bgpV_w>mfLRyKEFd55vpCt1oVj=2zo94*9pM{L)V5SDGWztMm5sl(`h=sI&JK zqTBn^mp*_K0CgN z|Eev}%Z$nTJV&1sb4^P44RDV&=`O^p>s%mhtNvQhUv5ABeFVDH3$BXSq5r#amNr}F zlcaq@+}sWQefFGa-FV{TZs^amg`cDkIfuI&@dsuLekBcOz9@Gq(k_kGg*-!=7iq#z zfU((7pJ*%uce3*Z=!;{)D~K`Bv|HP~#Gc4IXc)KEiCXy17%%*88IuP*zG|M~J4p7H zyMM%WlfUtO5n%zpSkZ3<_okk3%k^ILi8O=f<&oG_p3oTK;rux6Tkrnc4?hHd&X}LZ z{ejal-vggeuQToLqvTQ8($dgYn{#K7K1pqkg5?r}fNP!u>!j_=?OE%c^GI=A#V|I;a)vFYnG+I1kaLj8V=Rj9Z@LU+%9wp+##6FZM8| zobTg@bNy>&+(Q7q`#5&go!>Lg`O8?&6MlUb>v8{sa!sDl>mq+*%E3u|oX2!JfjwihPYxV;ja!gybx}O64ySO)DIi>&I{d;@y z>O$CCg}0#pojH5olz?@qo*#=y2{bJDt)3v)hD{ZqsYR`xFJZ+0(a zJ@zlLl%?+Q7nsI6;FC0IgZ>U&Q^W5cXP(k;N6VZhouQMa!*`!Lbb^16<{2wW;*`OPNmGqn|9NSMECQ)_bdWcrFB6LzEW0#|Rv!sk)>n{ld+#6r^)AoxSzhg}GA-E_~z7gF+A`@?iEyUbyv$rfig)^2m`;p-6 zs(jo{jiaBW5$|w8|G+$QSey3vFSB!A^)r{`T>{yE`(x&k%=xEi3&>0)91%NJL=MPY z-#H&XG^=MiD@!kR&M1X{Mroy*^HCc!WJ&2d#C;2~4VgP;Xq^A3>q_!X{ZRwFVoDr$ zybFJTWypi1kI;;M2BC}+Gj8RX?{z} z!&U*G6`7Zj`Pc(jvug8K;4z++g#uTz4(0>y_!|4%$ya}fpZrAM4UOC*=}`8r8ocex z_NRDJhIZTUVUh4>`Cfls{2l999)PujIL!fyk%6zR__5j^N~D$%VN|C>4j z=>0p;8Tw=MK(-Bd;4gZ$4`2C--tz#REl58r1}tOXf%bmJ_o4j*XEWA}bnq?iEGn4O zpYWm~p+V`cq03Le<^em)u`Nd0yv!8KoXm_Q#_plKLM-Mo#00(odQcf~&3pHK&eclk zlLc5`nZ-7-#_$ZQwmk)V=Yk0pxT{Th2>ZK5#)~~!VBG=3Ph3g+Xp?<(>;!H$X5o#W zeQ}Tv0gqX1VS9c0O{I)i=Xv*BMjel1(Cq-uV+#RenS#-jyDz%zT$G&&+9D2{=>TVyhdZJ7Y(br(R)Fl zK?3iviZ0*@rOUbydzM8Rkq5n1WW$Vmo^(3y`Mopkhl8_E|L}I&wzJ<`JyXkv6nDJZ zLt#t-;cvA*CFMcp)mv;!csBs?5m4tS@|9wQDHHbN9}abBcFb z48F%l8HIb0_X;e*d)`*C8RN)v!f(h&EPPi7ev6IN^ghA%SPnGJ()GTu`x9{X{s=1mOxSQE_olZqzbrt z#*)A`)c9w|jz5L*67Bv(EBGma`6!#1)394$;oDleZ_yo8pEz?=<>EPRm4D>8jP04< zXx#isLtM`+v>spk$D=%-LgiYoYYhC*?{wxME*l2i0rk$f>@Km1#{fV=iWgWwVYFaLPEO* z3t*=e_~{dYe=huU%N`MRg%)xyuwXUX7XF6~(+xiz7i`^(xh3{V@)r8p?g{^0)-UdD zAxkWHS1y6@#kA9&kNNAYycag+mxL#dy@L4cA+;xr%+vA9x;NF_DR&nPeU3F#t!GPe zIga)_Vdu)jexw<=X|Oo#5j!gOX}#^J`yXcc2+u-Svr7DOIqUlS70grZmdbZE56l4e z@OZz%@{q_h!0QRwE@0V+j>G*;-p4UAtr9YA#=Q?Z9rsRkrrn#7eOhGNe9E-(%g$^k z4$xK-`$o)NkqM+LVj2q$2p`XlAe)MS+d_TT+a1B(QrSb^i*upBRS=Gd9ZT)))%)3a zJM#r4k4D_fGFmSR;C(u*=fi85Mm{Q9+Wa@_zU)8$pv}1Zpm9#|Zi}9ShZ8lfGAIXx zKf`}ZWL=RNz%SzS3SVMAP39g#*%F~gQ%A?%0OQ2Exm|dQeW2|@J@e?(jXibDEfKw5 z)%oK|_JeliE?s0D!tZp@Bi^;FvrOo*jP$s;j%CS9iY7QKjl;jE413OQPC?b)aA-KFT#X(N6kz6G$KSUllB3hoqb7SCfJ5uOBXCdbg`L)Mkp z_Vfz~zacn@tPLouW2b-Iu4pn`=7pw7kb0vhqv5~u{2`e#Nd4)#M-O{So}nNqH^ZKAEtQo9+3^fAKw$v zKh+-R*0;PU%soy-Cs{js^9jTmAI@~@j)aSS-5iMCUyt6ObtjL09rllf#5a6kyvJTM z=3_5aw!G1>){QbA$Go8)U_%T|17}x!t0r~rC}TDD)Dn*j^@#@qae4==9gTaEd9c0b z8mY8>ICoYU)jJDP>mb`2b|OE|h^xyt9)lmGW$kR^u`P(vi!=*(#oJbgJ^i5W@q`)r zYrr0eZ?nT^k?mm)4{>DT9&9Fb%FI!#+Ma-ZR5$W%$j}um4_FvmzsIK_9=s_2^O=V^ zHfJ;T|D%jR9qMyDSW}+EZ(q&zu!$J(iNe^YS`%zMGh|totFlhMX833$750`hj)Ds4 zvIVIPM-lsOpxuG`3xJzdh(GWh=(hpX>0tS)f%2wkA>RYiv|@W<#O@ZEiLtMa;L~BV z81TjLKK3i)A@cnT$irH+ko>3S!L74oY_M%p?z)pRXJV-QM9-(}n1`AdaP~e;_yF|& z#`_2UcpHM+89D~S+KIrM$@}Vn$-;XMkQo;82+s3j${3d3WmLQe%9j{^62ORaz?4G0 zMZ>i))}DrUd?eN|^!)(qV=aPQ|Kl!vjjlU_H=L{5gFl9!|L+Gf?`-`VzMkEuBIB+9 zCjO@EZ_nJ^;m8~uVC=~L;MH{6?oPh^d9edRE;@H^-b1@+EbN}p%`tFVCbR9j%-?dY z5}v}m_z-+fI}7b&#H9hf-t>enkTE!2Mx4{}S}S=?$yB5J*sqMjV|bIPYUT&2bvQe% z$UHI%d$nZP=T6h+$QX=}=dd?ejOz9KH;);{J)9*0d{n01c{DmQE|xeDC%VQXMi2Hr zz<*lxrC3`6oEJtC&QK9!5x!R=d1n8IsQ^oG4S2<_zwRTWY+c>v6R@W(fWE?5p;=X< z-J8i1C^KF0V%qhQdW7GFcTOnU|ZHFWE&{i^WwyyoR`i+EJ zs+iXgZS9`!FTZBqgMVSHOVcWL2Qz&2gRdEgeID*mFrEJb%Mc!Bw{=t|IfC*3wMwp)ZvffLfPO=dgH36muo^92WN73it*k{uR90VlV#j z-(hbNyl~F)?43TVSXYNScdo|yLx^wIlSkz37>vINYhHv`)Oj4vjnLMIp755DUH}bx zF+OcQ;3Kk9Ip3kNB`DRTWrMpi^Ey)J8tEAg!(u?{OuELi+$0T ziMC$#W^p(D1j>-bPt1NdUCDopfjrAGctZapd~)n*ygx%d#_WDEGMB0{+sGS)Wx=$) zNqLnAlWgEo?umVt&41lrb2;t@wK`^Vqi< zd%6#4LjWtw;0<5%Q`&F&mw@9Vuoo34JF zliM?~M{9*n47k3fd^-FuN-(BKz0{a`rta^o`o4Q^h%YKl$U}cQ!2Jn~Hw$fKIb86e zpX9zK7k~9T)bQJ+U%jju;-eYU{@Lx8-T=HA>1-Qq($BH5>>jflX=$V}(rA~=ij0Z< zA-!AmnIiWF<`tyi&Sc8Ds!y52eEU&wf%*aZ-ee5q9eA@1_}(tq*8lazQ^Y5)3USx% z!LlLll|mHxW1?i{QIcb`#DepnL8*Kv;m| zRM=6lFEiq)Te}B+QNwiVs=!Z*$hq*e$C-wEidMpiIx}hCo0=KNIgc}N!nrXE<0UP( zP=*1{^l!WOOqp5z&;l|13wWGzR3;EOmFw?+2S3wM)7s3UYGyw3IajrN4j-NrWPvoI!` z@Wfd)TuOEejB$P5W2k6GoSg$ocHei0CsbL-HtMc5_Ng`77!&v-U{dx0oIx}_%zW-A z(Vvp@zz1+{9J>~@h2jSqWwDOZA#4N4STcL#Vm4Xk+ca< zKBUTrwj6uOD5P)BF>`ZA;H94Z>eA4eRDY;r^wQ7%%jcdhmbWT-0C4ay>L&^anMsUruCttekQ!>w-)c9|Y*SLqp70 zV@XcQiQXmNUFZotlF2;CNm<`n2Wvdu_ZqsF>8j3P+)o+$B7eJ$!5x6lQgQX6xT;H& zwq`zV4218^`i%38L7>%+F;_?U^1ipBGX`Lj*L#xtmcj00WLP23YAo}~Uk@%e;vf74 z-nXtyDz9FjR8CqycpCRY&7|^d%zLcUDfpd;?*`N4E+Ee}&97d-8q7UK8F3-L2jW+7 zzlvWT{`Dln3f|ujJulLKQ)EAZe%c~^vp?|E>b50DVI&RyEaSnOiHgU}R+PcJ24b>6|>wSSbmt7mxcOU`!h z`lrwNH0~C`cWT;;1M=*_`-`hH#YO|3d{*SL<7)GCb?#q%nOqC%5L4~~nVV@>0@rw} zbWC+xQu&xP$ln^}??gXLw4-9aho8-5zu>DkDz7?U`O5_F|MOD#K_LFD$PD(aWq+(M zVC;Jz)IlM~;_p&&?EZHUAJVm(X`+>8(jytL$U1tQpI9IGjJPx@PQ^cyk1M`=oOJ!2MjmQnJTl zEzYQQK~B}PPo&vooRBRVum2*AJ>h2#bG(vw*E*l_pZU?3Vyg__Aaf;>r~Gq%^bei) zM5JCdXpc1*cjtbbqVn7#`&o~7C3M~hofn<^a<-)E>HEX(sO+ab&>k$`j(Zr7NBa?9 zmh#t;Cj2yJ9M|f&PXkN97oieIq#WwK?JGaOg#4iHW#?SYJX!A`tFd0h@Q0-V=hrdu z8-zbrDVmnU-=)+R1?iY=6P?5891nY%(9<%e4mv&Q*|EV`0~*#=#b4(G zUF&f^{wDWmu<5VSw9KikgUtqcV-f_G-8iEQfEEd;SC9I&h}%7d@S+ay>D1d#VqTrZ z`z7#ahRx18#%eLD66Jn&5VBK^?54@YMsqwNKlPd+uD z;<#Gn1q7*IJ7`#In#`?MABu+r}@seI|Sq;l9_tPZRL(B-tRPTVl! zL7jnokxQHU(iZp{b_TZ0RQ+d~wM)S}=cx(pehOQ@aRpyNNVlDbE?mVnzc~_mW1?ipkX52lM z`EvVbtV8>=Cw%=#-6r#S=7o89``3~`SMn{)$9Rmq3D8Y1!TIIsMMa)a(hE$_)a;SA^FzpJA_v zG4RAlyN&i4hQxWN9gcE>bY1$hf>ip= zdcxOikg@n=Kd9t8((S=T*vnJ4|4P;}=&1Cyt#ufAmL)$mY)gJ(Tmn8p{sfWzfe#>S zY9#K4McMWzyvcT>XAYrjzLtLeyEU;D`vlxqrj2LO9OxMP(YAim{u-G#JZI>;PGfy~ z$Nmo1%5l~ddn@QO+|G4!X6;={hJnAfLxyd^c_rSFPJ(^t=pvjkY=Ruy0)E@B<=B?m zXN@@j9~_(UH{QR^vDtn^=(-iZ4gW^aH<5A(^0)@?YhU(PneS@M`7&ln!+C|gWhdtx z`P&2+ku%iLHHq>YXT9<^Ug&DUjnaP;W(l4||Ls22XI&v)8Z=%OdCV`H?JZcNFT>g9 zGBcHWnL0m<8!zq8TjwjPgbxh-&B^n3SK{os#FD7aW7@AL{(!SFJabm@dVVLes^{!% z=0<@9{11KxK-JohfacmCG(>vUbWhUqrK zM0nKLa&IC$QCP4K0xUMrd5@f-fo9ro0D9&6OqNA{^14rGFO8G2fah9q1{0k(+8ztK ze|0J%o3uT`jlXf+`vaFei;V{?{GLO36VVf5=!kf;6({Ts*3pWNTN>~-K?mc+pbhwl zCx|YoWbgxjS8_eShA@g;r*7Rq`TRhu&YQ#th zha&vM_}r%8-eYJy?WUBie#EJjx9C5(%I6G3Y({Z%o&~=l;R`2ZGI3hHj(USpj&za# zHMskNHfR$PUtQ7@v3&$yU-5`^!1^!5SQj2={f?!Am%^O6ussz&5b}h7c^n{MeSx$6 z$T(kb+mrE*r~5$?F3h5$HKk%V!)ltJ_PP6!foo9fs4nWyc_oVxQYQI z(*2*=M*9b4P~N2Cyx;JDHf1!|1C&{VH}neI4#0=8U_?58x7kO=;oko_(A?zHCobyZ z$?oK#+i(sth5pdagE$9)ypBBwfgCHtKc(k~u9@mZGyU2BJ)tMsnTK_X{$eij55@_5 z_u?6z@LIv0@KEU5MJxxt?t8CsvZwn4p^c~8e4S;S4Uv7YkxKnY+rf#u?VK<0jm@fV znzVV{)0j)$=1SH<-agosw{Y#m*oGmApBVf1kY4t0)Vsrr0%P*9<~Lhpodu23iw{aH z@$~!#8+?tHf2!wKhO$`?*^T@J^wpZ*@R3=((==Izf04GIazf(e?uMVC`ROEC$GgKP z*^gewx8f^5QMfwvom%+or2r;}7n#b|7mdTfyEuh7cc>?S;CqqB{JpHF`u9X_hlnR% zatsliX42h1A1X%GzSiidBC-zH6Z4xETBAJ2?#jOHRNTKpE)`HZNm+ z+)>Ynv;jYhnZ3(2TmpQ2$bsS$J%eUkJ+@(2zzQyn1k}4k)%$oj@HI~H*kC_~#*f4Yr2V}&-kJm7j<;*Jc~QB4 zyge?s=?8E7Jl+2g`FA$n9>16E^~PIsjmJDxdN$5RU%|2}{c_?AXEvOhAJ?d~5i$5p z6WP!kkImxa=`pXCIivAdr0`gVbJHjuUA^(xyiDP-^7xtdMW43G(~-Fh_+@OI zCv7qFELsO&l;6U@Op(D@qexTw@HzttFE}fgToF4LpetD^TgYqnpwf#l&WEXI;J$AH z?#o7fbpa#h$P)1BW5NeH;8|bs@=t;PzH?>2r~6%z$hB z))rp7Ue+%=`(mFxb8!h`+CFv@%L6{hYQ~*uvoi)`#i(;00gUt)gWmO+%nSUsV+~Ek z*`mE-l$_5jp!_9XF*m&EkLP?@{q%FMC%oz~`^iNgjF(-JDfa}+$XBet678Vs*lTkeFfR(uyXnRw|3mNkIo4lf$-pDv-Vb^YXTsQ)tR zU(iqeIHgyHwyFA-H@>0%r=IZ2_gNpfOc4FB#%S#Wi`KtGrMj&^|7}$cLR$~_1Iqvn zOQx>x`iA;hsITbXn@5vW{qA;M-#Dv&u<~~DD(Vkm|NZdi=brAj-(?(A;#=0c_46RvM$JAH68P}i3|3ixj3H`IR$^+h)JsXs{97ym%i7r)QgHtfO5 zJ>^7y=1_r)5rIE2YN(LFy=eJ3&xDZeQ*;isJt z(E%cJGFJB4gJ>(rJDT4Yo+r$dg;-m1h*ypuKD-=1_jur^M|OJAVL$xj_jK=D!93z4 zwtw^?drkBqV~*(~jqS4^-N$y&YrWKmeC(-r#-ea(J`^cWISH6xYd04t`o=$luKDRp z-|e!->lo1`!BzwB>9ojt@UrqRQ}nG$jjUT|)A?ap$IhnnSkZaDoz4#nFK9Xk6rJNv z0cKf??E$R)q;s$M&7BkbrE#2E*ZiBnpFbT1Jv`0k4W=;CSvU{T=Sns3Y?~BgbHT z;sAYiodN89W>oEkTu+%6OvxrZ;IVyd&sS^M{B@TIOq~|^c$r;?B5<|iH(^6N3#6}~ z!tZz@?71=@9(l~?h{ypOYz-oRo|<5 z7W$6P*0O`azeXz=b z=j3SzeYTv21c9ge;3cW{C#~ZYy}G7qJX0Qa_24Xke#3>ZrJ438Q-<}}($t+Y@aXVw zsMmmcY0f88vcThk%Jr%|ePeKrHSiniybt@}r_PsC#&KTA9TLt}-1Wixk?;0jrv~p( zgtibq=fRXT!pFEdNevQxgHY!GE9qiis;E<*NPs>*ipzDNwd zYF~{L_{v~V+UtLdMD$G-V#&9DEJXwx2 z2|L?%&iF0%w>2HKtvXx5^P%)cFB%Oz33@jAY&>dEl6&AD zb<&GQ|2w!lUhg07PR4hNz@5nUdf;Z%Z}~*w#vDZKlCG72eSCg{k&q8x*L(wUPoS6N z&o#R$cP6#u2h8sLSIw?shY?qqhO?XQ<0Dbc$ZIvb=e}peJ1dvj@?Az8`z_v?WfZUX zBkugN+bbQg3#E-S=AOoJ?t4)?5@XUeqff8iZ@E385nr>tj%u8{!qx%#6LaP(eyLrg zGwhgnyKp)7y>aX9!DZv@#mf*E;dYVj)HS-ULrjil$P(C|x}G+YA6ssu)+;@50Ah6u zPqgP7YeX->`5en(`~&J6jjS%bv)wh&$XV5nH^jp$st8X^SxZdWv@?5+VO@V7X>U@9|dS43mVpiA}%VTvv_@f?| zHWlxu19s|rUFz=a@&YA8gKx47)~M+?CoV7An{YdP=8j;lxFh9R&K_J|;Ry%D{sGxv zK4zfoXl5@*+`CFiv$v``QMe_pz1b`> z0I&mww}BhJ8KQjs2Y2?w+0(j{@7Km9XbXJVc`o=;?g@z99C2|4PsA5+g+A&C1Mo?? zFeZRI7J?hv?t{U>3OCDNU>WHTvJ!0?D3^>m#y5Mcl{jNVU(l7hWjwkqCF9Yax>Fm> z0nA%t>)rk90PYH=SbayRmv>uY;OJZTUerY_NHt#3BWujov-b?4R)EP*^v(nINA}?! z+!2ifNx2w{gWkN-RpCVgdJH*z@Nuug$1Gcdzhe((_Ztq7Y(89 zUN$OKekji!_M!f;v+J+OMqYDFxi$iXPt?xq9{g{{yBy24>)2~}dqI4I)x9O!dvNz* zOdak6A^&6A*6@3ne;sUJcZ%;%7wK#dt|;f);D7%r_)QIe*NYB#K3@3@@2yy@l^aX& zUK^NeS4qx?f4PgL2_B+#{ zTcn9it3luU@Zmn@FrEiNj~oR1)G!zB88~cWKkGs}gBY7j-EVOINB2pM`vv{r zKAE^Dz7>9l9VXoU;1~B&nrAW3Dd#&v)A_*ZCOO+t`q3R4->Wsgc`v0|&C##wQI((Q znOjlm<$00y5BzTJnTspFjI4v;Ik)ewk1xSJn`YnAB%A*=t)q1xzSN7xScf_f?C{O@ z;7`tt$hk7y-y?4_AL$;&M^!Jf4)sCsZ#sO+5F7St+Xp9Q?;4kNar{=KF*!hmK7Oev|zz079=f`s2 zBy>T@i$*}JR6S2Dm?x=#bN;%pk(LvP<9R#ISI+yJ;)%|7C6{sEdL-;G4LnPU6B`oB z>|+_;^#P63uqVL$rwliS;e1Q{*BP_uSUcv8dC@A^_%Bv4TzG&m^uq_Zd-k%(dEiIO z-;=vhl%J^lbq_vyFV|^jGw~8WPt#?(^OfjWztUqp_x~g9ZQ!FUuKn?6Hwjq?NN8h= zE$SjccVo1%qVBQ@SzgplR=vT(1+B=YKrgg-FSOY1HoIh_5tI7Tpi-d%1|dqUFVO1$ z>b;;rAqHQ1u~%Dc6=Q{xpx(yTmms_Q{J!5a^K71k=xy(RKOe|5Z)eV&IWu$S%$YM0 z6r}!J-#;26d)%5UFfw&QlW=O6tnV5}8Dr8sPJcNN8gR`hw%<{BgaKY@*VR8^(E7u!#{q{fPH@}c*_6% z7XssNJzw|ndX6dBR-N#Bn_daj-FoTqiAEG*bLywJUgP(6)=d4~tv{Xm!=^{4UcImz zX|&aX55Ud{zO6eAZt%41Yjec1__>9kqwR+d?2#}{vC7daib)U8t zvWr~+D-Pejd^ii|Yil403yjIKsj0Y`L{>cgEpI3d(H2BAK@h-SErj%XWEq_(x z-VwQ3 zjm2A^jIRrC!Ce%Q^ZHjQ z+X>L^3-%Dr(0w|d_cb=<`xZ47h11m{_nh&h(RAz{vE4@9O~v&eJzNz3X!~RI{R-ZS z)*mva+`Z7qy{nSCQ^SM%tb8v8j z(in?z+!ARt#`c>=694p``jgXeAAs_!d$1hdgndZ#W6}$2^qb(1mgs|9-LR=@iFS@~ zm!1vRGbfj+Lzz9g%y#fiGWyUtScCq>4ZK5hj=C>CVZ!Hky%CBZJ(s@f=Oa(kded;? zI}$d>%bbZfFr36i-wzp~-V=ETKJuf&Q}wU4Kj!ODyhVQ%ihsT);}f`+(RV3))h-9> z{K?(!A?NG7Lr0&8yoW*iH@UYsXW7oHD!hfTE14AK`Po&_oyQk_Wd;01y5}tFI3>Pl z631)Hw+0qT;?PIqWAg%M?334}O(LF7gI|j*ltQYX!*Pybpn_iFBhEs=R z99s9Afa~}QuQ9>5EQR|=sq8x-gfkTOP?%chAJec`f<8SbD@%C49$n%!oa`&m@PKZw z{E@Sog7@ZfO~6_Z)px{CfKKqO(>yu;%(>CT=`;D(*1`UFjP7veM`PGt+z+e4*-D_UEC32ZgUF2Z8>x*w^(6yl8HKf{ZnHhYum!}-u3ZiUs;jxI_{yJPjB_( z@bgeOHQ^mE_c(A@;0fqm7sr1Zf~{)mY1_F?V|?+~X>(YJu`6>8^(Sb%=-fyXv~6QJ zvr70Fczy<1Nc?_doSw$8&QJNkH^lD~?T*OTN!p@^3VIJC>^we+`|j)*%*sBWPos%4 zFaP~CIw*7fP<;L|8l6~I-r9rxxdFKd=SW!^eRTu-YZw~s4QGBR{XdvSCqDkiXw=gI z8aY0XMr>2B_iz&d-AzKi zRRSaRhQO$BV(slVoPO6&C(0PR6n;dYE2DiY_-``a7^Pe?rMx58$-I$Hhk?B3po|0W zk#%xU$Ncg(ih1H=<_Axff|rypvVxDYVRMdAW9HBIupaV0iutx#dN*ySasTKE`0L)-44>xoWe(Ua-+vyo#~cmZrV}^v zm8$RcO$-yB9sV$|YY7*0khf$s&OQy}8p0R`=OWFYq4;XxEc}VHzx^lAWwh;2c>8LY zXYhRXX|IC&@GjEW#;4+U!Ve$fHa@*a=DO7HPbEI(&tYtIJ`Vqt!mkeSVEcgW;qipx z(+uB;C?r2n=i$S+!Py7SE53Y(z)*bnmf|J6q4fY@wmn`WWjMyRYsJU^b}3;hK2E3b$=^xqh_!yyzm0pvZX=g-e&wI=22#o6^DP<&%*V^v zZrp9<`XKhv)?4O~n|M#QoOPGO&H%Vv3jeO@2r46v(4&`8&xLYouPN)mZC8gg|Juep zoYO-Ehl}Rz%{#+~_t54v8s{tgr^{T8vKX)Co6f{&WKSD7ZXT5b4HR<%jmdOO}axgo)H z)gO70OYp&gj2in}_4}heLLIQ5wHiGq=p`M@vcYvi$>`p1_lJ~pxD?Knbsy_JtIRdl}ndhnc^>jY>rU(Q<= zHIEOcP2mfjXEo~wp7Krr`f(}um_5yCbLO-g2v6>5C6yfkWLepd=(76W`0~@4PvV5; zchrY7%cU4|FXf*+U^YPas^Lpmg;}JvndVZ|rg|qPf3Cfr~n4dD3E4pR9 zzeS%DXTJ0S^HS%hCW8YzWK^5tLCdozeRC|Bh;TMvoVHk0j-5{MpykSjiLF95M@e74|bYul$pF0=O(8 zoT;DQ@g>Uh)WriIMJMyGV#kJis!x>qB>s{2Fs1`oJAms6_7Tq9@!rADYUp@*EH?VuH2fmA3^{}T#{bl$r)ZfK`r}QhCTxq`zp5KBO*lmp0 zFn591O*O}LcCdZn%<0lsq*plgUs8rJaIcbgeQQD(}E4S+ts*lbMqdlnkQ1@2t&2wgy=EDnJ;@8r*Xd=b{b0|cVgb? zv)<(!EqVZ?1LhMLn~~d-CRL>Xl_IgSoM+cN*%TEH-sd z33Sg!!ryy7_yRP2<6LDTUfx1oNXBLR>9S*Ktc*WtY0C3#XW@3Ylk)=zzi`9FK815x!EMe&;BBHo@P~s4@+WWei4Edl8|enT zf!!NHhEUFT+xb`HJ3({^hD+L`zQ1$<)0MpOhh6yOFKc&?y7&%Ut^HhJ$ofQ??x2dZ zbOR2q!+V8CVW;40T#NhWi{FU=a5z0Fiw}7pbKFMs>3I0a=U8=tzPL-H-cbXM&cNP` z?_%P-K+VCB>(Qo5b`loyLN~Er(T@8Dgy)$%uc|b0&YRjVJU3_YmEWsB44+?EmjmX* z9gATv8fh#X2VFjFHAQA{0Ea0WSLhwqJ_}qgB7DdxL+$RdCbXrQ23^mJo&~3>cVonNlbMC!sv|Yy!JmrqQ*biq$kBk6_ zmOVwD@U`7_nw1CVui()FnFFZX*ZJZ~=7n4~4f{tW$IJe8K=&ej{s`faw*f=u1o9nt z!LHK|;{4fW7jst)`gStjL_Wv2=&mK<^o8q)AI89WphZaVG1C)?3&Pi7U6L`X`J#n< zK|TrPT(pVhIB!?tTxrq9!aD+yPfhTF8;knthp7u~$;CM6 z4i&sUp!df4hSJ%-F5`gniG6Pp&V0$ix;2l8Zb8lGg*Px>t)X{aJFw2VvU3yWge&TH z8ScRDvO6FouEUFBRK@2V~6~xwCOq^J?!Y-6#0T@(9ZZX~xzN`+H!|Hy1FpCuSJ*9l zXc`No>>hPSr14yeu`(js`O#V7%tFBlxK0pUp`Qb;tC0@f^g=Jop1fysz=pe3+8Eea zb{FxXEhy_18x2|4OqoaYIT3U&po4<_9Okt*-$NVgI$!vX=WHVvYsh$8`iD}tX)fjY zWUu$`WVG|l;<`gC7uOxS`zGe|Wg!0n7RDgYC7Gwm*LXMY8IRz&)Nvf6^d-Uc_4r<| zzgzL$>W5x0zW|x!y0dLOa+dt1Xc>%2Lj8Ogp(fcL&g((d-*3 z1KrC&zc}8q%0PxbV99vWR-7*a=jBS)`&jA)FGB9m+@t46yeoM34}6XDP6H3)UYK(& zVWI4CWk)yf60zNeyrs^v93N-XX-kGO@>V?kLuMQcmc!L1C+@qe^!IWk-!`?S=Xog_3rJ_)!+g20r3lGuKOUHj{M*$+)c@|^MA^>xoie$ z#<>K%lO)|lcFLq>EK+{5&KU>z_5#U=$|C4T5r9;iFt26T@|>gA&DZq$rB0r8%eY)OMwgW}fo1D%S^rbg)M{JI`{f|s+JQea*sQe~&S;y{P=E8c2~9H@ z>GSwf*}pzP_;n8{8tFRf*q(6a+E+DxL++I4%n7G9ZeSYb1^V5y%^UYT4caL?nC@(Q z|EO@jwt;1kUKsaw2V-Z$o@r3ob>~=R#g~|BZd2%|4btxv#FgQ}xw?chDB0C4F6~vOiy@%QjnO z>u%Zpzw}jJaByFx75}TgYO(c|#_#8UzuvOWCe)#!Ay6kSN?BIU5mo~BafoIC_M&5kk4cnS5Z+XZ%mpLwL;K}`P zuNV8|1J?Xtt>JukL-A}%&ArN=!Pd@uzm0P1hv3pExEv~*4qV0%U+5$(`5-Ae09pI` z?c?7L^FhFzmBoeUO~@Z@YriSlHs4VAoRGP1n0CB|vaTWc$oX_QGf()ReWLnf{nv%2 z<_YW>X}A9JE}+Waz`a_1Z;j)dGOE18jQe6dJMv*Iv+w!Bnf}uvD6Hjq%%^>n7iGPx z^%^GIO+6vovIO=`yywe#7wv&>$4*=O#O zy{^Wa^4{P(;k%!VptzQ;8gc*o39X}#%KdQ2bofgjy>ooch0puFg)ej6p==hbq&;-z z1KmE_c_*odVOq~gcj)l`RCD1zl>d#Aft8&&=xz8K*Z)&EGgsDOZ6|>DFI-74*I!{vWW-guX8R`wOFU+H&1o|D}B!U`yS({s-}G=SFF#6h9G&CML{`CMv_x z#Qbkr@@`bt>D0let@fjh_B|JUWZU*!_qAdz-vC^1^)1@83~!9d+(tRrA2i&FYGc*J zYNHZ+*l6t1ibqHX@(Al!{ds&O>*3xIa&o!w9OMo4Zsz*+pabUjIUh35$$jqF9!~vS z@B|;rIi@c60?NVP&KyZ&edIyfhKYaI%*Pw7Iw40rQepJ*4XYJ(%l5fyU42H~Et5Pk zSAHkUEpdS#QLffynqu2k9rPu<-vWO|;7>dkGC(VQ!JHc{ZygI8OtF-Oghju$_2%P@!mjrhj=?!4zxHfzTF|S$zCJ<$y+-9gtAB5P_5%-&L{1-KGr>G zoaJlW@GbOA|@=4@ZIC4OsCWDu;BVS#xk8c5-UyCe(4wbO>Eq-(v~*Y zDmUOaal?%ork`mT2jqWkgWSctaQxY9kh^dm1fB)I43-mf*G7;Lb%_pubBdBnooUAN zoRsn*=Lqf}qm8%M ze@40M>KI{i9~_ug_!@1KLpgJ#UE~ewLdn;1E)G1xnNKR1U(dB}!9`?)4O;*ge0;T+ z>5#d$OWHXvMF-a44ZI78zQ0;<3al=?o^?=05Z&J5KWh7@kKeG>VOt9}(BJz;fy;IM zUyxtw{>69&ceb>jZ3*RkOXd{d%`-#a0G_|lHbk>!{>OM5B2RI=-Cf1{eVM}F5Qg%r!$X*AL|{=10M;0oLujH#gN}p(r)%mR`$9tr0X6-xYVo3S^*vn8F?}{4vrfk z^`i}o2gf@vV0ml2g$ni+BFJCXmV^BjD%iUZWK1K4qJUL3qO_a!1IG;}^@UD#jh zz0yC`GSA8Lv9TkZS}QzW5rhxoiN=cB>iT4?8hfW|*ie;xD|#IBteh2AL;qI|xqOt^ zh7~h^%)8$y`Mo~)uaYv4bFPKly26qVMBc(W8qWOnRU4k!xmDxIxs|fAtO+Vk?YY94 ztJ+u&>9a5|wQ~Ll6+HhDqWH_|Llv!_-|)q>`pfJH3Jp)Id+Bc-WBw``3&YTAX*l(> zG|LZd8`h8g@(y^qUCTMk1BtTRNjDiATa+&u<_l;3 zM|dItd*Hf1V7@q%|AZbNa|Y&~`yN;Hx>CmfVExfOvsKy^y~N{WS~^}1r|y*T&OX7C@wShehBpAx*X>&V6{blGLP z{uOnHK4&lM{TpP~exr6W`~ZOl;Y^>jUGHJl{CqjiwX%3^{14qW8UN_BV(gJ9SK8*x z`})bR(w=UsJsZEKa0=3a}&RYxKq499h-;Mpl85`NizSp30rF`qs2252>%<63MgZ?VSrrj4W< z`)aVPRkbz(Ah^3fgEZwB#=2nBpB)pv*Eof&b(i`y<|S{$CjUU*oD63^u&;kU!C!dl z?hjBe=EJJVtP5j!sBEz+ocgKEcZ1hvtt;DHh&^(smt{paY~cvHsyU{2AJY_$<~HIe zxaoAvsks=BkPF@yo;E0#qvylvw_au4;DN*b>kGSV zJm93wA$iKi1Bd<73ZGLv@YgE`aL)AE;Qkgc6E>Ky4g=H9SA_YJg8AWb8%#Cd2>mx5 zRb%vW;bq7PN$?Q%^1J$gI)B1LbCz{Xt64VVjGASaO@nVPxqqON)&J4&8E3(!FbW&^ zX>&sz(`HYaamMTr{!O}U+U(F})9^M?XMEAj=v6q+x0hqTU=LK7D4p?OqPV&U_Q6S> zVGqsc$s21DzONJel!$3w*?>2Ma@F5>r^i=1!Z5CYO<(k<&vA|{- zv--Jpocx9v(&cTRLAZL*m&LFtY%^1UoxT#V5?)UY&nk%z zVGhN+e36;yow=sPKgzj-?~QX0op4`%OvgRNI9s^G3J(VhzAdBCW}heFy}Af(%3Fzd zgV5H}-W>RJ4>`9>+2B7P<6m34_}C*}I$zMr2RkLwespPxx}&-QFuC@{e>y1|E9tum zcRR-WM_O<%KgP6ZK#7PhdjnRUHm(zy|{Ev+lUNxUu@4>q_=E8 zndkm7nC?=45b{n{_iqTBaF2fuT-E2J4U2ym_Vqf1pDK)`QR^}$P(P9|Dv#;3GUkN| zmVvmEVe;2x^OJ`?4#}TzUsXb0z0upfVu^Pwj2<0jwfHObx`%_~$hE^^e&!{=CYT?v zd|e99^6sP&IqE2k9>sjreaSIN)A>IqZ`tSOJ-PkCEFLONxBqenXwxq=04{z{ZlP++ zRl0sF47y2rUTAQ-70#wdhNSDTl|DK-INb`XbYIWtCZ?;oC>z(I%4EY`Rv7Q{jQ8h{ zF(hnUc8p=)3s%^o;TV^dU(s|-a7b9UsZz=Vp2Beeo(^Z>36AI!O`|a#L*Na;?-XM& zepc9mcghg_P6-Z%cS;sN;-9Fl=?9%unILeO2^x$Vt;<|~Z0I-!Zx{L+&Cim{jO*fy z+%>d0aEpDy*zTcuD<)x0Shl9S^jw+$8a=u8`Noj>X3qAW%$!Eg6x?OR8>D9y(GP9R zg*l)tf3-gdUE-ylviN&J%z-nCFm9(|9xdj*Mcl!2#^0O%Saa2_p2Vf+M%TCB>fx9s z4)LR&A?Y64jC2n-FQGlI3cPXFT(zc=>n7HjS-m4MuX)DqP;|TMSOxA&#k@7MV%}ff zM81!EgUnaBVHVe3RVR4Nu>or_>ggM<9?o%`f8zTujK-?oIECwx`hN8)MRTXDk?|zg zO!k3O_#obz4QqTZ=Nfd<`pfrKD{jVJJa5i}@n7N|QP+i0(9eJjC^|&{?JN2crQbY1 z<{jaVd)>spoans9J!Fb-)MR7On$3Pe8EaKkze0#4D^tQe|KKQ|( zVVF;_Jo16=>W$RIN$x(EsO3JI2RHmeD`tge~90yT6D- zJ7!^>f}dQ&4LlD9I`9@9=s@~JH@z_h`u27T;tgmz-O7U zD8u&{Uyj1ImqLA_G*lvY)|1AZ*wnAS6LTA9p{~}bF`_%}?h%^Clhx4`hH)m^Tox<& zN^Z>i#-HdbJyw19O|f(LZ;O|BNYA1~$+bmsuZMNIxPK#^Kv&q$nU-BuQQ}wl*ca-8HduzvD{Gy5_fu zYG)DJ6*bh)VL+x7dV`0Bf9SXHwn?x_r5u#&_TLQJgI=M6oej}vx4EOwEP;Qtv1Vm* zY+t0U&xj{+Ulg)I-A0TJ=uRG54nC}cjw3LiG}-7WF+W9{<6D=be^lIxIL1oCd)X?a zkB${T&?!3J{na>Ez`bVNc|rVQ(NB2sPH}NaY zOI+nVKZ9^yg101p&VcB_cE4Dwv;`;?QeI3wnTEHyJ!va-rJGZyOn6MWRW=3X`; z16+z$RuvMbD^Qo5WkD7iQ-Lv@@CGjiJiaaJhMk22@p(Ef$h)`v4#5{o1-_Anw=IKw zgW&Mos)jpbp_|T1cxFcDR;|1zx#4y11*F8JHceE7Q@^!tqW39m}>4z}!L26VP)1n~(KJlZQdBAgY$ zr|5FK4$}`_dhdC|wPB+1W6y{j*oQm1!DHoIchRn;h{N3D@bx@njN(2VYYgmUHp70` zcVh(bQ66iZ`5EkLmxDIjkstd{^EIqHjNe$96pVbjN& zK3nz>hOfc+F?AZBM^nOSMmzK!CDE|}U&5Ri&RnvOFcBV3PYRzlv1X?0HnPk#lo9=p z-<{(?`&Iy-1B38M;#}f?!Kd;W$Pg8`TJI-DZ1DdD@GoGUy$c{$l(@8f2ALS)n71%U z8zBEEE1vt$9NI7QO;+{ZDC(BGRpwuIgBMC|up98Ekm%p6GVG@fz=Jd*PLj8A^H$Oa zdE1A>!yn|nsOL_>Z!jJ?sE>Zjplkc(0W^U>IU@jnt)T3r`A5bB{Tr}8o>hXbM_6Ze zAKZ?2S2GuTBLL8_kh*6@Q_>sv29s+Sf`=-N{x9MowXgl1iY=!rLm@0!qPEC#JwC@UG*UrimWY zI!{UBZ7H`J$E=U-2^F+oF-%=W7EHj~4}53`EaQe_#!eHgQv>$S`V0s%-oSeWncpXf z2kII$XCsQgtZ$I4p99|4r>zh4%UM3Z8NL-3iGPGP@@voi;%mXhGdSrF?LQj2Ij8Cm zGp@h`zEjQPgK_$A(Fy;rIAQD&58|V7S=pm;v9AN5&ln3Q=(y>-n{UqF(Nl}@JaIUi zqE**KfS{uPLp@ns;ZsDdIS;{KlDV>SFZ4^$ty7jwu3nQ2r#8sC(J*aIlJ5tCC$=?D zGFCQRzXtku=qtScT!wo}9EYp8S7uB;c~vmUkCRr zt;7p|Em%zYS_;(Qhx{rev_&1U;eXWHWUZfB3gr0cI zO2@5WTrJ4K{aw<8c{WnH2>pfo6@kE-X#C9!#ZLom z*4Te`exa=4d&l_kPRI%1OYll>E&7>u6B`=>YvL;*_oA;JQFqRa_XDVlaw6>!nV)Bm zV#f!&i*S0S;8r@xOrv~P-M-pUx9!0?4lG$u-67f&;|wU1H;RAfnfbeTUxK{gOFDwe zmqe<8`*MWKR5+bC&m8Xyn?1|o^{I=0of>Z4Hx`FeD`kxbp1jvOx^$ZC3Ce*l<*S0lYfQ7{$5K ziYJk5^lZJ=g}V+vA}^FL0zbAHUxJOQ8}r^seg2^4_2-oR8hkV3oZ;WW_muVUR#E1B zS^qE&q-|R!8b4ZD!u_x8@XLA&&c0|@D7^KrvPNp&da7xQ z;;TBdb`TgtO34!2=MR<(K6p zU+Xwes9?*-7{jw=yi(RmV!R=L<+Fw}u-bupHnFBa(lN;YU})42E{XtDg`0X2+VX=x z$SbYJG?qV(@!)rl^N@$(d(g!a@u^ z&1Xxqa2To6%57;ARJqg#IxT2RgD*DLmwHvF&9bEx>$E3zTF92>(PEefVj+EuQN>WcyFo+2SCNQEvIP0b|0g`tH+qd>8hwR%hWz z+u%!%=9_{|I=va;@d%4QEu^FWM+XsJuhW+yoJ6=)hp$1n3E}NJT!nB4!aH=>k8l^l zPwBANmmI_TI&}Cngk1=C>F`K|S>FK_{^U5qA%qX=a4*8FzgvgjK$!LS=^ zHpb6;&(bgN$4}S)2lzbc^ys`50lY62xE4CTjmAXe9oak8>`P37Jcn}FYaawGmjz3e z`S`}bp^+HxhKu8U0M^GJ3as54)@s12(6Cl3Si`I%upj^44G{xtjo6@UdJUia*=$fY zQPy)*J`dRedOpexdOm-OYgF?z=+H-sK7q2kb7zIvdQN~07+)C>pN?f#JpShFESI_U zslk3NbGyQ+Wk({YxOo!ROv=}=+YX$uM*DhjxS%J&9khRcs%P_>^^`Rs$I?bl-%b1X z4t(poY5(4Z?^d1v0KT{D?}PY;p5>!;q(dpr88LT{zq{W22zWszH2XJHnthmjH`?k* zsxk9Xtt}4s6@qSvTwx!>$xyU_dyxTQ8MH2avB z^d$|szR^c>GjJyT;mab}I}P&8NT-`?T17BfBxlD#>J}h70~hMhAlJ3@o`HNGr-%6{ zb8sIQ3qfz`@t2_f!^n>{KzR~A@eH}7_sqYq3wvzTiMsoSFAE95W8Ac}Qhi=$Y z!Z$E8bFe0(Eu({~oqt`2@A34xro*?y;Ulqr(Rj)&Q%VEG6~0xz9rEL^N4>GFe17|b zLOb>$`!a=gZzLS*TiGY<59(coKQ1MI_)6-R3N1L-EBOn&@QuUpj{^YvoB|OhLKKLyfgi#)QSe+2R?!?vrI9+wZEMC z)1fb<4G8iS<1h3osVS(-Anq0~3Xd)XpP;Se57o}p1mw?E`a(4?pj;l-K=3Z+0q`;Y z=E{8VMSMFQ;|qK{lxOvR>3yLi;AQDp0zW0KIMZs+c`z}`S3*27rVH8TL^b+3h_k|d zb1sgzk(TEBb3J9>OoXm3V%qU*FgM{JZARc{CIr~3w9rj?5k15A;UTxXq;2RJ>ri^Qs zm&4{Ck7)ULV?+5#d#|1V`W<*VeD9z<%8DN}%EycTh`raMS0g||%g1{~K1OEB$922& z2Fb|v=6lgtrR=+wCTks--~Kp8Y)G*O<9#|VDDAmJ@43isWmOEH}I^Im&4{CZiq+zQbJx1e6Fl~FQCaUYwsN@`1$^m z=!`wC&>8#O9RqZB4yN-T5_*rj-A3o9^gbjtgw6wdT%q%KmPLSurt`rpoe$aeA&0OJ zIf8u%{>mPAn$UVhE85JxYiNIo3c$-w$rd(wzHh?t9D+Rt`gR*j9gM z3-QR(9P&NpqabkS9&T2zo#q|b?;>p9@A@Y?Tv!iE)w*DQ4`nv$J?}kw&r92Cz2{xG z4OQ`Hqy0L*g;Pu1zu+w5M$ZT*^|Oy~ZW%a>aAJ;e*maQlEaJ@G2rA*6!#yhXf*8*@ zi*Ww`Jd0?>J+m|KAnakzB5tIt^?CYQ>K>aX8aJwSH|wJWI^fo2faxbpAO1VJk1(!M zku=i>^!cr8P$rLY|Ccie8=S|(nKO11j@}E&9#8HinIEKEZTmjPMLLb(4#Z(Te#P&0 z4y#)Zo4=n99w^W1TIIZ@bwBEv`#JTj8(PnbPV-q^&l1$bvxW8GA)GDTy$Y!Em({r; zj_;jJue8sRwz0c@VC*^ukKNzx`GT?AKVGR<8^n*PC^fA{?tsfkqzvyrloM)r|-oHaXDE?Fi>j!&kvvh{f zEa(M~DE;7TRn!mOml|#?a*bNBl64QExuwtfd-a2MdW)?&=&$rS&}oDUTB4|ezpN~f z)eWw(Y}8wi^lRHu=m-ljcjQ-Q3}?@?vAe>VugUy}eIDi%r-66UmqY-p8TuGK|BS}m z2)R7QGdt*iTc)rw{A@b9pY4`;nff^H;SOCGeeY)2W!wyZTIsLsj3BefwwUw6>92~8 ztKmA>sEj(u^i#&!Wt<0c(*O_di=jIH1u-sFuI19e=3cXQzGvo+Bg3Jb>p{HZa^U0eHFu z&l-FW;OP=Pr_4JBJV)iz7R6I%!uA%0fa4iojN_dLJ|D+B`Q}Bf+%te~Gmf`C-`i@@ z@>J;??lWYso<8+s;@G?ZeKm?}F>E0g7!gg&&y>BC(bKR1dK;nb0n+;*!iJ{v%kwh8 z{|v#uqjG`a#C-o*;M!QLw59P^KM;8!^`!J+T@QS`d=ECNr_U&wcT4d63K!0s+Wr`= zTXSh=ugBe2*Bm@0=6w!leluV*20M}J6Zm(b>_v_-1V zdPS(Rqh_Wwc3Fc>R9~wx2XteSgO)w12$6 zM8m`0ljC-a#~4wb?1IfY?!TLpqH8^nfl)8)jJGqL@*((T2WUhc^-}=JUsgwvNE&B{ z3T}UEE$pf|kBR<(=Njmr^5jSd&YlbPda<5z1kdVjs8VN6p!Wg#GGjiDC$z02(pWwd z`h}*jd6@6K%Q}CSs5DvMo=j67Mp|y56`2I>^{vD1$pBUwMc5Ndx3v zKUm(*`zV~6-^O&{&NX({`Q?}>*atTOH~N6bn~6qFWCqR-fFs_g{}AyW ziO=Hv?#c>x%4E8grrsUT?}UB!+iaKRpP<4`Unq<>I<#LH!rU~34&%e-yQgb-ZsNuI zqHWP2U_s~Gp*M1WSK2 zazKCMH2e!9J+coT1-%~fVN5Fh4Q!`yZuma-yeoW3@JAwMpw0^N0`mDPj1Hsj#hoK) z!&-M}M0c$E{O6XqDpCL9!?mss=!E_Tdoj)h{5vyxydv6H;qJy9!9J}t64l}8@deTT zA-EL|;D){Rg6J3Grph`Z9Lp6!x4P2+z0}}%!)d`(@uS7|wIXP~Ra<>~Qr(R;I}+J) zW6Bh~jeo1H&bU~1Np=65wbd)e+VWU1%-ZUCxcA09rUUdpG?3>M4JU;>-BzCZ+Umad z2l7DoBe(}q_D=L0;TX8LTYMAq-2Ap-@N5y|dqM-|nm~tP*ya~kpu>@@Jmhqccfp@6 z@Yysz+KBfg8t9X+I}hWzh4;$*LGncn#?uaG@Gi$kytfcQpJME`Fi+hr4|Ebn-NrqZ z52lu|`St}ef3?C0m+&{2VO->Q5`Mhxii^pkvB?kcemL{y_3Vz_rS}Z*E4k%*_;_n3 zuavh2)f}+73~$*Q4wfgJ#cj0HJ@kr(eLDCI^EA&g4n3{o&QN0}f3J?;qT|oB<-J44 zonnjQTjs9v*3+K@&SN8O1>d#m1ndRntsPc-O0D)RZZlzfYIY;NqP%q{efgoizMM## zC*LZ&xXnX+Pgk&aHX%JX(iR+Rr6bKz-nykv(ax-u(E>&7h!yy;Mmd3YB7!ftl<3_Tw~ zc0Pgo;-i3b%7|4RWPdx@-_T7K!lniOrJp>2cV0}cq5kR@YmQfOFaH_W(~btLcZrq; zcc@@ZF{@@URsC<*9e!M4wg2~O$M`7O{ za;EQKShu0PXSdxpaLa@6Z+i{Xu?~I0HEZxX^}8q9@Aml(S)_**Enx5~6TxQGfQRY&X(e0D*#Qs7bc9Vx7JCsKEf0gI@#kR80 z7d+rc*^w-Z`ZCDB!&5HrNn8PWwHV*Dv+&`s6YcmK#}2|PkoP$9HX=S3akwXE_K7bD z(G7j_A=1P*JLG`(g$^hOecnjnZK;=hkF{{gYyS%_u%Sb4=i^e9X{%ikw$yq(sQ>#hTP;R zJ{NCNi~5xw3GH}o&h`;Lr1vMPXGVjY z&DTIz7uHX+yxVZhY0RA#?}gnD8t8i)cTFY-eX;zp+v1b?_B-xytO=YSzd3L|Y`bYo zei-n}=X4fMg-#{s8)ysQJ>jps^=#z5q9O>MuF84viDtt|`=0Vv_T{af&?x0o1$X#( zW+Qud(kE7Jc+;~D>p^=-kze_QbCI4n_i@BRuq{?+VWWK!b;l(6f?>>cK_}TYt1OW> z`!VXyNwc-k7x>S{9&q0`VUJycdpl({gd6QL+zqpUcXg;HR)xEmvtSclU0vj#%<-J_ z;EbTTz2O|#RlAJhxn~E@ZvlMJrnI>P^>|0XpZbMS&I6bq-G2BS@>4!?8E)PIM7?{N zk9qLjjkacgxLf4pUJ7lVu%gk#+pb{HTy?(?-^Z_l%KHLqOHY47NJ1}lH?lPhMBRr4v?=zQLv?Q$9=xf{IPi|C|EsywbK--z;k4QVumNtH%{8lH@##w1SZ{y7gv`3ZO>J4W` z%D$QPBF(a^2F$JBLfRWvIpCz;VUhZ_;;oXAyKQOWM?k~ehBRrvZj0Mi-%Cil#a2%F zM^rHXinL|6G|xb}ZSXreQr3G7)3VV9p4);*ll4lcmD$R{Hhko%HkdwJ+MV!kd)$^b z#g>LQNL_C+4YEsl*xYj^VM}`Bwl{INANm>Y6J1a1JVE5~4&-UevE^B(^FSWilV{6Q zV#~8q=Lv<)j(2Q%X4~@E+keFXu5Ivt;>xxkeYn(?c0bax?MK=QTN-rB`Pud(Eow{q z8`7lx#06=|fwTutN1DhnI!*jlsJ?&ja-_AfEjq2mR&FKInr&(8ZE5!*EzAF?uho{e z4{5V)<+j_>{(`iNY-u|N(jEfO0o`)p|k zY-xLtCi8-B`$1dUdq}&(R<3&>ZTqQ6lm6G`dTeQxNSkXb*Jn#xhBUt|t>2b*H_}cY zNOKsrwB1N^*wS3Kw4?BOd&HJjIFQyp4r%*sY2$5a0i->eO*@1|Iy=X-FG1Q`Ny}hA zeTK`3_s)!Q9btRr{eoHW*COL7PsYE$0=5Ao9Y(BzzGlpq;{o0$`|JtAq^{4`*uE#6 z+9dRZe7@08zpReRl8-DsZ$l-{a=nHNbGds!x7#bc$TX~nuC@3}831@-9ry{+MBGIQaTN!>-VR5m$PRGjKO-5Gmb_C#`T{@dU~iE0S^0>ZiIK>sT+~F zUaeO#l7-eic3(Gi7iX9e%6f^BxLep|jD+uo7T-gyTKMN zq;7-ZhJ~^x+X~)uVo%0 z&3scJ?T~hs=z5_`*6kKN>cK&nKWna3c6FE^9-+?*+WjIP`zK?ge=_7!oa1=VPYB+O#;@ zb$)lWJ-?fGfsv;%`hp{vsGM3OtOhjYQ2?TcbyB-ivdp zl6t_3o%5V~&e6Qb>W|{P@|L}I=4DRCFTz_V>fF{CiFy0c-U=Jf;oKo`=!!GQ=XHn9 zEUtg+uSF-Ie?U81)V^%hj%+*i-6FO#RIrLR@oZyH;>?-R=ytTz<2Kk<-OeC&7;Goo z2>fYB%=*t8u70U|H|kD9M*>`I^_S6S5Mj>g%AJP&RW9BIK3spySf%KJaqpUQG1KFf zLD0_Z@>GsB@uo#b^ys9+^`^0El4(q-H}jwHR2pZZ?DmSF!>BVaJskJe7^D?=DvEE? zwAiEeKc2`@Bd;O|y$$jpeFEWwx0fS+TtyJ?Z{VF+d^_TA<88=&@Gpk8)tSq;k>0Q& zuln3JYWkx51eAvk-|QHkzCWNXJn4G?`#Yy~-UD1#A)MpG-VfgqeB2{%jN|wToBfY+9OF((XLjD{01xcG>WsSG=zsHA(TTTtmr3>3Z{_=0W%a2u6SU_THHH{0F0(M|4xKMVe9f z9;@oR>NxVEt&b2_{KXDbep$N+?sHL>L)r{^!M@i8@9^yKIC%njWWOu>Lcjt}?*l)~ z5$0j&SR7Hd;hAGN(=(SsN8)4O#&8d_@(}nCckg_wNrO+@#g5dPcQFRo-V0tWe&MT^FtIIsdp(Km-}Im^-ubi1WGCM# zT>bT^$fk`CpP_NH-is0)ZN((W1X!QsyfLkOf#00L+J-pbO%Zy`j>jzHlXVht#Dq9eN<; z3*PM9(tDC#7-wmz^1O&4G-(YL{96k?`ODfU=ytD#TpQ{28geK6^X#v{>tA?Ra|kT= z|8l?hAAwc)e}$F68MP&c^PDM*cj<0H7`Di6DU;2YSuzZaBI^CZ=8KLYFdRc*=zOm`^qwY{dm8Y^5PMMv-mLJe zJs$9-t)Z_2bN2wwqQ4BAuRZqQPV4==w-ew z=dtQ+5NCjxmmxd;1N6T7@1uA6$wByaj`-^kTIqXHsgFr3+~IkOwCYJ1F1+nn7*1a@ zCV~OOFvmzwyWuOvdHO-pTf0{+We$z=MQA-jP1Tewb}zd6q-| zi6{5DF+QBS4|9pO>8Tvj@2T>lkV|L9|? zq3YIR?uqC-j{0Jr&8$Tn(}4r^)e|%f`NOdnD)`=$X!GpKty*S)PF2slGi83o*{FRS zhSMM45J7314FK;@jSrj8&DQ%h-@qB)!`}gn9F$S|59^$@@sS1{KasFP#%LGj%@u?R ze=dbQhg9b-)3N6xz4pLnN8Z<20o~kO)`RiC6#2{K%o1gh5AECY3gXn6Z7b&0M)+DV z_e@4zuqFH`!Mc1&m%hWa=XIO|r~NZc6Z?V{@ef7b@F_Z+?koNh8%3<;R3{v$f@N zwBk`Tdq|fpkM!#Mh0z%_zsxXm9^1(mprZt z--kLaO8rdaDD)(w95o4VAZnGhc*@dm==$de8ytI?#~*|~V}Mp6|42uRl+|$vuV)x{ ztFrkJ*J{P#?9MIa6^<)3{@d+%>N@-E^(9RiAHR<0hk`o2A2`iYaE+|KL8nDj{O8)y z++<&mFjk#`w_;7*2=ZQRW(V=Z-$(lEasMig zHDi%6D!K%J=>rZpD!chtcKUWfMn;^q{^|G)h@XJ?uMUZCK)eU>-xw0VtfwCSjxd*F zT=|er`8q4#gwr{)Uy!*zX%r2#@t6CsU)~EG*OzyoZNM{v{XTHbC9c`^R{Ha$O~e&# zc11c57=!D7iFl1hT7IO{T{a|b31K6>kg%CH1n#BHl}0nti+mk!fve?C(8WE_2J@xU z@qXqbq))ujm@=pF4Xh!!JG{#%MSJrH*ZJayy3VrU>wFQuZ8`TS-=Y4XlF4-3c=laI zux7|S0-tdbpX$iYBLX8YsN!#Fs;#E4d!>I?u$1r7is0}vpw3gH>+@S>n?VoKK&Q{2 zepBvzmFLkxvY<|nFwJU1h5K*Nu^7;?p7Ij#!FxLSom?X@4;RKq){sw&DuRF8URyo( zCDw=gkSj_9vd=3=xnJu%Wss*<5N1yz)+BST;wi`hzkEMd^#I?fTM66x935>yA9px%)a=Nn_l)ikIP`<6;C2lO9~croI5@2Rw9<#El(+e5mHNjBuatME zP1I|0U08h7e3su?9J%S0#=k|5?gH*C+rKe#bj8?go*tVYSXF<> z!mq~!U0xVd!*B&exP)=ZF&!to!m!3UDP;!Q&}6~T=|gBeW;@f%yF1|PH>X|U<-m8u z$l}#CM$^3#?VfYB%9C+Kj^2MLo2SR-b5`NrHKg7lv>!8s_Sy$+7H^vm+DeYyQ-t>9 zwc*#@Q#`Z(}qz?wx zrc)ICv-!2pXNH9iqsxes8WR>@jrIv`*w%$yGY5R-a?AvpokF|%$Bz2aFc6j|1{XM9Dp>-z_-E1KaSE`qi?fodLbsofj(@^)cpFftR?Nlpi|@w%A_sE)3hpf^*F!ddZDWDR&rZ6dr2bvF2T@%j~o9*hkysv*%yIx><<$2?PDOZ=#NK55zs+ zrsIljacdEWb42CmMae9Ck3a@#Q8LJQ`i#o<}e@;6m;wE}*=n)3($xX9)Ui#0^ZgCi^=6*eY zT5LqVd+yi%{7lUC=Lf(8JX3afPB#j{GyE?6p=pf89SgUlbt8@M7c#Be$l-Y)?<}!z z>)UY-c=QDJw~s)-ZH%bg413J<5!wI8Pd^aEcz&Px_5KF7(~nL-y6XSyaAs68Lgo$S z!)aWyZug!}dFQp_J=nE^XDe{#CiibuzWP?2E3_J;dQU1l?;Y;NfP+KsiKGs{9s$wO z1||Ov#^Hr<`tW|H1J>fxg#AlnO{4ujh3NBRP$pIpyFw4+-( zB>z7nzwG~@Kcdee=~Kmv@cjeWt%$eLGZ{|5nDxQxuIjPLC)Ztz^QxOc z>sF_XC+`WIsXXu-$v4XC6U^-==^Uw(o-8&liofR{MAiI>&4#w6oKN1w8A`6cvz4}Z z9(`1b^QHOtThy~`#M0JVsdr2J(QnlI57H^~30=M01!RSXB z1?RC>8y2i-vYjoI^-eR~uwB6&ZlgH4*$9^Z32P4Lo1BNawMWyoF=^r_~Uc9ajd%Zoz?Z7wJdPfiI?0Mk5L2=Y=g7@Dy6aDN%mIbY&Ry_8eV|CdZN-HSCJ3K!4?LqmLlgl4n*Dw)%i1N5g z<*F??V62^vdmq<>CM6>Ok8iGq&VQ7AkFNC|a6`A#nJL=YA#H)1->pklAVw?}T z8<$fWPEC|~FWR;u7;S5TJdmESWfsN;?4mIaih?(#uj9BV9hvhczH3?=F@_Z%K(BZt z4Sa8J-7I@{@!J&dEbCYFI#MO^Xao32`*b?;%Fz6PF|lDf{M$`5ZijA-`rKkYW^#N- zUrM6vXwc51#*O=9j+22g^>R0P6zR}irnbZ0=mc@&odm{n933yTd_LcQdYt9$e5K2e(B)Su-p`bB z+>`&w`;gg2fDUfk_$}i2#XQ0^(g^wm=wZMciZ9-Mi{&9J1<7~F13x{Y)3}T}4e~wn z3O{me3O~HtE<97M?3#SgUs$?7%$$SJV){bJ)r2v1s zE@gmje`*WpHq?JN)+U^{WY3Le?*%LJmy;Y}7^THIJ(yeJ z=L`B6?U$VEBW)Y;=JGW9$mg1+*GRk*H$nC~%#Zg%X8p!&3(n3^!-*UE{9Q0_e!I zci82=_%_5@_pI03?z7)snK0Bn>$+I0k9Vx$CxUm}!>MQ$;np3&d&3(q?etH^UH3zH z%K~>f>wh73E>8L}tY1)75;lKVtM58{xTm08$Z*Vi8SYkg;QlrISndx;VUMTNFFz(e zdr~o}L(3633#$o2Uf{jwk5Dy#12(1bFG|{z=7El^T?2gpyOX%%?ArqWzGZTMkaw$$ zu=&si>WyOFpHW^bgU*Tm9_bG^SKT$+;Ja-<`ed4{F{PMuc4-Y-B&H|qgg)Va38*?_j{a2PU-`pjkb|%`a6AFud=>xN5$y|2XzLFuy*~0%X0h_R zbl#_j%d6w`Ue@MIEeKy~ZrD{-X`fUf-G6wSjO#S!r1`v01Di|Qu6Dyem-znT7|=e{ z=#Q$V=h>`mjs5?l$HVD2WDmx^1|8AwS6!jb`b{_5U4%Nn*e4r00{Obo9sFhYXzce; z!Ig6ExdG?842yjod?C@7AAS7BGl6JxMb?iW{iEg3?uNMNGEU|L?OZz4xGO>7+u(ZZs!>`!k-1&*F>AWSzAvT3yLpO z6JcA^#XYbJw;v}Sj05f5)_BF5$+eJuGg{?)X+QnCaX!%T<=VF!e6exNn;#*b=Y7R0 z|J@_Wn|Hr$euRJGPY!fLU%}5`c07j%_$tR5&*3F#le}S`q~HCUv%ZaGkK!4Tn^T!W z8(}Xu{iYFe?twUrzuZ3B;sM|55a!sO)3eJy79Rh0IQ_cFKzIwf19^bA*-f1En-e`> zq-Q{xSvGh$Q+;d_;I%~ztZT5Rpv}F|O3qCFkm$J|d!~BqnOecua)!R;9koaOY>Vb) z_zX~apkL~vPhsSP47ug$o8Mj&|KTCp@H-|D25g*e)b&KU7OC-TjHg|-ZU3b1gU`VI z*lX4OSm0Qp{AH}{mGa0>c=SmzP|g9rd~2cuekvSA+D`@U3UAqi`ME{$q|rE}{a1w3 zuSxrx1MtPZ5O1nzKkVQE+;_0k{;`$F8zT;^pLf==hVZ_++@VE1*I4yTPr^^;;If-h zwt{6;a5Z5x1T21-95SY;hp2RpMIZ3@TUkMeAI@JJ-3kf^QEv zK001uG<}Yre@N9ivy|_POrOBntnSN38##xxj?rq_ zu%2vQ$#?%UgcUEUvg3h6??73#cZp5D*($5@iapBx=M5y&mG=`*AMQ zFjp61t{$)Ea-*9-iUekB4-}|dKre3gf z3C^RFD*X7f_{KcWw8#I5Z}I1g?_>BDf4+Y2jvwQ@welvcksI6Gm_w4huiO*I7)<}D znQaH{>2EGFEonHXbtH}N-1C#rPkv{(r-E)%gN8A+)ySFZ#^o`T;XBeS?`!+jr6^xD zP~PR~o#^lk_F?hmm&2*C*Dy~wT`&E_`#~ayQKyjpah{Z?PgDtiSL|vamo>>kC!{W&*BOmj@p0Mj0_J+T z!}KrEk?W=;d`yiZA)mOn!X!_+x1@13!F1E#ZigT+POB>eeeYf0ow#xb%cbtW? zIHP=7=J&V0YMHUj+Zx9l)(Mn-};*kKTP#KjH!V zG~xuf9Re5QTh3oJ{=i`~;Ku#e!afE#$NpL2ym~M1PpyB$?>7o0{t_L3_&*uH2X#JW z)wjBFD`{`l`BXUllE^>AtJh&X6+b7)aW!j5y>F|wza-NaR-lx&_Ao3j;X#09U;^;fI6zYe1A16lkQ-zWG@?KYqbFcyS$M zIuP^TeUl&i@`MMzmnIt9!|4}~NI%|ZOb6`l0oeEMM}4yp*JF!&5^d~3ecjpmX5B6I z!KZ?%3+V}0kaczQ-o?}|!?C&x^i98*U_Jgi<5IxviruJbyzQ5u@x2QGvnva;_%?xw zGBTc+#^_wRAI&}QfHi4NFZ7WwBHu#-766q^TIU&QynU>;1Az`ulHnxE}A4+3^q zY$Wc{;BWmlj04K{seKjFmaiLMqc4f*lI3goek9Y5JAe!NH*Bu^*dH*y?t>f?yBK-k z_iLcf*S!JSLaw0P=GZ$x-*tPy3thA|TMfDhKO8wonXC)&+DM1Ne6{m&@?to>eELP3k9=d2b)a7iw4*mmRe|3qk^H-N* zPICGh=baW#-yn2m+Uic=2{{h&0lA}@zCqe7<#?72*hsrXrF~E4R-~PfIqdTPj@Ezo zk1P)v;#dEy@_fIScrwpV{yg5}+X5fgjlcS?N?W#vY2-)N$vIk=k+#D(M3ChaKEL{r zPTQd29k1{Ff0TU-d{ou7{+T==380~k_eKkKB0)1Wwy|nGVe%Nj$H1Ts7AtyD2Lo+T zY(uNP6Pb{SjJ-*%mGH2js6mZ|R%;Y!!5R$m5VUwfK>-DWLYaWJ_&^e7Cg=ZsYwt6e z1oZZQe!of1*^jl?UVH7e*Is+=y?<1&vr8(WZDWX zH*?K)CBro?mut8zk9nJt@=BRWdDS{Wyt<8f7*0HRrhsz7r!p`y7^|ZMN*Y$0aRnS{QS4PCU<3HUXxuCY9Kh-EN6^D%wU)k?q0j8g~HxjCTaVBq&-MaP*U>xjP@UUgyp2;f?V z^NdCq`S@Ri{{@fW?i8oQw-%Eonh#4PUo-pssuIXT;dvb=G(ot}o0ZyT=ONve z0Nx|F8jX;}IM3I$-HwA!GEDX}K90MX%1}m@kvch?%YB;JXj2KB9L^pc3{S`zjdlu# z2dXY&v&lVn2cw|uT=KZ!6-6KT5#{26byf;%v$Nv@hwc@uHzjpk# zs`P(nnjPx>b*9^+-#hSrT*KshM7?kKKJ+81|2Aw%J@89b^!*{_oYsv8 z_~Ne)vC~Bk7+$n@g`|W34d^y2_>a*!CVVx0@eQK?5FXN*NT+1GO1CH}ozR&usA~hp z%f8r6-BJeHFR88G@Wt;;(5S2SUK8yyEaCTraTIwwQ0`sKyI{SsqBrl0wpjiR4dTFE3kGezDR`6vO@=SIa06HS-yCC zFKk$LV9V<263okdv2inrBk=QU*a=yQ za*}1n4%~6}kdhgz>)=cAeko*@CTdC>Rv~0R+_Yf4Ig%|G{D~gdZhur$QHmqZEQIMy&lu>cVLgevjQhX{L{t| zM;pG`>>&Q{;Z85$KV~oE0AKhOFu$%nmTUMpd%1g}zzGq4>OsOd2ETJ(Y?aWX6XOqf zt=nve?j~dHkN**@fgXZBSaWBp(LD|4S;lW4&U`u@eG{Q4{`iE6wwn)H3)Z!9ZPIx;XH$0JVtNR?H4x^fLA`>t%j|r40xB~-5s!=lvV+8tqjVtv6Gkn3-gw^H1 zS<3Ng(tZ)VcOqVJ7JYWL#Wb}Im4V>E)Ie}RD#q_=QnsXNmpWk#S@TVWm%0l;#i9L# zR%{h#0wqQ_!(!uk&+#F4RfF^ z#s!cEB3qaS@?u!AVdR1i++zwp2ET^a$eK9PiM~Pmiok1G^Bcq|J4gJskqJDTQNQq- zrxXsiiOh%mn_BEKQWlopD`&>xe=dH*03Y&d%}Y(U)Eq~=17S|)6Mvu5nlBLE)pX0f zM$_<`7{XF8Pfo>m27cL1rS}eQ8h&qDse!Ze58w=tM15|PK8LU>sgvO(Oo&rd`L5M?ZD_qQ#cUtgMV~wKUPr_iTX7TP5k3`dm!ZN_ zuBJuBZID~&Q@;4Rz07Z? zTq{FcA_K9cisgwBS|bewfO@)5Q-r&?bWWXXAsvkEh^ zF$d;4ZiwUaEc$4bIxP!loFC^pKEkr^8h|~<140}h;d@7W1jes+%-gbxm}hV>qsWfK z*^tm5uYv!CAASepy(Z4F%(yPZ{k{V8Q^rFtZcQrZQ><~e>hPzDto`t8i^(g~^J!C- zz0M;;@Zn6m)&92qZ%8lNqs{QT2+B=`Jj#Zh8a_B{SNZ{r^@ANe1UoVO0lu4~;V1lS zIBx`xq#n(Noi z-KeKokNxLDEt(eQIa zTL71Al%;(t3M=Zi;ZBB8J52g{BF;N+Df0PwKk48rRUhrSLDd_6#K2qR0M7DIb+lft zJMdvnU978O&H?x+S;A+_YF!aiuSsMB-f_VDKKT#jV$Hy6Fw~RqS?~_}&i$yv7{Q-+ zT;M)mH`+Mam#7dz5BP}>2mD#K-neZ*S=}qg0(ma%=N&Hl%QJZ13vqCJ&<{ofr~fjX z)Ol-0!VhfOTRE6F-mT6sfUoE@z^NT98 zaR~Gv^yTF^w*mA64(NlAcKZ#KQ73)o%DSuG4zQ1qf4rH|=NN}}EG%}Zymd36yXC9A z?gQYHo<~KlGCyc_hBG`!t1qSPP`6gj1HFS>VSwe6w0`Eg9QB*cy77LF;0Kq=dEQC= z0OuIFCKsIVFzR}u0nXdq?HC)$|5(R@kM<4ZZQTcU$yfz>hw#n1546{XKT}yw|J5z~ET<3er(tY>e-vny3LZ_F2%Cn;R_$X~;(sFKuQXEGCn%GE z&obVdZnrgb{5Fvze&c-LQmotB*^6I~-Tw4T!o8ZpQ{aJuUK(j0kg+fP(piYh1g)l# z7m|4Z^K9bj_r-h;KZvW>!c0A8Vw`=+3BGao;x9_Op3c{f`M&5E_b@N?3G^wneP&lC zzd4s@e=2spS%fmY-R1N(qTJd(AMZGmp6_oOc+b`EPP`ZBG{^B?q~AO6Zes3m5VkU| z)m4!v+^X*vse6Nt_p0{o9*R9u_FQ=w*EsbY)PucY^w*>SGqmIE5h@x{i+8HRRNI+)C>U%DA-7c)Hc3d%v7qo2xI!GV?+??b+OfER2N zFNh9|G-+RxMq~rZj5CiJF1r=^iV3@S{OQv5x2ib)xUtv2#&yMqOUrRTe2lpDOhijg% zmnJ{seF@iES(ba*^Em6`r6av;w`(Swk2S!d0Z@a4FdrQQf49CN{!rZG&Fah;R_ z+61wl&vn5D*x5v1!Jg$6o!KaV!q^(jK}65;ML%s5czMPSBpE5e(6nL{Q04N08ryVepvhju!lfo zNagd@-pns*7|;EJkwzm8XO)qUM*;@td{}#V(+^xyeX)lQGhI@6HNs&_cxXGrmF~VQ z8}X;iaPyuB;l!^zNAkR?*L1eLbd+m3fD4)Nqr2>Ur77SE(6HoH*j>jz|76LN$~M%Q z@%$fImrmF7&!8?RBS)V93(CxhYu_O z91|SDnaBT(u^(mkbfWzEhI$fbwvkqyuGR|Hk$uf=JukijcAG)w;~em2k;}S`syc7O zoyO(3pLdj(ZRYL1jW%fohjJcylub&#Pt@y`IZ#y4`Y%M;KL)ZZxJm`jK%H zc-09V#G_=_`I{1WrYM?t2c6G3klX`{H6KqS>Z<26_8IxGum9}{U;K|lh&$yh-XG&T zU^XNF#D;tU_E-h>IGA+U!><=%LwocIBv@bbcC~2Ljzf+gGTd&jaX-h}9 z{t#u2CqB*Zw@z$sYn{-G>&rhw+{KJ*Sl{YR3dg$IO5uryeXT!i_*?6BNpY>d_=;}k z0Wb94(>b7**JO^51a{j&@7z4O^4*?bY8U&HQ}`z&ecU5`9DS4h8#-yeYOinS0z99@ zoR$4o<}cu38!p!DxbBxB{EGK8@vGf$gRWfm+ib{R;py&CtreuZ`@r+>wWw&bQo+3rJVTCAyPl9eX z%}DEnyb8BXcYk1Yn2=jG?I2U8`H@lE8=Bb8tUG-4y1L6D$1n}X|44Kj&boNL6};Mg zf@LbX^{v$`6wm)vZuRNqj_vd#qb~Q{q;eyw+~+aBu*-e^ILfU}Dz}7%;z^Y29dzDv zqW@x^>Far<*pEmh&r(8^38aI&jqxrYa?2EGl*3pw|+=y-TX8+_RY{oxm^Ut$bZ z?*;vMk^UwRDqd_m?2Fx_>KhW70iW$JK#*wC+mqg_6hut-ox?! znAKWA-iO?2#TYE(E8d@GyA``0^>WP<_BJQ*nGaoZNO8tbo!Eo$aAyW|Y}c5#YF*do z1WRz%9M12qE%8o*E>qQbn{h7E{9gJ1^cm_kob!?Q$JD13G?p9ZsWCYv>3)~>&~u*J z0Mrb`>oME$v5YI$mo*HFJ^}dzSx!Ai)#1d0Xjl3dQ`e$Sl#Tj1WDXh|vzf4b@jngc zf1yA4!wkQ2fiLE{O5&`O4%V{~z6E9ow&6^n`!IwJ;k+o)$T>`EMEeqNHRl)=zIfX@ z<^|8fmpXSc%B6m3VNM8MIdMPF;fCCAXe={sf_*YJ8g`BwE!;hp*+qYdq6VyeH}n|A zf=?t0eA+%m{#3VNY*Dh|$u`1&pvSoBQT#@CVB9!=Ep^0Jq+>sABhJ*>^qji2VH;p} zLiVjpjI+=k*MC>TleqxpE$PyEQENpf>Iq#cquUET2JMUSLF6Lka5v)n=rb`-Es8bj z^52kq>**g>m%b5W{U2^&crRVoKkSQ@eZny4V=I8G}S)gZHfa^XQYJ|4M&F z9Pz`N_41u6y)|98AM6TWi~;>*JqhU`GuIzPI?)joFLa-Sv?+)SA+7xm+tC-92t(Oj zkD>fSK!cAXG)RrRi0ie9Tc2Z3is6tkWmKyNJj6YPg&Hnkecl#)cI-1~<7$nUVb3?v zZ)P#v{=Tkv9^?q2K*@&}g-g_fpv0r>?5bR@w+ZZ)SH8P+RI3;Gc!m-&FPdk=Jc(mMD0^8efeeoxnm{*7q0GEONy<-fk? zi+e>^D|Lq02hOT>4J)GTXMEGB#ilP_Au>to`55+y_bgKMeiC!|&;Z=cDQA=$W(Vld zj$h0FsyRu#S!kf^^CvZC88~m|$r;2!e=EH>{K_6h^T(wu=BYbO+0cF#!r^!NxbQ#v zMem;KG|kuTST|8?E11)k*AVvC@+amyc6#WB71(1umA**OS=Rht`B1;4vc_iqJ7swX zeN)*#m%&zyISclC)8D$!o@ShVToJ0p`eqp8p=DjHXx6?C?LeF3be?+$A0%&K3C5i9 zKMK@jPCg6Y6KgU)K2il0LaWIVXw0FGS*SkmwUK{zWwWqdJaTeUZv~Vqw870 zwMF44}`?BA+RL)(#*Pm=C6RLuc-_bBhKgAV3y2j7((_n=({yE`z)#gG~N+wni!G^VWq z=O1qYzL4ed?Fuhz2kTmLyK$Bo-f+EHx!406YrP+Zp?po#p-z1AN$h*}4$9sR-7k;j zQm)X>>26z0*#=p+VIR(2_k=!z&qoo)tvq8bWdmgaY|K@#IalK>xC;0X`yp4RqP(Gg z00-X`!k4W8{uTLHQx}=XJZAV+moMHb<5%PrrB|vm-(H=?_~79VjQy?K34`UJP2ppC z;5o>+bkYyyx8k<~zh?ZF;5Q$?2I!hiNIM_t2B`5XR^RV?Holi2evpozhVN#457yt4 z@Vx@x%4gJyasNOozB6_FC1~Fj;F|{Ylh#g$T6ZVT@Eu;DR2eE#Dy+IpRO85l`cOiV<_YRhhMfijbFG=`VgikcFqo zFc~KveZ{y-=2EdAoY1&nJRTAQNInVMHNu*J!%+59!sL3B8Q%B{$N^i{%DRCs_I;6a zs$ZteYAi8kVQrUlX_<4lJ!ji~g{MlIGme|#Yfk!Gc7}ZMvFUyk2-rO(%?9pE{&lk% zcBS*2f91zA<}l+g!Co}Vz*^V2+0;+bp2_}c{pK+{U_(el85`%Bl^Y-O#i|}7jJmdO zJq%sn!?pGs+m5ea_zKoWlVA-WXJnN+jeDoLzG0LaxW_(YEM20vkzTTv8s7LA^cb(M&pC`Mp6Uoe z|M27d-U`?`{p@p{QymR>ucF@EUE*lqT3NKt$Swz*V%Ls#_p*&E0AH`U?R4TDKF4bg z65g>_jBL@R(LZq?KqkYu*2#Gz`5fo}OFI{ukh7Ryf_`RaIecAQMv^Ox$KnRdQhC$B-pXtm!m(Ge6%? zd>~&MWc^j}Db8aUWT;odK;N$AunEW29LnrSjX?G&p6hWxY=$=t(sxS^;5o9s_+pW% zlu`Zqtk)NPcMV||WYV9*s_0j*ccBXuV4a+C)x!<$ zfg|(9u8Xh-(I=*H{Yd%*_!9VCZ2BGZ`>aF6zITZq8TB}9GOZe`HkDzl!ruEo?q>Yy z<7lei7k%eb#(_tRkWcHC*PMiIy`143E1;`Zvp-5dj$bY9!!m$N75Htc3NMpzw55Ue zz@Q9XqhDA2%FTWQ;}-Pb=m)SFf)6Pl&|m2TgMGIDF^%!sW>CVhE(!m8H2%WNy3B`3 zFLwv@|5%~mNIC<)-~AMPR+U^5A3N$0!0`{h_#zj}(D}C_eY-Cj6`lL82g17$ z-q<(&jv}+pQG{@zCG>zI(jRtf&Tl*-SEHvEGmn}_Exi(bgKgv>pJ9$IzWa*ju&rl= zk&_2`QR6zhqol()oBeNTxiO10re5JcGSz&b4CWhw5;SU4*c2tV+^$W?2Rik zO5DxjTL%B!&5zWzP5eRN=wF)yoquQ!M1J24Udr&r=gSyr$vJUZCzvQR`M(!=*Xy}jT_4bPF z%k*d?p8G?4+|Tg7g0#l}W@$I^g-D-_WPB2OO7c93vQFz|^;~0$G=j%x$~+J@%ukyX z&9^vhxg42``M34iVjX^!4i{U7$V%j47;sm1#qgG&Nnfg5JjEAp5#9%GVsrNnO4}ir z5w{dIUJxIDixuM8!?wPdn8Ss6x8L!2pLx5;$1ONR_@PWcfC2Vb+l9E!sMnG;?Agg< zP%rNqew%lD`PP`&k8ext_Sbw{*vqT``b6WX+F#+g7U#%+Rr~+e7u_N4&-NCc#JP?) zZUZm2gBRg9R(D|H59+p=#o+s4efeR_2Y@?-b+_>me&l_DII}|WJw?$RmLM8h@uGRTsQpSICf9UJqKwgb&Pa5Vap}@-a zD{+1!&rQymtIlx1nA$j=@HvkOtSk!ExZWCw_`Bt704e`*kyWgh(j$yvJfDKNxjS&K zWBhR$&q0GK;FZocXu5=YL-$a-?abLm7t;3hYeSqZ^3+V04P9({;E&fFL_1#AyAS^H zTNtOyO~*R-VD9lpIqZMwstl`H(YEK~g*~u);e5(@=wsCD=5+ohtGr_a&ep~m_#>nA z5nKcvH>Br+*@$!4amZ6hpTap-T2`I)9o#9}@g~YhCI3lZ!TJyTf`2Uf-2vW9(r^xO zqn{mo{Y=|tsrn7hOtCt5+U=;?{PkN=$9&*L+B-oD+>;usM!ETRnJABYjpvgWiBFw1 z3ioX91paARavy-@W8L?t_U@XbXgdk(cg7Iz31FLoZ)cgI#9CfPVtZp`cP-75<|*$uqlLyf*yiuFD#T7W$$+1{+!6lCQidr*|W8};bd z;U5_j`w#Yc6he>5E=aeIAKM1M7tALRmJd1v;Ny~p_LDv3$nzv<37zcMJN*ENGnOoo z&snj2_j-Gu?dz|IJs>thWF6%S>ka*z<3R}jGoqc)vwOG>cB)vV&DZcm4{If|coMpo zH!E7tarAUM)$6$HV8kVUB!)g;&3g+W(@4);p>-_3hH(nVXo%yTyIJPEBKwfe#M&|D z1(1E>zm;s)dL8R`_jfYCnuBCM$amVclE%Sd{qk$u=a<_#j-emjgmw=g8=s{5oLY(c zjFJ1NX`_byEqQ#Jv+W1|W0eMMqM_5&#UK4rVm z(dxW^&bBKKqF&H%txk;h!&LZ*F?zn6Q|k0WPT-D;ZK5lG++WsX4ze0{Y1W=&^;Sp!^Qz!ykXum3anDN30X3eZfG7IfBa=?YGqQiqw*Mra!+_@}pwSfi z9^n0-kl~o$S%>CqJD>6JSGPXKw>f9qMfUeGd>72w_C5O>dql0=Iop0{e@F10@yl)G z0}JP+TK~eDMc1+YvIfVqtYtlr@&RX#pR09FC;PgZzrHaJcth`AD*TJQ9kz@p!9H4d z$dEn_nGacUxJBoue&B_C@pHeM$PLjK->CA%=e4l@fU^XBC(9R`C$iCosmBk@8xD#7 z3p%bvo1l$ErsnV-GJXHpCe&+L|Mnt{e9_G#S%$RP|Jh!j_s$>E)%?Mm)y2Cn@-z?1 z8JhJYjA}h*l;D3l%DL>=M)Z{tT9=jiuQ8bIM%kZKXUMDjULZcewa-~@IA5p87u}@# zB-Ra3_XgD4rR40k^9oZHEZPi8nk|d{j`DLl#(&IjxR!)_CJ$W#n-u3t(q`xnFYCDy z`o!-Kvs}=r0dubGI;@3b9ibtxuP8L^Ki~r5C1<>Z-}s1QNL{IkwUl}pQ<-j#8{<{FA#3pX&+*~6%%k||?+a8v z{k`x%&hq&Tx?~~vBR_arChGR{=Hgt{Ia@2{%rTt6uL}HF7)&pMUZiNH@fxGbdU&0t)gr<<1|1gi z^Hg2&s2spFf-OZAfV(I-9Jpd1Oc3)H?p+C<7a{H?Np>Bq({Rn~!;5jY0^6k`sa|*M zvaV%Wb;0)nJnL5Oc)ibZLS|Rhtvs3o8>H;dz})%r-g+NF|BAOaVs2Giyz|f4GX*~t zD;!MUBlvKSPyqOd{kR3^E(xs;rPFsB>6!!Eu3?+rjk#eJ+ln}`4bAZ1|LTi}h7zB| zdKu603I9F9_~79P+PLmh#&b*o@AJIcL%sR8UxfIb65mVz%Rs|I(hc>Od5og<_N!F= z7fb!!A+#&%kM_(L9R=`p8`72)5g0DQu-@kyUyUnG-TBf^d*DlAcHx-7zN2K+L5J!uJ+L_&d8HqdMyNM^mkW?@ldU%vdr~8vs0-FK7GOS#^{Gj3VXX9s z-<2x@m_}}GsrX%G+vg~uKh9F^_^*qhzifg$>+oiMz82NuBYOnv^VPhC`%$UOAf9Id zE8XB}y@rK3rWxM;2ad14*tevu72N1K8eg_kE@COvDkTcG5(kZ+b+rluQuS5Pw%>Y)~Dx?mweHW zTz*9S8}*!z^?~WV^_;kv`J|rcFH+BCZ>FB1p7Xs~$3ITu#mlfaEhDjZ70bPaa1{Ps z({z1?D4sl5^ogW;tUyveVjDsKga0Ge2hjH@yP(T2DRmkHrn%aG!~Rk_R{01(mJEPj z7G%VmIlv{4^)7!BICsD%_9Se}Ev-}CZExc2mV0GPrJtz#0M5gl3%sarLgxD7BSZ#p zzJzwTQu4RG>F$?C_)DiLRDJ1RUjVn%iRi-`w`b8F;!6jeigJFL8KS&D#QA@(PcQTX z*1_g|v8N^PgNz=W?FS*WPUb{k2w{ypRO|wujR&8Ns34z-pOflW{|aLr#UISVUKaS< z;LI85gx0S5afUaTMW1%-{YWMDqf}bW#pC46C*-#dAx(R+%MlVkHt4D-l24*^HGmc9@gVZ;3nrPc(WS230KqDLz$2S^M42v@P;!D`xyBNI95T=@dqotPOP64--e$Hbjbt^g^wAj18-nWm}9dy>j9CS!Rf*q zvNqYTuKNHhr#=(3&ghW-&pq1+^JCobScMQUVZ5GzZt4-fibKb@?bSY zoyt6k9y3*l)9$Gp-=XBgvrJ?{%{h+mC(IfEbZm`hk&@l8K$%rkj!nYETa zJ>@!$EvZN~LT&v|7S@hKRmLc)_6r$DE5QdaC6%UK8L76$o) z{3YXT&wt3+Abuw@u6=Vq(~-XbPwc2XLl)DJKko`T#0UYI{|Y;Puhf*0^J!#fT5&zTyp@qPdkoSeXKEXJd@RzG}@ zXS(;pmK6KOgN&EVLr|A<7)BoTMfXX)NH@q6`lk$2`p-9Hd}12l4SloIjI^cf zf&Pp^kYiA^P01mv9XOv)o0q5F+5>!X_F#HG*3AD|oY7hlSoxpRYYQg)vDP&UEwkmM#5+ z_1ASv2l^tjblql&jW?ds3L4zQIH3#VKJs7WZb!XhXUmvDdPgc(dDy0pZfl)M`hfpo zgNe#J=;V2HKgJp4m2oLHbR^*^n)L9VMx6DXpcm$iUK4h`MoqJ-zPw_+47fAUuU4}@ z#0hoi08W=F-RA5Wgv~siu2m*sW36N`$0Q9`?j=-dvmawx+SJj1?iQXwzX7Zg(AfvX^wh_e9KZGH}jgHte`Z3rdW$(UxX$3HG~;!d{S&Rfw}82O=!L95@Jm z#lnZT)s1)~FuDUUV`pt3>|=9r7q3(K#E)(^GAIMVi&9_8zyi0CwxF|hHS6-o%GOrU zbTz_gN2w@uv)`m-Fn>4X8vbYC{}lXpvVXr&Zd?!BVJF8uq|ZjU@H^oX547EK;($IM zB>Z9*&&z`?K<18)9eCGg=Q%dvtfYL<(8={Tb3J?-#$${+!`d6lNa?DBZq} zPeC8c1#B|``) z&fUe9to+t?J_!DWpLlgW(;^@BJtYgrVT~=kbH6W^buHt_mtA!!5z0^SWO8{&k?%{u zkUjyL?0N(;Wff!$=i27uM(%=*Mp_hi#6TC!_GbQ7(nYaeM_N8&alh_alUN4pX)d$6 z%v!8(qMoGLSJ6wgO-$Bos2dZO8Gb9z7aJ^X6PdfptNK#Ul|q~FTi^A?26nO>wr6+Q zDlc#qTLJYYID6$Cj>X={ z&+b2qC#?Cn#&ymLoY@W8e0{QPzKlhp+#r5S)Vq8?jc?dU9aHhE>J4|?jrrPO+@qdd zQ*eHM?YGC~bb5^ywG+$$*QKy0X$AVkmC1b-dq!&1+;fnvvteBP@C(8z_82L(WfO85 zGcm4aRRorf=N>R?la7Nu%{oMx5~;KK$XcQKXqhjTE<7fDR8Bs!eyh`ZwEP~X)2>8X z;U}Heqx22!XCnKW-};NY*+74t75Ag!c6;{nm_@$OXQEj7%>$l*GF-!VC_!_?w#+TL}mw+HZ!T5s0D znbdDNj*v&(ZL6}SAHw$IKG5oSKW>DQbzfgBQ*;wWm$$#E+9l(JABB{F&m!Sf71Y(t zcIfmN>wGJz7G_GUIc=TQB4jA9M5awBcd1OeZwLV{};AKe}h^v~@d47JU1M>#7 zF=z!o85e*b#)`V0Q#n{a!dYRWxAoVhK(jBdQsY}@8_Pdkhc@zk(dRB_TrUmZcH&Id zQig%H&C~ zUZ1nmp*>G5OHP+OM_2t+^!>H%EYV~)Hz#rZgs$b&I1 zvqH|bsVRoP_Q-*&G2W@ZD1Gg1+;KH?G4rFH+TFD&t%MJL3Mjw#w>OI)L~Ot4YfV$=P zt=q1Fs2j%ND$xl6<5|!$a~0^ja2))oFfZY}9^+>&&QReVbexN2l-0ThuI?Uzb38By z!~ZUC=JN+xR%5A=spcz|ZPy=8-#*w*sN*=6!haHFK-ZNgarT>%Jxj0jX6@&mfZ4Vm z8S4H8Yz;jHT-&X8U=7naoBBWc(52m@TK!4imvxU?Re?1>+=1x0lDx-skOv*433-BT zj5MYh-c`tU_QleaZ1$r~qHju{Bds)@-dB7yK*kutBL0BI{e$oi*@-dCn7wq=D(Dd2 zy#S%*r<{3%HTGS_k(q!Si|5&K*qfs6GT5@~Dh1=lbGv4)!g}Li=?CB`j1lkeDs?(i zbREB`>X`V>>8+!&33TLKSL8F=Rcx}l46H+0G0fvTu>b!PHIHv?_QkG{`u3gU{By6? z*HHs`#5SSjp7D>&KJe%iW&7K8o1$0hV}5`EZ9vobPQH_# z?Bl>uug$0JXSk~GdxtPK&lf3v}t72dAQ(Fc#v&xWgkKcbi~-)(BXNM-6jjx{ zaU^sK%4z(^yQ6U&^ZqD+Z}>PHX}Dwa57BK+(w5{D}C;2 zthKiA+<;x3;0O3DO&9rBU+$RyXg%C;fG==evT}?u7k0%4`YNjV5%hD8!|`_;iEE_J z@i6@3hw>eH7nuifrWek&jc&j`v1ZgIgdZZi^MFIFYCB$qUm!bjg=b}MxH}u=pTjtz zf4O4`#@Bvp)X-a_Eh6J7Zy>YoCSE*m0(GUY=MHby-d@|r0JIO=4EOtbv-Z&Ln>V@U z>N4z2G78RxFKO^<>?Owel@-8kjIpE`;S0wYchkq_o&w{1uB+ZK>euiOGi1*QbjZJ- zj#J7m;53GK-%y@bX18~ee7p>}!XD;GqmIpfM%n@QwJYkhcq9NXJjnu zNnI&$RhqlOD?INJyb(K$_j?uJ#P;Gni1+yj-;Q_k)*`%b06*Q0_Doa!6dMnI`W5); z9^ogPqg^*6nV)W#KJe%mgKPO>r{G>P%#oiX4xk73bzvUe^Rc_fA@eOWy!(5qzC9>;tR_h^P}q>4dk3=;b!qbX|J(G3aL7(Vmyz71yU57sLtA-6m1KRCw z*5B+v+cbl(!E@l3%Rrak;2Y-vo5mRId#t_YM_6}5ea0A#lv})~z1BM}zNRoy3;8Th^?7asP6^q5E7O+bQlxKZ31o_xlLulh6SWhV|T@7epWO z!?t`o{Cme*r??+Z`S`+SG-vmJxo=MW57{4ABz=A>`?D{)C5|k(7jAde=##teP8xF- ziJ$Q8fN=rfrQklf53TMpjK$jjHwORT82o>`-8VOEch9-=HFxpu|8eK0{>SK(H=aBC z9l%?&`|i<;at4k5WyN))ufOpM)dyRgvK9t^5*g>?88cbFYF9@C`tjDjd&g3y`=ZNh zn1=R6lpjO+u`=wxLiyb&KRWuIoI$?W`S&o*=rt&7QK)qEFK@hJ^!2;%qn%Xy+X$bc zzC)Of^#t$d!rvo9YzZO5!@lFqe0ekDn-3US?0=l2Oy6R3&_Bx;`{F^y(GEi&3ffhC zamObNiy)qSCH|bUmo8;L!=Eez9E8hVWC;JsC611{7e0V|DQ%3OyLnYWd`~5hL)x4; zNgn#0Dp*aD7WpI`zN|W2=z>1pGxw^6&|O2%sBp(kOpkC&?BnQnm@_uxE;DOli?MzQvp%3ojI7v5tA@BO^4h?}kD=;spNO`=1KX zcUNRU+^A=I(vLXWu#i^D79ZliFccl06#cQsY}vg~Xe98B%Lxnp3TJ$PK7Ouwy~Q9t z3BONm=jT3cdv3f$`c3>NEq){{xt;e#uJVJ{&|P;w+6B3dGpD8A5qXcgm=1j1f6Rmr z+T+sR=-160ha;HZebqUKaxNkKbj)^)9l+IIe@LFMvwv|tFx0rJ!PhgciRFf#Sj91) zYbrcnoBr7j>U4--%6Q1hf8#vH$GVvoc+QtIJhOI&@ySQdB%3_*2=ujqWy4)YR&eHd zIf2Ubj_dmAcpUG&Sv#ozPGaA4H-jch4$;5a!2W#$zAhQqBOzy^ z4(ySnu7bI1&k4Nq+;hyodphyX^*)}lupjS(@t%SAop>i4?BnU#O#2PKbI6kz1Fq~G zgZ0aD`q@+rg`9TcJAm&D{hf{P5b?k_eyLx`Jxb7F^?2tkE;Ss`9R}5uau1+dV?|l8 z%j|yE7r(jF55nM^ZQgHBfD!gZmp#XL$}-@#4S4aLb!o$U8s3Tj3wY1KJJyMMTJSy? z@31ZOEXBIIh41V+8)+--X@vjZmBE=ZXLiD$P0rz@-!|q3ImNDlvls^YVNFW>_B+sq zt=_EvT>%vMB-T?Ab}iaw3}jjj$Nyx#88n-iboQ*Lca3G0A9da-cAV&!`~3ijdSi_+ zJ_+aSgpjX@ae()QFWQyI_%rl_S-$9_!i$hCA$N!L$=!P1LR;=B1^?th=2!C}>t&|( z#pm9_Fz~jVjjh5e*D$OeW3N7+Hqc#0m{tSo-cFsvoAu_?NW$l|^*-INCMDP3*W+32 zM`As{()$ftoU{i(HsTzZ-5;v4Ovxu-kN8Rd(fxR~i6{|k_xceo5fc@CT_b5p-?p6in&H_D3{PdGR~{93-NalPM} zwXshGx8Aus&a4Fucy6sP7F)-B9G?(|vvJ>MU&YzD=C;1!Z|y)h&c-!gU^wN3lC$r) zSw3Y0ehv8XeC%782I;|nr*>-oBV70o-#A?2$TL>7hH^mRf8s%gQ$K*N+pPE}n8`5o zt@*02{JnQhoC5xZuNdYK1;Lrlia@2|bmAOX-o;y0dHTE*_)=EOytcnQWO)Cu#&K}& zcN(es>`kt98JtI-DJ|ux&_Zlv?@ZKu+vJP?cqTwoR#!7$x4fT=_6~vn?lUXD z|C}#&q42hD(+7MpOL%W`WzqNh^Lw!J6vh?tGr+j{d%KR1Wv_pMqWL6jl&FgZo@T%F zuzx0K6I*T ze#GGn@GoY59iApWav?3RV-HI|)o{~@H{*#n@mKh%Gd%j`*SIg z1qyf7zDcx^GWybHvD7~acfzpY&nA4F4Pu!`-q}%%chHA-lx4`CEusHnp=~Gz9dJGL zJw@AO9Hjn9IGmw<7H6`YBpk>O8iz%nFb=%a?9JK|#4DeKo`5hHc?Ldb2K>$F2USSf zDKe}BI%muKMUZcgL-u0bqhh}wnKi7-p4uojQ}S6_ZJD=%K7!CG%VA3>uwkIiHjE7# zhR8^a-!F4L2Ya-gCEy#*>-8^<~g@~YT!d@+572l!K^!++3D+F7NCFUz8I z;wI}9h)Zfu+yl6~^~>)z%9)GmTAW!k2yk_%=TQj*Jsb!}xw> zh)iW$OFJLg}-uc>nH6` z{?T}Svv1uNNd3RQZVM9iKfP`XRfg5vkypuOIP5Y`uS3&e#sPkAzc>xQX|tVI+Te`s-}E)@ z-*mr*KkO^u&o~48j=nT%8e^yF2LtqJ8mZws`^DM#r`vhI8vpqR?KFgecBI`_vlS7&&D{sp@1fqi*&{#ABf8-5ahpnuOFX~&-dw$1;A zI<6n=hyD%ze@NLdSI{^@c_o#An2+<&L!|9bc*^_5wgZ+^Gk#?7Yt>F|Fi^(Xu!{IpE_-Gj^zUp3pG zATbB0{-br(dibe(3bA5>`Jmb}qTc)K(>QC!fxDZjPoGtYeLLd`8{-n!vIbYLlsRA# z#to02E5Jta&L^DTM^<896rWJa^cbH)tgAQcm1pZ=mqvViw%n~}MZCtP8<+;;&-?ht zC!t4`;{FbvO$m8T{lUQcUc0SpV{LmreoOFc#%~3Ft@y3QuN^pz=#8)`*|fu$oF940Q2W4Se)KMUJ;qNGcy(4T>T(tC zb})-GQ=CB~?b%XK$Wb!^`{q5@9L1a~qu8U?J9U}@rePe`E|2%im(nNS`4;?sQ=cim z&1#NBj7+9Ax1~t_V8<2A|FML1>T-=xT1^JZHOh}-3>;8gZtbYe#Q7dak@tM$-P$kj zZ3sJ>$eWqS>w!KoKJ^)#^_+>jDwsy$47eCZAjYD*O@jsn||;! z&)|A0Y!z#9w!f0G8;rHVvRv|GK5R>Aw1we4ck;IMxqyv(7`n1y6N4Y9;lx_=XTALP z?l@okn%#aBN*ss_@bKW1Ph!sI&3bt|^s3oh+Y27JG%)#z^tsT>v&HsfSvPTRNqdsp z>f)Rp=LDSZ#n~wpwIyAnYDT?^eSgLUFWiFpa+#EY`Nhlark}oYR`2{UjWz_!>ITnn zZjJS7;BW0_CKm$8b55HzPH~`{0>A z@Y>Giz?ONNCLc*375vAgftrh6b>jWc!7XmQ-@hqv|=)pRDf4;*V2>M(YAs#wkK%#LSB@vlk2+4rK7};;CXnxiudl}82|Sw;eFtM}8pFs> zp+AuStPeFWHANZ?PiH>%zJ(1Z&NhP`-zsK#fD=!)H}N}(R~;eXmCT>S!{*QA^_^pf zWWIT*KHo^geYHmLPVhoHeDJxa2V*Jmt!peWJdYdx@BCVQ<9+h?eq&kqUqqV2W-^TV zarO`I;41mg$v;Xzwstp#fXd8gTsmieOd zM8+R`17~oI$Jx)YLxJvT&<$jM3LonV=J&XiGV>LGd zN76{iWNVS&+pnI)A8RiGwg+*-`z*>~sUPZtJAA3L=)IGF*#Q#sN%Seowq$(^GSnOT z$eO}-=X|tqAo|ob^kq@{0A#tNcmm5YN_cm!>i2s}(Z^t;_nhz}59p`-X2S3CZV`s> zXE^agoPSuvSaGb=@)u{o)?$s$fnW5J;6+Z><7x05`AggD-#rf8+e0%gC(0`-_K5#n zlrqK)@A>A^@sL*veyxHpe2sB+&Bvg(1Lqz%;S`t|wJc-Uq_z&9zgz> zc(ijWzDMct5IFD~=P?;IuJ)8#7v$GAU!XY4j)zP~Wt1#jBW((t`;6VS+NYVm%%k9E z=$Lxz(BzfIcWK`yPl@c@2iX~Q2Wp3n3k04P zyK*!6=a+x9*dD9Z{s8OQg7b`lID2PF{dnQCZp1As_!jxXT2wGh{{Obdt9{!GtQ9q` z>`;yCcnbWP2hXuOFpfnr2LNvEpjBtrSv3#Sj3r#?W0-S*&W3*}$YYKIGgii*0@ktF0K0lg=0N-Kj-k;<{YHkJNI;xCvM3=jJOaI;^uL8KxROH5WJ9IzkB+PuML>5 zy`aX|J=1-$AGUay&3ugc)HxT9VBJ5WvIm*-IP8~| zIWX}7%qhq-m1lIrf3yPgS>?ONJBcK1T^r#ruB3x}jy9q_9Pu)zN1OR#by82AU&dp` zBR}jMb+ycI*A=wfW!Dw&TO%_ERA@Q$ayjg*?zTPeiw|q;y>&X@l+IfdX=3);^s(^+ zD(2mRc3HM+K4j)0wT8T>n0ds`*o?6f`=xxbgF*+~Rgv2@4P!Na?l$;u!xlNGZ6Mlc zkJ9z(p%-E;yY6Y6y;6(2J)(PMK1p14dqhR{hWC8h=^9XhGq*jy*nz!*R%$nVXhy%Y_tw#C_Rbo;Xzzm2zuenA z`ucYs7=6j!-L`KTY%WWvQ*b>V>EYMlyS3qi>#?JA8N#(}<7My@{}C5(XbB@Zfx%?plV`ZN)wU`UBtpk@$&m-#dLz z$rHY~DeccSSe{W>-cCQ<9qX76d|_xOW1Z^ z!tq(3*D#%37oXL!y?=f?U#Z7%BV9kJ=O$WI2X4bcYUB=u}_xVj=A5K z_i+|M+5|P$?EMS&3|-8!#79o{!+!7yzgf2+e1jEzKKP{5k)RKK`)s;GUmKfK>M~O7 zI`R(G8dvJmicX(Rk~5`bZoGRY;j&JU1%>d7>_DH~GdCZ;i{Lx@lDdmiDgs+xyw4ZE zyp3r=n+B#K4Atkh4;L8J*G`|a=J%`TR@Cz@UC)y!GZ$-0QKUHqT6CL7_MS4gyz`~L zFB#*_zEZ+wTpj$P5$={YrcYpOU!ENY-rggAJMFMRN!+^*#-Tp**>`*M>^Z@3>7(f9 zN#Xk$F6I3CBb0OJYe{jU%dxH0S!w$QC&kIQfU}R3&6a!`-uEqq!=6SBGg|kP_W*NW zeo{W^f`>@~kZ_D`&ADUKyg))4x z{RwgXUg=oji*7&2^rBn$Ype3HXzQEb&jp`42}}5LAN$^bAF1}*Hx0gG zRYHeK)@N@8$`TohFx>HR=WFxlqW>Ze;dZ;h&vM^jglFjRku0BM1> zRp|)99{3^Nb5+`pW-zVP3*$d&ggB>)+q;-?(5rLv#v5mg&))=Nhdw`5Y$gRRo?mcU zU2v8W?Io+=n;2V${7%YU_?*%1UcmjpcsGm794qkOi+dOjvz+45#*XM*w3DQDc+JXz z+!L0LdKKWD#V+PUT9M_Y&XC}YJE7Xi3xS%MTjV@S&}B|B&TudGfj;jKCtUU=oFAk6 z(OW_@xu>#U+w^Udlx*02Ue#^#6ZlB&B0b&B@Tcy;SsrDE2fr^+2eZ4(;lU5}F(T;u z^Qo&)hL|n;TIy0avJWlw8{h{yXDaowV?74s+(mf|!&z+w#gLPwhT-VEH>EHNKCQ`^ z$8+Mkqstu`r|KQUDC?TWI11$+&UV-_J8?Ga7{}z!n^T0>oAQiUx5HsTT*7bc$N!jr z(2lUzB@FWRaA+ph?F)#=HyHJCuF09lELX>nDDo;@=Y8f_YJHwvtzmAJuVo zABrL_4d)a(Rh`ycs9}%Mcrl(hp-yYAVBDPIROtV^q`e@g#U3%|BEvc7LgNC3`x?K3 z@phara4v+|d8^!q5-m~n!(Ph^u4bB=ftc@S7#F}EbV1Do_>&!kKd%G(doS_@YP~-K zue>ewr@uS-2z|i`K6=rd=9uEme6Tcu<8sFoGrZ~zRqk8040nUC*mqP|?HY!uc|s=k z9^;Olr;H`|wHCYH_Tc|o{Mzx`iXYaSjAor?PqAwk!?E^ctY8?@SL6M-ey_kg%2ECk zzSzx=u^f?`ICBeU{n2+)&XY3lO)-73VM`eg+ZOB~4s4Rg+LAHwxLT)yJ%r(zjxL6y z9~fLyO4PwRd^z?6hlXE)yK`~Xu3tdrd>`YM%=7l76Q}xHAsgEbV{&D#N&muNCH4ayrTs#Dc^t6m zI|6l}Kjv--dl~zM9e~Y!n^D4PLjD_%{<8=2XKrmlMG@e^FM1rqzknXo%`z0u`+kG+ zb5+@TAKC)YXe7p3^qgug3m`W`~@UhC6O|5+2~l+>bc& zTk8Vs4|*&SxrMQg@^{}doU1!**fXHtw+(M?#;6?JTU$||0Xm<5D)7rrqYH3w4lwKo ztos(sOD$_~1h}D|X~YeEXzl2?3}@5#4d>_=jjGXp<5J+7GJk=QPkmV7{HBArKp$IM z3i_Z=7~I=XLSKrUqvPfyE`+#D#2G5?NcXYNILA&qia7XHW#_Q$1pmaUFDG6+ zFO2azPWyCKpUXIu2^o}#)9cYck29{M19+%7vDd18_49)&&RWYj|B;lnfSd3i(P`RL znr?wDbAF@&UN-#4B>uyMpHBy1DH$8W*e*Kb&vz>r14t8;-596q z_VZH4CDn~_cG)7wP&a`Km~t+TE>rqN5=_Qvm_L)bOcG4SX_!CT&hjFk83py%qrU)O zz~#9HB8w&;F@6v{@+0xJ^2L6(oM{0^`5visb{Y5UI3o#$U54n;A(Wxxa%}j3d2PRZ zjI&|NIF^Jr<7}AcFdzM`=5AdTFpumy4%p&bhcx1+bOzYDz2)8|ZInbW#_4kJTGtOJ z#_4kJdaPgEuq2rEj7x&aI2-1SGvu>jj_(&II+tqKyDshrlW{i8p=ZdaVa__yFD}0q z&slr>!DO6&=A9e*)t&ilm@WO{E=z*h*bgS-Y?ybQA)gI%65~3r zhio2&_0FMK@4Vc@dGMyBdYMV(+pD0b%@qDWdWn%c`VuPwdt-yy0lxvO=eUgdSc`&Qu-+FR`H8B}mpjb7 z6xbUd%|`Y3$bPyKzsK;y&T<{}JPFl*7~Vf@)*ToxoUn6biG4l0YNmnvw~W*7`_cAp zoI!y75U)Znuyuni9qo|IH=E%DA+z$p@4K)M0(Sf+>{%J}ChV20 zetlFkHJUb7<_Vcx{=%uh_th$G?teFegLe_aYioF1jEpyd(Q=tWU`hF7=lM{ zUHaTXW#a>#r&^bVdz#dl_^`!P(@%zY)r{;qi#P)33%hh$wEO;&P(;Q%>P0ADc#J%Q zxD!$*q-B1>4zxhe(fJX-+^#ducefCirppJMp8LNdE)Q`t?YNR==&G<)>U>zQ?z#3W z;zl6u62`$_0J^tY3xdB_um6q{b8(N@in%AyPN&vQp`T+uU5&Y=19N}c7OQd2-TMgZ zX9MPa0@G!DM4QehKYO#5_zm?We65oBsy=xw$!<@(E!2Iq8H0 zH($ALRQtS?gp5I46QiBR=1H{MT?o(ehudX8G~?CW#=4>Iquc=Y7IJMXr^FvJ0@!=Q zvbH0S@5LvzmRdZKb&R|PNM%{wRPs5?aC(H!CD@q0B+xP z1zjG>h@gsm68+4Z_3&e|9*27Hj5E2Xx&!6ESJd^?IQW^5kNrU34^uR!Q`INfcr>T0 z9zM}lPc40U>M#ciR(=!+%z*8OwmmEM0({9F1@Ifdx^6r0X+=2!DVb#tNYpRQA0Etc zfO>oXdd^@ySf{UR570XqhNmB`^8Otd{rw&5;eSIOqK^vfQ;Qm1MhDlqi3`TYAj&+O zx{*JeFF3>Ib`Qh&J$Xi&Q`((+xD{Q=aLmIz{&1o0-vhTGyr~z4YG1&(M8QZi{4+cP zmt{4cI1^lvj3h+6IwyaLj)1l~jCf`HUBcx?|3E*e?!)rCpu?^(i<~p`x z_hCCOSowK?_@f?WqW@W^4x=B3NGSZ7P^g(INqNW)j>7>kj0a zx{Uf#r^g6>J6CwM6gIdT$Pm^^?SWntWc)PP#8J0iU)~g5x88myb)YOxYU|mGAETcn zl|58=b7QXJOKFc&=l0^eRp_Aet?A9zwb#y94*f)UTJm=954~c9kze+N_`^j;<$=Kh zW6lA#X?Gj=m~%{{_x_03a{4JCPuk=yX-yvjeukajYhtQ;peW?#msWv(_G%4TZXUJEo z=;p*&v7o3w-Mm>%>qO3hHsyd{xonS7-fRq+vuxm&%C@%N5*1!rLG5IACt#n^gsd+$0{_dROer%(KIl27W?eS^TPJf6ghCm&48f4RKR z$TgGTb1X^W80Qb?a{lhH`9{;@Dxo{#h3BN4QKJQ;Q#@WnYLxZD|WCc@1? zc-|phzc}*1UW0{gj5Xv_!t2e~y`OprZLR)r7h(QC&b|gd%Hmpob`z3KM2NSs#uj9e z53^~cu~PbOHXqBE-DJ^*N(x$)#XyDD-U}7mU9!t=VAaG@Z&2(FE$ULC0*!5`*oz7n z1S42&p&wpUgdo8rfErLVVUxW7=b3qTvsu#K-v9o7o4h;ke4IIR=FB-~&YVenx7VO= zDfcFp#eCR|@n}+OcRgy5pLk641lk&69qpblK*JZxuy$%gubuU-e}Q%tzZg7E41V$X z?tykbJwbd1PvyunxQg#y=p3M_|2!J+!=-Z(_#U1-KQI?RzZ1B{yKLcIdGM71oPIvj zUZOWa2I>M2ZT2oX_=>#@zw_kprpgKDSNihK-;H(GT)nSkJlA&k=bF6Rgmvj+lbEyQ z#g*PgFW${`@?+lw`uvFDzVexET!R8LzmmXXE==G%j@#fh=)&c4TxI$`r=&aZCFo8U z=#LH=jxiZbl;Hql_WCl>%CQ7Kum(MH2*0@oburBYztusqg@K>1Zh;RK{0WgC_hiE7 zr_bT7fv+U}aRRp73f`GG^+{8H=NQQBTf}+v$2y?@97B9T6$hWbPcFDy82;b6fQ#|f zoy^PlKU(-WWB6kKK4-TFg9KO(8TG*T_E;3@o0*P0gdeiV&$?E>2H37)j8@$LFinr* zuCMX-^44bhW_7|B)>t#u{RlW$h&Cn~ZQRDXjI}BizMl1HZ%fBPAvTtZ75OBt&Q_qwBxDEl9H-j6q<)X_l5q81l|g*49QwE}Fl*Yc;d^qq-F6TB-(&apOn($KQac(kH@I)kIN8{r zza!RH;uZDwmo_`GCJVEw_lw(EFX(16`-r-;mM?LN=g?Qg>fVr4w$&&TV41nc(#?+@ z7xj<5M;!GHobec@_C?o@6ZQD6kmCjUDnaK(Y3vC++|iA9ra9rXJpr3_ihp%Qo6MxUF@7w_lgF5i9nt!&u1Ig=Chqhp-;FsoR1g`*JiU0z8|8l8UBY|Q=oHe)Te52 zKgqIkfp~B9m4z28h>cw>EwDlQ{YpJGZFMr zH*7U7>@!%6=OXGsG2KVTl+GR6euH^ENayLy#D1|wG_fA`&v)II76&X^o-b|WN%~MS zeHQu(A24U=w3rddbyZLv4diN5I`iPFQI0(i(AqMWUs zlK+t``;pOyAlE(O9k!{|e`W{pKRC`y{df86*e8Zv#2Zer8gMTIt*U z-Zv21it^$2DZhBhPdQg&cm_{sO2(ZxJZ5~b^V)3Gzhpf5 z7X4(KiEvI1y$Bl#g$}HSzNV;l=q5DZ2~@-}S7;GyIvw zq!Q>C3(ePhtG;PfdQR_FV-DUy`5b!1^~Ud6@b9Icl74@ANaCq(oESY7`_Jo92XLEH zY{uNSJh!v5Y~_oUKw<+uzS|BUbJ~0VKH`J3cG-j10P1%zgt8!%N{?={UBr4fZ8De*kL=W0|18K6l?`orItD6IVEo3m!C} z`iOHsYHYKrpKiGubNBiBm3W7_$9|(N&cAhY^|X>IU4$O%mAc#6xhHSxuhwsR-=E@y zoUbose*fZ}3bacZ!W7&#qX2SmfxlB6qU?$LFSLNILkpC-<-9i9iTsJgnI8V-2eLgsz~v_%lSb)W?{9Wx1|I-k7I2&$pe5OoZoiZHh+p7!lq$PL?ZtZZ&oCYl)W4Yi<1DZE>n8_S z9~WKJ#e>IAXIJkR*5FaZJJx%~5}&}?_{6+MomaCR(gtwZNV{tFG2sDE9T7a{_Tv1g zFyKDNe9Dho595)axIHE|H_7J^ui!lA0_UkQ4{Ff%jQkXwfoWD_7g~z@i<&Wh(fGYN zF@86SvXVjLqrh{v%QYi}Sc>2`?3*idWj)%E^PcpEvFLC||Kn9WO1dFmK-%9phmCr$ zcJ?n}JDhW|a|T-r6$5nr!+y$1zieN}wORG`|x z|3boI%7sp1p-sSE-tP=#d>j7dm+q7La}l$cXR2OF+#~H@szpX%-cs&)e+SOAWUxJ~ zMJ~=4Lzl4N*<9&L-;-|L$Ybfb1JB_7@)`d* zXVh3=-God$SO4*gl#9{+B+eN{ul^k|-f7U=fSo=~+398c2*XzI-wNJH9*OmmF~AaN z$%V5q-W$p|cgwO8DZN7Jzy6wtG;_T zr@c;Jd9D5AWq8l{pUb>Yu5OVy*z<{!tw4|Hi%HfWvcQk6|IIqo$^9jaMFRh=u3%kK zmTEsa8fDB{$fEp;Acw`uK=wO?bCWpV$=D?N0a=D?NBhaILI%G=3#Ha7J8%D@gfbgs zKFmero2u?UM%-Ab*`f=t;Qj89bX3DgpX=a zcNGLX(|4SS!XH$JA8R!70_&$tSN-6sa^J-65y9&ao1VV^5fgj{Q?0?{r{UMyyF~dz z8)@|a*FV3)FH#$W&7Yml9W4rg!#C`uuyv~(`ndFuk8Zr0c~G7_ zs@0Y0wcre(rFu8kPW`l$g@Ud9dDS{?+j@b)w~n9)KPGI6sJ-@8w6nH-(Utj=$2o5~ zXAvJFB-fhgQ?otDTr*$EABbZNnIe6My$omEu*Xe%sXb$uwDH)TkjvobfZrkFQaz{1 zcj{T$k&Nefp3+b^?wRCy%(e+UKd0uT8UBVD#=h8v1#{@1Vr!Lr0&;C1VM3pdc)w?w z7JLSA2U`UEKoiScT&Cz3LR?GRjo=?b@q!Ci8vS~Yb#RUlf3D0QAH`38hMez=W7=)# zJc)8KV#9KM$-nQQV4Nhm)^$$3M8`o)+36+^DrYPlxBk%s1G2A$-IMZP9Fu|Mmx}lsZ%W{T!}cqzRB}aK7ovedCYSo z$uhd%9i1loPq^LshlhElgK|y?`Xk`HT=5CqzXtz<&yZ8BnO^OL_zC*Vdop5YwreR%;2d7;-mCGPSLkz6d~Or; zlBt(_gb^1VYaG_ABj`WI;Ot?XNn!sFp#Og0<8rJi{JoQBz0qH}A0lpn6wis)Xyk`e;IQLKJW57Ev5vPdf&AD#I z)6Gmb>=L7(AA4)23V)@o0dxFTFYUuUz^x%SSy69}FVB?ObEjPIMpfE`v@KAlrkuq& zg*eD(Sr6?`v^)81xT6)eC)yuCkJQT!xiasY2Ndxm@1USvM%fefsQ2NKn{anHVyS;S zTirSLqfXu}LO&gh+tjDa#G0pVVqM;sMM|VjY=~?Xo8&qKofI(>!qrcuz0P?S)Nk|_ zHH$I6((VzS7LkrPLTy^B5FW%HkY@|@&t_KNj&mt?*sGZzYc${cCU!i+vj)K#I5W%h z5Bg`LIS&S>b-hYl1Ha&}h>ITWz1|}fZ>oMOJu{*Gkw*K}RV432><97;t+N)f#weCO!x3M?V_xd{jS6Ir0}QYa{2oF;8b1^I=BpE^74SzoecSZIEko%pa!u-4x*H zbR}(y!0{-RXZ>w#kM#k@2wUond3M^ZpMw8TNbsB{bzATQxt38^ zf*y_-V7+giIc^d>Yp&pKlQHjs-wg?Ak3)a9@V8rUt21DHTeUCgH3T?_FE2piC+@4p zxE!39uFU@^Gwh{T_!Wd33h^Ux8=C@2h> zuPlsYoBG-$n-JH(fG=z+g-(&V;(1|Ru@3imEcrz?Wf>_G zEPg$kG)~08gTG6F&1+qKQ~91DWhX7h1TBoYvdiy-+108 zW#VR$3A*|%V8(ja{da&)n&}5z&g!x;m$3voAkz>B%y)XMUVD0~UVnOl-f+4`kDT79Z#lhJZ#sQK-+9KQ zH=h}+A2>5rKSck~GYj-1XKM7;GaK~|`WXj+L;4zHufPfL_(=(kRJ_K25H><5o)H@! zyegG3k3hp_L!PSX#(Gbfp>sSA`2ML6sw?x}3*`L<&uXw+2ir7&JMypdSdSZ$?1V?% zQG386uty!n9kr=~@dbyI*8wkoi^U2APg{z0ce&zepPe&!8s#a-EvK$F_}ClZV~|@; zJYl@w1$%|D&gx<4Is$f-Eb8bTCk@>!+|~{L?5*DV`+L#f(5(l}dmP7MgEryLfvMOx z+4qST=TRXiJ*M~{f z{|kOMVSNh&XCcx)bso|He4Wxccfr#S8nji9`uwX;K(@ns|I6=Z^Syr)WT1_dC68Zi zAPm6Cnj{?U1Z}sHKdHNPmrNGwlM))Hc#A7@@$;o-F=BPj&s~TMvo}8nep>}n z7TQTzoU2kAWt@@|;vvxcgP7lIAj>ORLvNR~K6O<}J!F=L)EK9Pi6i6L%X?N<>(6!9 zO%)H?$H4#BQi1);6c1@Es8>`3$8;l(C}m8S2KxZ`dCH6u=&~iNV86<#C@6gX)F*}O zJ}~>oG&_kajy2#|2RIt!IxgkP_WO-Fm$0vFUPt|v{9nd8Xo0*ONj3I0KlpdZb=kq< zi*ik*+{$$ab*@9->Xi=A94@oxr$GfL-m=ZiK!ZK2?pe+KKU9hxfA)Lq^Ipa;-xBc)uNO#N^-&tG^4sqT5II z8~5nO_l^yi{s(!bZU}zUih7r>sz$uV8>v5-!iBve#QVHfo_xvson_aRtoZSD=I{K- zRI*~F3Grzjd7f~t+=p{T`tbnw4BYzB4A_SSZC$W2Q!e+g9mcewJqP{A9Wdu)|EZ^I z3q(W5>)a$R%bb4H|-S-m^Yj=TQFyu-1@O6Fm5J{TLaJ5@Qkk7OX!(kzg?Id z$G`(Jg~bOQ0z6+SH11DDbh_Utb$`&*;z zLg0P>iW!~nyY+W|NL&NAyIB|bg9&sEy!JUUN7fbnN@$_Y&vkQNuul$s2lnkh*vj<> zcXLdF@er9*lp7yP6z~v3B2lONDRj$m1vlOl_-Nz*#Q0slw zUSqvCNu8L!4Y1`<9?+=2VeJ90h}}bXfV|SJzyF~@H!&SB>Hb91Ja@Ff4lv}tx#E%f zk?JLBR<(DbzX6^Ra_jG&Gh~8Y7<=e@N3S(X11ZwvJY%+)5$)ty+|?j`?@F2Ejm2zP%X z_Bhv;`|1RBZOZ`9{Y=KBq&$UrH6wqV&O2b^@`%(M!XLZ^oI@s2_fIK!f4Z6QV!pN! zE@RzlOkB&sCu)@*)>p}XgC`t-zP^8uPK&kNt$%za=3OJ`T+WAqb?$C#-^h>j!GJtz z!Jg4P?DyY5x=4Ob#saSss4?dr;g-`e5`F7kjoc`0};`X2e zwsH970B6~Ds|oVvG2{`4ST1|vIQNp*;ET$w(y-=8k`RTrY& zf{+f`685=(P188f;G41-Fb!VPPNjDQ zWxS~NuLA#A_fM~9|H*Is8Li}L76ycBkGSJm}Jh zfAnGUSeqEbe%x8^DhZT3HNbH@>j>KZ=tUpq`7>Qy>lm}yLj2e>6}<+m-`u8Xp>_$| zH)tWjA8LMuJ~K=n6vI~|jtibmut_cMyoX*+x~q3Tav=EY)7iy$!seG}_|;l(*%q_o z$~D7^v9f-=ZD1ZoUo~L;P{KM!q}Q%Qn}cYyKTf0mD)?U;vBlLsX#X2(>`uMF@)(OP zp_dHWg3h9a+y7nBR!6b%z82Z|iQg%ln<9p}GViowY{n+a=2L4-Jb&{o4~&A-T*`4qK2T$SL#j^n;+4$`v`S1v1|jLXO;=Y)bI>|+@$W<_$}EBct^KkS!5 z-??W_iY{fx6;e-O9iXY5?%q?9UMZJAPT!g6kB9iCw0X3uJ*@AJNB;{9UjK?}ca`iv z(^P-j4=dPC$@amYLXf|{OCDFNi`!!PYLAO~X#2d8_lJZXeOydl-0`yTAU|YNk!GK$ z_iGO_#-oPw0Gft5Uh55CRU4S;nuK?K{s*ks3sLET39d=qHPD$J6lUgSKE6j+VC_KcBkZSeetb{{f3Q(9J#GW~QvmCfCx&0Z{T6hyRQT`+DPKvtY8ys8L*AF? zmehE4G^sXEcB*$e=RQAiySSQ;bS>L$vx6X z_%HWU7sed@Tc-p5ymr`)Ae+KZI2Y^c_?WFr?r(<;AK~7UM*sCl;jx%(+-a6NZ8Z3I z4>}!NBE8TDhh& zPO<*JVKXw#sDW&Z`Bm@B+dBf!YkYa;5s;(n(bxLwU#D1CY!&yi4#-P#ZyS9`wL9gW zlSyk8X1rTUIKJ2K?ik9>{mcmEyGe+ys_uz_!e0b}AGdA(bI zD?`zF|75(!??2BpevgNqe*v<`DE1xuEYt91|LQSzSsg1)mg-I1!};UbsgLxxQQmXb z^lyoe>2=sAq~*>__cm+=`izKd%#>j%zc?6)3_8*%E^Px5~01Nq;O zGKd!1Ae=uqC3N=JzgaBzhdbb3d3cqwt^RQt)(`j;LT`YqT|@g=4filAd(jL(DWS_i z`wxwizPTVn#JK2M1)Jgl^;-Rn?XzN!6?%3h_raFIZfL<7Gp-HeS96|XEPv7|ZME5m zYh3`j2KPN+4AtE)@Gq*|)DZw~dQ+@C=j+yAkKeCD8HD_s?FZX2hRuj;7*YKOEX6FR z${g$&uruh$AwTiC?#i44n}0v|5WU?0rLUDSe>+&eTYs~LV+fcN>}QwwC$?&l%a$m6 zM`xKk+9YLB_78k92Xz!vPadM4WV;FLS!&dC3&vq5_^uUeVm8*sEXQ=%R6|Plkp2VV z&Re6ff2^G<*7?d!*H+qoF%Qr6m6<6kr7u2o$YRV>=#iU>t*xmGFmHK=XA%6yKzp_B zXtlJLfai_^f6bw837VGsg9EnW51vrEX+X-ntHGaF&&M8B1m~Rriw60@l`(#TN5BrZ zyEUztdx^XM>Q!q==iO*~%VQfwBXqiulJ%+;uYVb9{8RADLwjg>yi=**d z=bD1?$-?;otj%g~0c$7T|M6$ZW5qKPv36k>tHj*`(k~A7Q^XoP0l(BR=5Igz4qubH ze-gbU!K?E;(OgyT_nWysagV3c20NzepQ5!P`zp%y(GgP-{*ix{bOrt>;149}nmFLRM&Bb$XPjTW zdmfL1et@NS;|u$K$@{od8tc2`_Rilr?59^as?XfyfX%<~^E1b(pQ60HOFdBPM!Z_u z8NVj$=m_q}#XLv{jJV_ds|#4~rFc&}I+#}V3N7+)ISTJ*K5~h)$)NKH%VEs|{Tz8y z$xq%Jy@V>^ZfEZ4g%QhQ-{|rT+-}mR|TWVCa4KO1|e9uYCG9Trb{oM}Hz^ zUGC3ge6xs8ci&g!`avIh%1V$SMzJnAXJNxexl@RbdysW?=q8cy4*O`QuP?z|y88ym zjvrty^}e%F<_nqx>Nm^r%CJf}lFH3R-uqQvaUho0-IpiJlUL?ngT97L@Izidn)ymz z(W8YSky`jxTUwz9el5&XLVTEal%S7}2=X)wbK$zvfAAMKiRwL&v7Srcfqjw+d#PxJ zJb5GzL-=$%@dP^Vz_+#ZS18{Md(Q4NUvl@p{T|x`-5v?zo(|ehu@6%#_sNk*Yd`aS z_()!*g;IB_vH8m~oGpnmMuX$nnH7*xQ-iNTw~3g(h&<9K%&q_B=2F|eiD|#w6N&zf? z^5X6r1&g@^c??(%?@fSVF=?YD6v90D$=BIdp-1Srt9&b;^R&uelTZ_n<%8 zUN8r8&%6g-4E%#mN7ZPc^$d*wL^{X2?C? zdzWQ|2;+G4GZi>aC5{;*j&do--QD{?a_)5WVNDqgTQK?t->4j{U);xJyx$OHQ|@O% zZhJKYYeTBmD%U12W7ny1KfYGsF(l_I;56AM!-*;bGSXzyojxc3Ys8689p-@AGurC| z4lFfy4Z`IHoU@Ya8gN;E8hoDX-(&pSW2|B7vg`xn>&1L@S3X)_;B8q| zzVh4YJLs1T8S@lTz7kv6>_OA&oP^|Q`fb*7ZlnqyUz9s*|7Pd`mR`?lQ?$7Jx zIvjocPLII2W4=L_s@EdI$$3p&xiUuC`3`Y5LXfkA*l(B@^Fwm$`!^bVYzEgeNgpAo zak@i$uf8Wmj z8*Sb3&)Z6zg9myfZd6;L5aPbO^?eikKORypOgjGp|LVwP#$Fud%U?@bY}Fpn%Uy$H zv5+g{OX~^Wcb;!eWBmU|A-Al2{xR>P8->5{57_U-+;>Npz0Ew(4YCoRIVOX7)m~>? zpUQLJ)y!kJ3i11uVqQ;)@Zh_6eF^T&E1`_kn*&{A2s_yqC6C8??gYMIH!Y)Gji2}) z8_z;fJt&Y{3LnxM^lvO=NAQ3S+S%f^wa?p_PWwFC z*uVzxiJ!rZ>9wFioarnZ4gF(5_g*Q(&64Nsc0-qkY~YUWd(VS{9pC||FCu*qG2Z`+h7t$Y6+4ep@!u+%yqkgu{Puw@gmFc3+a|Lw!^M>5Ag}6+F3;ZgF{xS(Z zLor)Gl3$GJfIE7nq;c;`_-DXJ)&KU5^q2NRcJ_15@_t3EIdPvM=%4uA{HkFyXoOu4 zzQDW7&}Z5ZHo!(DeRRRA>NWZyH)DPq`*unef2~-}Wu5y0If;8{;E|F0%5aA51o)*% z?V~}r&dIgIj|OWZ?lE%`SJj9^6PS2lO0m^6b^&7P`4776=(Ba{LHY*kJVz+)3bYfz zpC$xbp7k;8`&Vx;UCT7wWA`b3*VE6&SGGEz`(ZUxMHufw_rXbF?(S9BK)2XbS)Q_z zx({_8yst&RNM)J8os06mwBd`&tk8Re6=hN@^Gvmj@eUp`e32~``&!ViN|A08Y1E5Q zcFm%)dbzLXh{kLtXD3VkA60RZVfUmzwDkQ0{%hqqK>h9e$uCvD|By6=^-KfLtaa;q zUf?TJ5+xEgs??6jAzFleEH z_rCyl1?2C+*FW*{96oVbbr7;XaS5AEr7iMBj>RJQDXm3*=KU&oaQ-g*hiozFC7j>S zqYsmCPJL3Hd4)_Sraovq7vcFA#xwFee=g6ol7}mraY%mNh5S~=0zy3v)PdmNuXG@^ z-J|Ll#kP&uJbs_;p=+U&45uIY)Srv&j$bI>=-~eI+0l2A_N2Z+(j;I(U81sVWPfG( z$o`)B>i7NSR<@M|KX)_fi7?ChK}Vox59SGd;8U3nSSwf;{>$;h|1}u@W>@Bn{c_~V zE8_mrGw#Qj$IhHzGTvx2=69Gh#sYqaS4bNlW5aNqF~5e#{0?~@m*+GATUU%;c*h`V zfV80e5dRb#f9|hi{CBN2cm(&?p)>9bGUPo>SgN5xN)euu`{is>6roS*oa zT36=undIkeqoZm_f0X3)(*u2bnbNsFfFJ#n2R*2e_MQPfa zb$u86W1V?YZVl5${fEb^_sKYt5m)B49HYUoM|~!G52=tp)bK1#Xv`azfPUa_lKLcfYyvXqKX3O2_O5zfAuR#|AclV9Cz;ecz1^A5h)!+FwaR}!IV{`2e{BETl*S%MS z{I&wlyLla3A>VaEN5fpuLh#=^1;1aUbw7h~k3c@m7%AxxcO@Lcenl-}(S!!(#V@^q z>t^Q_-T>JTv6@!gz`3F4qd(;B1&$4nb2c!o-<6-L_p=YqRei0nr?m!;|3&#w9!v6} zl)Ccn_qe8lhNaJ<+M9~`P#%=>FY3acL0<#-GIY{E^nfpsKjeeg))Q}-L-6@K+wJZ> zIGXrierc=K|2`AtJmBB(q5N-o{tM+e2aPfx!cXvklnu~_HyooNU-Nti??s9(+ReI% zCq)|<*ODKqclSsh=^u4~er7rOqf~#fHmpJ0pP{a>77=#94`ZZ ziU%lMCh~wD&@0Zjr+-7yYt`rOXoFmff^}lK-H9_25%skmnv6&+LPpt|WmUspsUmLs%RGb4Oq9i2JL3xK!EL7f$t{(!+7ro)CjxIYGqqQwsnx_evJvOVC%KVTPn{)OtT!b6=tU^|KXN+CO8 zyc93ej~ad7%5spAnmET@nT~;VKQ1RZxbDxE=k%bzxue(L$uj=}9Ylcpdj{y>=LcCX zxHX9mw*CV;C?1jySm)wFbZ|0muT?ZzYRvCfV4uTy$-ROWo#lQohz{U0Am?WIqc132 z+-)a(&`Ww9?_n&DAqGV;_*_YshP3YLw=r(gMhRMY*`NiSQ&xBFeRQp&g;(Xg8%zsT zGg)t(7TyCb90e`BgqSE86S?++7W_}XlU*7K2`}g)Sn@}2{ZpbB?-eZsK?~T6!{``% z@3*hUX~BR+(ZWYRbocImfbB_I*bkjr`iVUEZ1q9V0&Fz_8`gO6MoA0TkPc(CaQ71| zH$V&adi>-kE;GxxQIP-QHUoXT+_S-aEim>JsiSlm`-$W6+{{hb{u^DsGUlaT8U+kRH@qO4Ks-|W8tID%onHuyKa}IEE6#p^Tb=eJYCrX{al#dyE^=|# zTLJQN{x|;Dnp8X-)!k9saoZ`hJKc-Mdtp8JNk~hKzA!E()Y(4Idck92sWY13S zee6!^^LwUN?4gc~(_D1zW)B*G9xVMCtKUwsa@>PY;w)e$_eVqy-t|`3!Ny~&KrEe+ ztxMoDnn)MMa|{mXQF+J9kESmW2bE$x|zkEp?*Wo2q7lLw&XmD z?@Vrsw`1@d;GanMg<1dkWHOF38Y{&IW)FJ#e=FLU?qk?TMD2fgve zqa6W^E%xZNR$+E!e6(A({XN-&fs>V#JA+C5=BZy%cTI4q?6-@Z+pyLt>r* zfAOyw=NkLha*r6k<5~&$MeGb#$=!IKhVQj_hyRY&`1vsDJK~67Z|(C7`MBGCXW?5P z-&6Sf$Ile5`}psL_kG+}`0cK=!W+6KI&SZGQvY=v+ia!0#Iv)LN-+*AF~+@%q|U?r zF7T%Y=c8BdApAwYf_T1pN5NyAKX<9|d2T2Dc<@fMZWg6zSKfzMzxBE9;O5){EqtNG zzZrIR=@Yl>9J(&>e1*F&`+h#-EQ;3s{T;cN&bzGU1m2FjnlYy$DXnVmo!yNErUU*< zv((;*X{S5-odD}255BZ6#1(&qegc`fsOJ*j3DmU#rot?Hk=`4~;Q2sZf4oejP7``B z_(X(r7i)mhOL(rW_g*P0!`H#MKk|pqvhA88;jIS^k#2CN!35v-)U|gA?^xWG>njJ3 zf{%OR`iniRk&p=z^#=A6ZDoE{_4DH&rJ6;`f(L(qIpBWtePyr)c)t zH~B2*q|!&?>x^7q(0^lHfNmn&n5^1>d;ngEbuJ6=$QbPKw~yl41lU64yNP_&z@sw( zkKh^PsdBxY1HPP%xSUg#APzBo>nm+RP$ zbzuyLI#ZUPM0*^Q+Z1fD;pzSN0xy&&wrCNjqzjite}eVVhQ%>*>rWq+ZGP{Uvdy9p z;^{|=uVf0%H>_x4kaK^NtG<8JbF2l~zU*xq*VuHF~MGT-Xsm>baBwBsVjm?!cs zZkK7zXuf}}6#MUZN8T=d3EI1ELq6#bLb=>Gu?u_2+Y{oSLATK%lN{%qm-B8Ed8W(s zQ`tY%=R|#StuX4VLO#Sg!(14sudL39!RB0*F7G?nno(DK*S{k#Vlb(^Xm{*DzO*j1 zTY+|6M!O?9x9G2fwr2wb;jnl4SdWZf6%p{ch}F~dc0xT{0H2KYhI+0-o2z5_{4L^; zx8<*?-j*rwTWs2#P-Z#GxQL_Vy47BLlP@8!i*Nx}4`7{Vz&e8Rq|5a1Rte{^u16%C z(l-R-G63fd3H9(Cx<}$LBCZCU4Y7O*&PKp_=QPN!eFJ5Ny@4|GB%DcgD>#35I3X|3 zw|O9c%!40cF3-6cGFLk4$RS;6;jX8Bw!^D>AV-b1;oK5p9%5cEb@%>3$^I!A{KrxBxw?Lyr9aN zFn`Wfz*i@fRIZYIOqE3*y!VY;osaS%S@&Mn{oDItAFNO6#{zfn^Ky+a>SrI;pu8Ad z{}y=%2U)2JB6ERO_S=Nm@ zZ3ezfTKoA_;L9?&&SzdV#sd2nsTS||U>ld^8qXQ9T#s^Jz!=vjm6PMI`eVLczK5S1 z%lanX2|m}j-6)?8`kBC3a7pEFR_&^oZwtR?lDI%QpDfo5S-1UsE^zgAEo9oldKKL@ z-o|o8&GL?*ugh3a#(R`4hL4tse8r&cYcLL7Mt>6cpQ(#Hq}Yl3=dGf^fw|#0cD|Up ziqE#Xjq9;pfIng0V_%^GFpNPx-LQGrvo5sR;&0jK^`&*);?{q2ux^ZTR(+MJfxh_M zw+QCoOpZo9LEmAtlf`!a?`_ck=r@ZJ+L%048|b%r=zjmkHo&JuH})v>M0jAQO8x(X zY1`oem~g&#h<<}7=4e|n51_O8Ba==`y-V`6{qxp<&h{nD6Q46uf4X3Dv{LThely?U zys;%1MT{M$>7N`eYHt4rJfgK3@h^@-cEB7Gl`hL?kFz}Z?Uq>mMXmJfJpV(qjXBx$ zA>Sv!9@1LQuLJBMzG)%#iVppe@XOwsm1I^P;Vt32niy@`>H( z)75+r8&kYrTFWO3SRS%xv(cWC+0YlG%|rBmMY}v}hOb}S}_qGt>x2^?&#N^;5)X- zI<)Yo*MN6IKkhrqG|uy4fxNQ5Pu)g+67MlMD2G%Z8`tKte5&edjR?^0q3UBN%-;6& z7SgAuo%H$XkAlY>F+4ql>dOw&ye-loBvTg3ze+==^@l}Z?RRC zC;j34@QuJ9=^uJpTC3uPYoi}JD`oF@t8P5(>z zWkz`yX|c39#Ia|)X5LrGcB_twu(u`FHf$OCGlnl!Z#K#tGV;rFlS%omRCPzsmRdu? z0lKKR;~qWm!Ol`sQ+3+vw(4tOlZSsA+LL$E`wxCCd!UX#qOD&Wdo$53bNA$dDJ!a*oYDCv%DATJag&CxwP%dU1-iLnZKZ)zxp&5k}r zhTYDL`Xcyxifypz%{OhrGkiN5W_kqq<;DG41TrdyCl<$(xW!!mM>=`TzR|8|?94}q zJ7_ucOHn{u4Cdm+)!!9PE&S7cGS=HmhYH-i(?_!{@c7WbP&f3Mv{S0?3-=;ELnC-a zN_2q;;k(rC6y|{CP(h#^U(5UCxjljM_p^gb-lcD{muKw*U&ily-u0qi7}L4maMXw` z?%r=n9D*-}D4Q5MbC1@3!KbxccnEpT$a^*NNuDqBfv?!DGRF6n81F35#Bk)p`f#Da z-FwTM>{Bcs-dXStyzs&{yxX4iPT(DQ)`gedy|d~P-xW2Zj4Jmw%K7+?dIb83^Fhvn z{L$dm#jtN+JOB&r#wNxXfN%fVqPykZzlOF`$pif@&H~~b?R-X=7i~$rgBJ>i6ESk^ zpMj5Ny~j4B?WvUGY=%8*@!y$N3VEsYZarF+qkl|0Udb!U_ta7S*?qD+d?TuJgthSV z&mx6yeRh-bRZ4;X++w+o!nSu2?Ot>M?+#SRHXOHq_p1*3qia>(VP{vStj65yd>imm zh6c{xZU%fS+|h@hAPh!2@c4EU(yNjFKvMc4q$5V#!#_+&XFVaL2asNwkdAuZaUvb- z^~1#p={Vnf#DjF~Q$0K(Asu6TL?C??(k~yJ{!RtbSG%Ki7ZTE;+r6_C>0zWxyc+Ps z_v0Pb{{YgZZaC22BUwm)5b4h*)B`w=u>PMSUE(qU56q8vP<4U9`QM2k{UM|;8l3(% z+r^o+x|j~+wMZaB-^oNl?Iw2kSJkpHxpCNtN;Pkgy&m)NA@nJ$b z@b*p!>HmrJzYk7-+llmgr0-5h=XeREBd$*EW~M{lPCE0H+Tp&~DeV}(Z(g+!J|!mq zacz?SsC^RrAbQWdtj;Pi_9x~>n7rB@9bsZ!q&uv|ly)lu>t)4S@8RM=!^{p-olV@_Dwy`q&2QG|pz~J6SL5M;^00528n6zQh|k-h<5D zf_r@&!ewm{HyZg;ye(L>QD0yp?|f0PqRwFoJ{|qmJ&;wf4|chfn{i$=qT-{Pd0$}; z-dX&mxW5bb{2Cj4eQY@u^!)}78|q49dN%1TQ_?g0!ZLG@VgKV8_dhaUWj}G|#fftv z1=O*)2ZH!4S;76|;8TVD>IrV#O`VnH8`d>~Ybn~wl71v*-6Qyp@M+DEYx~Ni%uCpj zhc*_Aaee@}-MozLFt6ONkAP2xNY}Y?eqb%>B5cOE{Ij)cu@)Jgr`D5BoO$fL1AIry z7JoKMfw&c%aB;}hJ&}AfQ|bz*z7uZCNXQ}2QS5BS?ChiRkFXvUWpcZUdhy$ zi{J&H{m$L{bPwMf^Gf2CYbEL(VZgT&xUvq!HraPd&Aq$jTvcdVGY|T9QUgmWSxi89iZm@|b_YwB0;WLdso^tE$YtUAKqZu%( zz4nC)o&{15GT;f*?}+K7dybXA>OA6|z%CrEkn6EQgI2_O>av?4vz3Wl-pLxsEze$C zrtf@giN1@z81%yk9RdEXDZm|gdL!0R`*WEi~EBklmIL^OE|4ZVM^h2IO z9ZK?w-kT)NVXn@DKiATlcx=V1noyJ<8Mk%LlID_%CDX2{SaMT|Z``H({RYfgystGd z9=6DF@KablF7X~jSy}F|k-ChV23wit(e;}Vn8N4=%qz8;JK=!95(p#d?#=bWU&FV$U=;(Y^o~2(fj(dD=+Z$+U#u&9UD@)l)O> zS

=(zj(6p0O_Bo*J9p?a#<{&LU3Igo%CTy;FoMc6aBTt>g0%Q?Gv!ok#0+&nCo_D!_U(5@$+qH%<02XDfWUBRBaUnm!5V2~60d zZ0r0A#xlcfkz>ie(zg)zRj{wbM{`oUi-~5?s%xcP>Eu#N^N9VJ!^9xgD zc}Kth;jwSc*@pj{bGzqkPG1?Akc)m?o6We0XwMo*ulOzaK(>GLbg%!J*^KEd+YSuN z<@(tr5h@9-OOniT9q(C#?s4`p9+O1i}T ztDIZDGLO?|i}3!|EUuq{GQ{@J_~i)rJmuIkjl7`oWV!-x%CTn*kFc&{=j*i#%0qAX z%F|njKkjMUZR=-XP3L}nipqPr6>!(8{8o%z#UAEgRBo|i9IY&8 zw4mI%bRf?NtEy)K@`#Y3TW(2sZ`9$LjrSF*j%LC4?8g8M&Ji&ffa_2Ks&V%r?p~h8 zdkWUm_9tq}(U(kN=A2xXYsDF)*RBQ5M&X`vIZu7#(yYM$a&Oh)Rcr(4GH>O$H29n7 z9f@^=u6mO3>Hp)6-ay)PZ%xXxnmre^Xak%!D{NyjmYvg|OI~5pI>$9`k@xJKa(J~) zgAbwdfG9nkdzttGe8;nj&HBf7%R}>gKfvC1Rbzc1_x5C;GxSLCd)D~2;Xl7T_s4H6 zoxW)N?n?R|#qanR`lzY+TJo=BO)i0X(~5I|nX$Cvl<$VC_eVYS)96-b2G=r{3w%F@ zjNJlXltFhGvkl;}F{$nX_%SybZAb8alaa5L`S2V2&7f)ckSE~V6aw(EvE^Jg#x@0T zMrvj>Hb}mO^V1yb&hdITc?@|l)*HbwVS3H@#(Jis%<^KZS)g1!=!0w4aJv)s%bB1V zq!ruDxT_F+dM5S&a7Pa3e9uhi&t;Yr<2eh@X~r|=rzPEPvw$vfPcvu$d96JYL@wcv z(aqWkvTyb1M*M@h10OB@BJPIW;ID$u9pnisV%>SGMz$vOUFJt#OI=1|M8?DAoE*Yu zvZE8c@ta?gbA@*``^x9TM;>$DDj4gKK5ZfP%au9uZ$@4x=atI)8sppNc5D@yq0Ywo z(80zA=#YjVyV&aP{e_gfFg`ohr^>$YF13pZaK_JsQZ|p#i@UG*Jv7O-2hoP?lz^WR z9@v+`WeTNk`@fapm_arYlnq^(BbMQ8%vhYm^p?CLw8FP`IMq2B6ZtyN$&kNk@MD7A zzwCz|fpQU9*5vg<9_5`(y=79imHiHjquv#@ZoGo=T~DX*@gdd`RW_{de$u z#Ev_(>~}%t#2F-vr?qpeY2M&7i0QTNi|;IApOk!f^Zk5Q@}238lDmPA`_|7O9(u-_ z__fsBDxb6PY{gpL?<*5l*k)mqy;N5qUx3V$hJW9TWS%L=U`!`=H|53n5im#GQNWskTFc&kPG&D3>!7QoE*X!?$BlW4`6hOIKM zU6}CxKrnqDVx$&)P1^l#FV7C{gbtsFd*|n z_E%%?^}mL(&JNU19;WRziIp=Sll!3l)D*AF^nH_jztJSLC&a%|kD%P+-8{1mwur-> zfX{!A^wpb3+9NEiAN_j?{e&+2^UW-O<{0c^pf%Vgx{SF=925cv!;q(jd4Pj7;J^YL zFm4#m#P;_F7Jk=jz-5WSsr*Ai#n|_7)q=K@N-#w%Ba(N zdMe&O0ewBO&Tq5M=(9+_V#Ulsb*{%e$SQP#&J|D30WE7t%R!pC#M(qUcXpag))hCT zw@?-bf74c!j~Nd*Jb`cG&a&*xJbT5r!N1G&Bho%{L)R^6 zJ6qc*x?}x;EgyJ|-VB>cH~U3+S5AyD;-K^gMzKpX0(svQ>4%baVzcZxmYCPei}$( zX84h)a*CgBwt^No&$o0eVS8VZe22CH?5oY;Jf`lCz4|7t#XN)a7h^D7+5=k5-$A*Z z=(E|~tGz&2V9(luv_nXfHZP=aWID#s(t$aJcMZi>?D@CKeg9Nx^Xk>)+``^-2=9-q ztV0|V+%2~98Q?K(A8q)s*jF@<+^9QdP?-U+Z zs<`ZE4>tebuLZqfZ>8`GmII7(4@9oj$mi}YioqTte9}e$p6BkJqUN`%t4GyUb%(0U z{%TTPOmo!SkaAZbmb&(MQ6wf&AR4Cy-Q6k!)wLsX+1kqMz|S>Y^XA6=PUH z_;V+UuVOth{7G+=eNL!f#wauDmu)BZS=H;98f$lOpTF*7dO}_QJpikAI9A56d*rIR z3NOUclj^!YURSe}GcZoSf8D@|91Hb4PvQV`5_j^MT3wlG`2c~R__;cHhGyVw%d(Eu zjm@Dd$gCR&-@%8q^ozKkjQ2S#_1RuA!#9j}?c}rQ!`&DEBKryX2(kOpEUG!QXZW_W zJ?-gR3%K@T&CB9>g^txY!vuS!l8HnE_yu^-XLYz=)=7I7VL|&Aq}`9S0-5&mtr~tW zivQk-SPUx9?1Z#hq-7#saY9->(k?@q4Qa9ZLq^(_9WURyqI1lxjE~LOtZF?En;XGn zz#sop%>m{yAtu`3JntBJ9`~TrMm^f#JSi3<&$4(Plkg_xnPTL*IU!GNQl5oI9?1h_ zTPFCU4T2}-Q6rC9{}s%kq&&?k&m$+;7REE_JTqdmT|6XZ5{xI#M5JoS#!q}ajWJvR z{XP~mQI20t9`pvB)xkYT*e@2Yj@NHpQS>Xw?3+_Qf=p~hOboBfa#q5op5K*g?GP}` z0}K;9XjI;*1lvYJT~W&Xq!+HUXeVyd!Qt+yZz|JwI`oKlyAK>C=7j({mnVs+ljc;s3A+YX;-EkHWj`IaTS50X}(7)!Qpk zcgU5QG90+2&UndrGFPSu_l2ih2qv!DX`h8gliHf&=l zEXO`Gy#{vp;LB&c32-rPe>P%wk3^pAI@ni?wbbHh6)E{eQs(dv;zx)D3f>0(JUPb0 z4ZWpE+GT)${* zHPE*$p}JdX%b!=&Y8J2sn2MUGPhDWMO!c%>RM2m**l9LFeqLHE%qG5H*fOhZLAfOt zd8RF}Sn@nAGh8my3b|gA78B`QYn8AyO2`uF-MClWn9nsh-(;yNZCtl3PhP^B$> zvfYXG>u%U!pB(31i+7!+;N?#>)@xPo74(#0UX(tob8+z=x_MPkGyjhJcG8#QA@*mAMG&tsmi_z6kEy`qd zoP4&)KLPi{hQy4Hrvc|lz`3unA&@cM%XtI4Sk!L0ejeKYv4Xd5y$21m9l$)urocO8 zyqhPs1;)c3YMI!!G>mK8o|-fZ@jO>2W+*CE_P0H0Onad>-j z-#h@_q66Ub6X&&a>O^6FhN-}n(dU!%9P=?2pDVNv`((h&(mE-h=XN}O>k7vAmV66+ z(Za(@(4Vm$RLFUVI=uFhECUcdJ_21tlVo%uR1of7T6L$+^p)+{j7&|#MWA) zuHT{D0FO(7e1Wr_OFs}++W(feOF9Lv`@6t{assw%tWSUfYaPadYaaSD zT{H{=bkZt-p&7V$su74SW?)xTNt)Clv(KBRS5M(>p|9M^E~Ay4iZ?ZUIuzZLf< zW@7$2F~=>r+)HX;T<6M?^gUk0vl)9A;{3mS8t00vQ|wmIZ@6L#V)9MtEI!^$yRg=b zd1P~X1LJYm(Gy6+-aqgz*x+quFdJmYK{uZ1gzHVB! z_cilaq%GH4u}9RN67t)?BM_&>mwq_n%X`H-r+rwS&+@>2fBSTAW%;n*`fPmW9mhAx zd9a`)D@0sLJ4y&PBEA2H&SHrp+0S6}+|!7gF_C?(*2jzw_Fc@59tVCw->Tmi*9rBB z_t%wiu{hq>fpP5!jG6BBZb5%@t-`+tjw?oi}WuPJDr@T9e9RMP5KV$Cu#JrTEBNwPFOLk%Ky*JLtO-ah)({S~;I`hY2%fMc(t3pwrX<&U^8WYkUu$=iwQ4 zdr51vIDdin8U9So={+Umd1zXD>i4u@W4*T>xYi4^&%B1`CqpAJE>{5FD~vf5rw{NY zxh}dgx+4Z}M!b8yt8+fqtcev)XGm}J9+=pMHv0l&re$}!)EXn#jF5i7SC$nb-h8$$ zv-hE4XMJTIk91=V0*`J+pRor%Xzw?Ef4eK=^TVJs8F$MO(l6GHgWW$v_GhP@V~`Dy zt{Z#No9x)9<=v;bh?j;jT(@*9@Sx_{4DSQb128t-&;`)Xbl9hC5{JI>X4r)>7MrJS#`qlVsB0vC_s849 z1L-L7p11@$T4w0PUh;dclUxtERst56u_ii)SQAgWduuNDpl+@Yus?Yd?>7NGHo7mq zBK4queXac9)qicR3`zYLeXLDfE2+Z|td$=;3q5bCv0g45dc91$4s|q1o`?N5SH^{% z=ng-U7J1LdvUJQH=tj#d@wIa&;(8#Cg|T*eu90hJ5-rP0h;x4}V!vUX*1~Dub!B{t zI{@^t4(L*6_R%jyq#L;3Lfkt*ZykFeAM|7FVe5AH2A}nyChmoF+OC&&cjK9M_*&yR zgy(wWnQ?_1jOThhN6th=2JJkK-MTL0?X=Mzt=qJxgw-2{Uci`3%AUpaD`0Cv?6g+3 zKcRpADr~M;hxA=o?;)F+;lEb3nK0Frz|Td^NyrD-Un{xQ34ABT6Qyq7{3i1Q$F1nU zhDUzlG2NuCCNxU=9Cj;P4bB1d*J(_n4Nlr@KQETLR)lNFD9pXjuTS{x#P89lKa`N> zqCPSZ!}YUX*(%$O*@z5XgfgI%D?*kmuGa}L49mTS@k2!p{!_lUVvKv(jw|DndT-UA zTj4_ooJg7NthXxI3j7Vo2xc|skk>?B<^VsE@iHKH;+$9(_uIHuVm*8!cNEqU$PZ}4 zgJ;OMdJkx>DYr!NbMUlH$vTOW_tJht8glmr9sn5fj*{xlOW}iGPk23?my{oU6Mp)L z3wz=T_09PQ^{q1Mlkz|Hsp`$msL#pzvIgoa8A15j58So9kvITeBCd?{^8f}v@$rrE zU%Vq8mNB*#`7rft_*Nc4`SqYJWsB5%*K)o(Ln5nMhaOYfk>{^+Q@4lAeIC26@{=Hs zoO=Rw&X)IHAHC5VTPFZ3<9C|3L+9EKj*Ie5gwd68uBm2R<2v#_*eoo_2Y*$S5C2mz zet)Z*t{+;x4>BuaHqvJcd2;MdU0*v+{~NwX){oN2Y9gl=|zm?zN?inIls0{|MX0f5L^mZv7DU$#kA`3m!wPNSy1)zlJ^;GDb#h z4a+v*xjJ#(m$Ea4fc>LCQZRSc6#65fEc%LlLd1w-ozZt;fBYqAZoI6wLDkiN1;^E` zzvokYSA?n$e$xY(X+v6Geb6}rdK&jY7L)!uzYIR7C+Ot|sh6kyU6b)6R&2+RQ3qi^ z(Lan6@QnW{z)P*dU-}OCM0t2`P`>o(*O#*2gb!nh_#yfc+OW{xy_iFj>Fb5(8Tb!+ z)|>23S)YiF-`e>YKdt=bv82t#^JjyXsID8lPZ1>TBg`YujVYq(N|0xg>6G_9=rO2<0 z`=gd|PaNknA%9D{b>@zj{rz9!C|%#6MZWO!0o!Hyl^!&V`9WOW+hN!w{0DCI&N*f& zqHRL0XX$yG{)TZ`SU-<5;EnZI zJCg1EYup!Kl(tI30Nt&Xwga|L+rcCo#?F#{mGe>f=^!1?RtXni(!%MB+`ZmqgsbBz z>@@@-M?p3~A1{Z$@#EMpT#7S~<2h%PuCDTQ|3B8=2R_Q;+8du|6OvsTAZXN}0X7nF zH)0zr>W0mq<&SpBqBjU!(2KGNyn@vhEP6L)muz6Y7p%6yKfR#^U94%r3oW*AFJ480 zB8{zDZ2f~(F(}pqq(!f-NjBkmzu%d8Hcyt|dw=im&F2IA%*>fHXa1czbLPyMewa#( zpB1>vgtY~JY{7pTp=Xruce_qn`=YEkcs%(Dc?rgMK+0H<)7dxHdYN8NQVXQ6jQTF% zi=*x6!`De2SI~7*+H2YN;BuYfI$(=KZPc2=eMxKvYz1Mr$U1OL)#2^<*hI=OpTf=U z2(vmq;#63i5|MpRx*@N$Mnvwx$n#6x(a~A(w}(6Li_OzNUo0H4p}D)FemD0D2Rt70 zORhCPT@0PIiffK~!y;C5?JA__)y;U~U_%^d^S`|zQdfiba!WDb*Wg`x%oorKIW*g? z_e%?+ni66 z#^b++dhb3OM&WEH(6U|XSAvdnQBFL%&C>;$5PFTt1^!l?L8yI9L3L{p`t|i|<$b@G zPu14BCLF7)!MeB*Z%bC!6kxs?bwfrxnfmSarqs4q>5C+v`@4|&muu`ioqPQO;UxCl zAO>`l)O~BhG1~1$KT`X_`rlkadAD{&r<6m;xA6AGgkv2a>|lHJvD_0VtN!JwQs)+( z5De7+yaoHN(}&r|c)RS7oL5y{!|N(}Z`+)SyP-+e8|l>5>_jv0FTIZIIkEvdsc@%P zc)m(Ej=3{n52R257d|-uGH|*7AL7dYe~8QZ{|6WOf6&#jP4oS;}yG z31SgtBjl;Y_wD+7x{`A)6jFbe-jlC=O|2bPJb1uf*rFU1QQkA9PcOAUI6c?5U#vl0 zvTbn>CNtu~2aTya5r_5l9lQk?0WK%=*>PbFfc$cL7V~1Ah*-07*iSq! z^C!;C-}h2{zew2gFM%JX@WDf(3GtDG^aa-ELjF9IKUn_z2FafrKIks%&)@qp@)w*X z|23KUk=Iss?}Nz8ci09i+jVAMBVa3c7V=IUJa6x-%!{_lW!;&F?Z^76e(F6B>-@Tb zwtL1Gp`KrD<5;6FDvl}OUc+~b*`FA52J;dR?1X*LQ|(S7-QL&XgF^y45hrc0R9cnQ zZ-~23!>8ls9L%l&jSiJ3_3D&|aL??lW1;sN=C}8y&Yz?6Kd8oo=|Q_VF|UMuJV4)6 z%=uH%Dgx{SjQ1g!lX(bV%l<^3JkrXxlKJF*x94QJ+bxg=9`7E25AQ}`E$ev|X=mxb z)QmWA%{iDe5p{(8avI|~)p%x`8M@7SQ2*0kQUBE0>OWl7zem^qH0ysBntFDVCd@z3 z)7JqW-pci-U#2?dM)&hCD~`2!Vo6p9_=DVUT6WNuY&EAPyDIu5^y*lP`(>+NNLi2T zPIQT~wZZrhYRvV#PJXEOBTm5dQ2rv`pzEVoSSbF^DsNE+xIQl25=#F^S}222cOm64 z(84k-dT@5X6?fNLCT7UwbT8b18h?;GS0vd>;UB>|2+YVvS$Hy9B;=@OiwkS@*5iuZ@1L>(2Hk-fLDl#t+h3ja;~g{5i}i|fh{t|T#dq$x&%@X#jBdjl-xGLlVzzTEv5(MK zJ%ja?S}%_-<$B5eDdfE=)b;qMK_?@!w?huZUdQ;9^5B#;juP&%U}rn0(j5@hZrtKd+KAR8kc{wLR2c~rX?d+NO3I*K;oo~4!h$%@th#+O4-AIAB1e80K# z_)&8Y&-hOmX6uP!v+cwb^U#S!=Hci|S40MTUp%(95$n}y_#~8a1Nl(`IP>+M|2obs z_Er71Ay3A_{cL^?ZufOg2Y+Q46rz5ZVK7q91fdOBJE)Ho|obIki| z@IAAw{;A@2jVBTF!tXrJM+dJ*-+ihc&?i)(Z3XqEL)z=cp1L0Ain7cGoK>Nmz*+KN zqw~9EewWHq9^k%6-O)ncY|@@Sw-WzAd_Im_{9<;RlegRG?25Oa96$4~T5oP3U)xQ!^@(*pI}uJ_>qY_#{0l_Xhg> z6Yzq2E$FQr8E|SJUuvD5TIVLY*13t>>KF2<^06PZHZI%|m|Jt|dA#d~v0#(|XHEvr zr4p7jFWj+Jp25Wp58zxGaFq(@j#j|!!P~yXR|GiR^&L=`^UAAVZDk!g$Ks62!!tdj z0B|D*!nI?4C5^u)jTM?cw?7F!Ta7S$;A?vOb$7C@+IKS_`C8-H9a1+p*ZKPMQKzJ5 zKE|d~_OBa#4xJbE92oD?UskzL`J`UD(QLQg`=Y$SwTilGtOE}9=2vPa<=(nWa8B3Z zEA{O8uRcy5i?Onm>AvIotP1z27>_bJ5Ac@nSLSW)9J_sMM<>c{>&L6(oc6f# z8m<%M)2Da+A8|%>zEMgFMW;{biKQI&SPS-oGXuyi$G`ZvGl@NFIvHie28=7djWn+@Y}@w zs_(IU!E+FVp9`7C-j{cp%@<$hnH=~~GhinoEsozZ_C3Bmo-w&RUp`QYepYLveDfeb zzIb*m?mM23H@>d&jOJP*68NQ`)d%p)_4{XKaewN{@;#V3?T z)-vCVLy%U1^8U{9sEb6n3Cbb<$~_|G0?;&=J>yni(pcTE6THd*nbx1J&Hu#r?xx85 zDL6-w_6~S2uIVUz8XgsQ+0Pa6p5|Wlu885r+m_j|`J#T&+Lz{2hW3Y59M62w_Fa$x z*mikLLw!F*I`yXp?l0uiu3X+hfxmxWZ1##!szshl!Pc%1`_s6}W9*rCT1=jIOicES zaSDuqC-LTa_Sz{1?afw6A2)oXiu*>|+nIgOC?4(zppEA@7K>GY>qWkGY;$}c!*A_e zZ}$24eh;_|!py@nQfS9x^URzs@E+Xz?JAphQ%)89l}8#DcER5P`~glz|DhkTpUDTl zV}Qe5;owy(gCj>G?}58#$hyCBrXJLICDhZh9#t18n`cC;@cYjge+g-C1f3Ipvk+cY ze#_}zj8U8`)LvB?$QSC4X+Fc{XwM>~$2~(u9&8|j7Tikj$dS;U`4FZfe<$9?s=^*) z6~;&j#_{v`{Q~vP!SAQ!tEl7O7eRiTcT;v1>&ZGI4L%ms_-wd&2K9eeR5@U!PlZ5Pdlt_oPr~w|P6@7Z68$ z5nw+s%2;;D-1n%W|8#(7s(|rf9Czcl48PF%VShxKOxgjw?Ll|)PwDr61N$zV4}v#l zW52ppC8__T{b|sh^*%5l4Kjjtrtg1o_6n(YZiS4M6k{R>v)$mu@;;okFG1Vx#vbGp zb(zprKIv=0eX&+Cg8NPj7BCwC)1_hJ6~xy9|6H)aAsqMeHrN@2l`traFy!eHrbXlv z9{9$i-f_Y)&4QKdAkH(m?lB+Az#`yv8 z53}Rk*pFivajv(8iL;J4Wn6q%5aH#>A6Ixk*vM}ieb?FYt^=&?{Cij{!k5`_wIMv( z7awlXbsfKLxTO9>)n_>9di%3}+`~cF+eiCx4F_FszmEJCT?W7ValO6UmKSur-C_%a zuD2hx;R0Q6--kZZxQ^`Rw++|w{=9j_b)zjzTr+H8;wo===~S1Di|08J_S(`}k5L>i zwzxw37*k{FtuNG2!n|`p#_pe6kg2-MbejC3{2h~e)<2e~^{m+2;mu0*?gef8_%`ZM zanHbY?%6jn2H{8FC~TZO$U2Ah7wec@`|yS-*SS;abq;B^bxt(!tsQHfgO12|FW0%p z_J%>BTIWOwZS29{O1PHsP1$+lbE>3o8F|+H_fF0Sl<`1vi?~Pf)J@>2oljg_3O-rq zUFwLmG&NmME-_wnF_!AOyqWPvE^H3Zje4!uC1CC zoH}M!5Pnvqjpa1%0jFyoJd->aI7?99LEaA)dqO>rZDXCZY;@JeN-3ip*0Rf0JWmB5 zY1^QFTRSI%=g&|)KZ>&qlx6XISr*!`nfzYz`ksDX-_U;gsh{*oTv6g6zqj*xq~G4q z{?~~pYXQ@J%N1{rhR2jDx$v2%n_@D5h2-VnXN~D|!Pn=Y3jE88=R!_#Ll>pT*9Pdw z6>smeY~m%L)M;ru6z=sjpI%A3c>9=&@7wV}^W!^khs+S*lhXGS`K08-QeW7;zhmEy zb9vJ~SVuQ+? zHE&VPG*jX)KI0sCQ>bT?lz&(!){*t%Jg4mr*42&n`a$PuK$p6TYstX+b!}Gr3{|K8 zGci>cTUgR<3rl(#X8YjGHk<8(c7^XD+NJ!ye=kCw0=~gMK>SE+-+lu2bfEJ*_*`p( zUnt5Wli-W;4%pa1=K-Gs&tpFid(ik1yU&5#-caw2(*En==XY$vo?iNHfF956UMOf& zyM+0OhdKb}B@X!Dyv+l=m~RvDEBzWG?g@m85O%1udnfladHS3BGuWTsI3WC&n58(|#ONNnWeD=BU zw3-W$WqOh*F8wFlEm@59{#Dw3PLa0x(0R!5jCp21+X^}#8y!Z@O31qORgFH$fQ>bA^4gyo4VYDoiIynFa`wm@RA$0Vw+tTj!L59<5MHoB#x132M z#<%DKjb*C*)COCAs~#m9CR@YYIS7nL!&o%V7zBpr+AOO`;~75)4DL2a7%9V{KHM8+ zw%H^NQ=;(nygvwxPs4aL%rk^R-zi+|x95!r`lv#~P110({=O~n7gq%nGTiCpeN4PF z+=4w-obebVLB=r+dt2ChJ-6PLR%=Vce0}atnHD!BuR-4VA@hyBSO@dFR*NwC55hYr zgCe|4hc_c!i0}#>eh}dR!dfPMBSskrVJW8||D6c0Ls;ISNBCxhTM?FX4q?c0d2I+c zYkZZIIT23k@MQ=)5#Fl9qY-9#J9T&%!a;=hsPG^A5N7$UI(!mgmfxns?<36e59#o$ z2($ddI=q*%CBp4GyaQpD-=V{s5oYxe#}G#OhS1?V5k~ojQ-^Ox80BMs zFWdKz*CCAZjY1u+q#Td1TZb>B{Eo0khesof@*%6J{KF7N`G!x0|Ip`)U$_(DfDWHT zIEnCl9ey9-tq2Eo_*L4WB3!G(due})a9D?TAk6ZX>F{QRS>6gAeh|9Hw7#_qvIFB! zG|=wxmF3`5+x7dq@O2({rgfB(_E%>3lhj8NhWbbm#CXg&JH^`>J&#JeBIMO=@P|?L z>n9xZZxRN|bH%)eOJn13=Aq=4rhd5BKdXOgY{|TB&WhMA@C5;#6MaEY_ttg$$T;DUr_0O;ob0$MQ%EtrWk5_Hp{S5ImjuCm*-6*xr zxvu3p$8`Ys=CWK3m!AO_k#LV0xTAW*Wj45i47jHVSA8q}1m7^k23ME?_nyR8Ur-n7 zo%OyAu4o|K(BXsw%riC^oW8+EGJQvUXh6d}YJ(}sfVoSDwqhij5t2h(d~*Lb!g++f@@F#p%|J@nen!SZu#`=EbNy!*?eXTdR9 z(OE0|AMeP4pa0@A!e*3p2mRw{*gBR6*hzpj27!Id1}p0iSRdX9d2SB+h8IYc=Wit z{!JT9JOkz(jpu0_OonfQ;d-uI|DX*f!#Bb3X&TRR8%*=SvWDw1bNw|om}CYF@7|z& zu9y9<+h=PA%!dl*x;`78odaP;T%cjzvBB)gfSIXbcH3ZDGhiAu%my1wTL#R3XqY=~ zFo!Z=PH31JHkiW$Ve)aN&bnVW-Uid20W(*_7&e%W44C^B%*-_Z!&p6)0kcoTr1>9U zx-wwC&@gHK*Ez-z17SvbHB6fS0mhjDbBl(N^I!F;k)Hwcu!iyW*UKo(fN9e(Lu@ea z3>e%AXB&o&+F(2bVb1xchS_U_Da(MlO~Y)o!T2&@HfflA+2OBDQPsOQ7`n2vizO`R3O)wuvR>z>)rzX=P-@qurGv;5PCuA z3XJDE7ee1vYUuNIWdnnB=>3c&=|MfRGwOMe^~By+zDo(eeoFx^WzUeg2j~5>eRba7 zAB8??4)O|rYg$HgR<}B^IT6n4BMt+5rikw`C)(kci{zdOdnknA*XEbAba)-YLlK^$ z!%Yb1A$++GHzN$2`(GC8a1vqI1pjiRoO6bVWYDeWcd(!TVR{dQIqzcChrxdSXNYwf zFtr+{pZ^(|zH81~tzr84pCOtv@NCyG{E@0vE8ojt(5;$DV2eC}`1{_q>Ky-A%T_m0Lr z$~JixCC`;lj-Oa1Ve(Y^=dbeKkZs;o=zCwhCsl^;H-M9Ve@Ybm$HS;Yd-wzM4g5Qd zddT}{yfeghVLu05|8-1KPTJx3L5-jPO8Wr9UDi7?^~z`qV`H(n+v9XN{QGEIlgc{ijh8>bdVE(+8=m^nZwa@EvphYP{hi*MHPs z`Svqgyo0nW>$4NElK;uJ0{!sO|IS%p+X1_t<;%U9)@v#rbe#6@_xNmav*@J3k;Muish+sd0@&Q<(`*W?uvuvK3(pWD3|vv z*J0cygRVW+nVW6Q{^vWGYm4N)+&I=I%qh;Zj-T#`vn>0$2=S{UH@eWazsVZlBP7`&^Fz2}a&q#5P0?fUVy-*;x8i=t4^ z+HST1dD%*@`PaH@+FwJRwyQe*=K>`0&t9h?R-I^*ME-5BM|r8`v;6wwb>2PMb>8b< z#9Z<5OlTv|>cIyFAAbU8BWvXvw-KQ~eJ*7CYZ|V7?t3>W`Av&g0a+&IwNl73^(z>uJ_MR)CF_hZ13hi;x-p- z93$!A*$DdgKX4;a{>iq0ostv&4x~;WI_(afCV%W_1RE!y+*aDTeHgXFkf%|0s{W8~ z#Va4@mTd#&7|dVtm`Op$sF%aYNWP@&T-3M_-^$l@KGG%MiZ0LHRW~lT9kk-yVRtLC z@Xv@FihnA-(FOM*ty1=%9EXtqK7ve%Gaa-Q?p_C6VPujr+R(g0uF0$mZBbO5bD3JF z5v~@6{+HEVmWI~9AGgbrEKA+#zChpUu6~+$8>@}Hx~lex%nLvGg1C4VC1nxBQ8z?+ zq}-+N$cStC#-#RFdcl!UZ}oCXAIsi$VZO|t{L7hTLzeX=3P-9sRbOf4secN0T3sri zJfB-=h-!=>|E7)8wQs0bNLy!(q~VaPYe3bt6R^m87-`Ny;@c4~=~3s3z@gwf5HH&? zBmNZPl?*%(e;4BAnH}L9E6~q+97wt(&Wi+K6zoEJF}@3UxIUyZlA)mlC?vv1fg zXrF1J-YUueluUkMMCMK3S9%?`u(U_A;s?-k$60XR8S1TkNwzohmRfnQm*WS#%W20a z$ARl2Npoc&{sqoZVPDKQ2~3=)ZW63t`~xG;{{haslG5jiv;o4Nq;<)`+|xW44Vmw5 z;25o|d}p{GS9)BiaWz1`RrUU)m8pvIQ+ZbOdMRR*L!zO)qSmTeg6llEu87L z=pRdtQ$a_;-}=jUtipb#Ip{d&I>u#|>r0HvrY?|nk!n8qKPH^SFF^Nb+LULhxeWUS z=4V>}TsCA{Y`|O|wJg-@-%LE(Ekr;nb5P%-+g=IYNgP?sK zq15C@*)Cl3D941%LoexhDC;wjA78kYIIVWV+}fEAleQTb?qQfTRI~~cbEpvamTb5W z&+Wk5S#r!Z!Us6UYr5Q&dVf%k+sAzSO5C&aAZ|Qm5?lOev}u=WQxQNqZQ_k|dg{6>iC?#C5!-DbKN)op^2An@ z-6hLrew5GqmXe0JH7?%Ti1G`4twvyA`ENBUnfa}!)wqMLAlifSom?-*B2ULCxrR}u zh`DiBopEiW+=Q!WzwUzYC-E-Y=gatxQEENvhbvQXzv>Eq(vWL|3ot&7uVYj&BV3yi zox*nR(WGVWFc>-&)UtyhbXKg6$n`QqNPB*Uc&~|t9VZnEKEm{z- z+FhJxT^pE&-+5L#@W?i|+6LiZMmZVnJv^hmVY51r&UG32Wn0_m+}ckk+qX4cXV9wK z_VHhU#$T$otsc-vpf96shqq;vH=u0+FWa`iez1!jKyO|Ky?Gh*=4pC4Z|v=wr)jGrb9p|A1M85S(=+k{4EvL6-3RZ_0q>V~QEDxJ9e!n`O?wXRU3=1Nf_bP@(eTJ+ z8c!kdq-BR0j)7s9Gook{=Pjx^Ps%4tYs7i|bU9@oR0hsFZy;YO%vbr`=UOrd*Poy4 z6Y{*s+G|mE!}|UO-f-y-3#>nK|NHYG^Y-_%;;#(D{%x10C(XxHeSX?$wQ0F)828z8 z6x@GrN1KkY@u*Y%v=5VYwS{#*e1Nv!JH(y`^0J5c{%`pt*7%*}GgLqKx3%h1hOrJ$ zw0QQEHD9Ry^A{)j*Cpk`Ze3h?`pct~#D$5!RC-#|L*yGfPdcSd!W$}Ha_n*#G0p@N2Y3`ZY>otR?7I<B z9Ss@a7VN)0^6ez9{Z7cW2J=P^@~w3ELwo6pNC)1XsYu|=C*g#A`sOpFTb29qEGc)v ze$?`%RFM?0@mqULmpfAWGy~9~lx#T_*KmvVXRK6jk<}#1pN+-B#%OV&3*yxHr}|NPRJ{E?Vx7 zL;mlb*vImTpYyV=^aAJw9NT5vZNd9To1HoS%{)(hc%&|?332er`mh|2Xy;~xp&x$e zGo7BKZw!Qw0%sCFM%UTu`(xZa^C7MYceD237k9qlvjA{*8bOoX!`S0G4Uv?15*P8D z6nmwK*N|@b_(;lfH#?zwe5hQ0Co#V^J1<1otHyJ3vn~Ey#H+dhPSa}DMYrQaR3g63nuE6Z@fIxRCFbdd85WG1 zryI&UtIbw@uQVUuZTdV0`m{6V3C7LnKZJTqUnU)@ZGU`C8h?`e$)??D{3_gHPbdEX z%m%g-{Kb4uUIIRmMV`)i-+?(mdvwlyB}=RuiE%W5Zv@t)ULjuyx*l&Ljcg;%v%1o& zkNOft*l&n}I`2G;V=+^=xwdyH9Z9;G2jdB6)ct-8k?+4$KYAtqSNr(wNv%tdt9LQX zHjQsR*HqMN55|8|^BK&EAD-lxm+cqlU51SION^e1GB8(8`zqp+ZzRJB@C}?d292U2 zY1)9pqD}To9DJVo;l$|MP;RUImSe-`TXRx)2jvQQ8weTUd^%Jc2Uh1;Lh?|*Qe zEs|v?g#g?%RrW>iNF1a|#gEhR`!nJ%*YVpj;xE+k8#3ZY==i#f_=!4R@_wvMhK=?j zohIkEjn<2F{AC%qtg_F`h%Z#|7fSxdwUTqVqP0xk-KQ@Ny>^ZY^}Z%~u&u0fb>7VS z%QLi~abaftb2NNr{d0AEX8p5tysSUQQCpfnNx9&`l^k2PIIPVN-ki~oa!t~@R=0(t zq7^<|l0r~U2=%_&!ZxvRAp1r0-IZI!I?SoT z)=kQ1SVJZ}LtMD)%JXA&-(=uAz65E!M=;2`J7zNS%l08pAWy-4rh9OYd9so-{(Kqu zLpJ3T%2nhOA@idLV>74ztP}DX&NYVlH{Is;C&d!1Ew!Bsgc$A3!oB0!55Xs2H)PI& zaoa?Xr&26|iquKCOgd;2gnBGh^MAaY|7Y@?FXwk`>X%KTiOxU3)@TW;%|9dVOrJk> z^KG=C>&H7NBU7KO&kdU?zlKu3FApPwdQWc5AJOip*PJqW|F~!C4yI|@P2Tb6cl8Tq z%75fC`u3LR(|9Z8wC@P+`z0@!fj-0gMQiR3_55A(HeYg18D&nMBLrJq7Sj>V0?axcSE=(fVLvDDZUxq4ox*nQRVNK zJdZRYu1v>WuHi_hPsfecaoLCq=r|YSxNbpq_4#jVk?rP7{CWalf*Y$vN5{QvtG%-A z)&5^pXgV?Xe_atuy*igTbefev6m%|@ZE20m<_6JWohNckH!j4Pf5Yq*D5DeS9L~lf zY|Pe+bLb<9>kZx+_*tDZaWUd@u;;rTan89V?yI7S8wxnzqY2pkq^=*rvQc*>SEN=* z-lb^C`?PW@&P8h8OLk9#uXOp{ehA;m@&NqjP7_=4Zzulk!M|4gYtt}pd>>NZM{DtY zSjQ93DIFiccZdGY$9KE>ejo5S3k|>*&ooR0IX|8)Cymr4aKCRqw*vq8P@W&*omlV@ z$alq(ehE7k;{;=Z@N93TcNl9BF6P18tvC|`{M~8XV{vB!V=M!{ECXJS72<0c_dCq( zu?s_ZepBK=Kim5CccIkpVvJ{7b~Vg#laG+D0rbl=!?E89^}6Nw;8>A361$dStd6wu zBd<^GiCfC>hL%&yiY0{qsy#RSj%!=S$K_oBobU8mZ9zIf|KsJ;aQ{~N2@(q;%My+` zi?gfdIvryO7jvRr6JvQVBX`4C$ahYM8?;U#T~g0k>R6`qHfh_sRcF*O;bxm-uk23G z6}&yy<7@fTa~QigvvI8guDG1XaZ!YGfk=lb)^&5fW1e@JBGS#i#J4=l^op>eH`R+Z z^F7?nc6+=AY~{pu@PV0_8~NSUMj?LP_;q6)leFU710P~xi(b-6Utrc*BG+2g_UL;WKn6fNf#ySXL){+y!sckK zl#lw~RNb(FIv<=H!D2GGFz90!d zaZ-+iUlIrV4*Qh?@@>H8BR}Nc+4nxY&?smeYsmYmd~?_uYoL*K_c_EH>K!A;E9$4- zf!&XLtV!6<;7nh>*(v8-TOa$Fw01QZ(oTT<1Nn1gyJ1XQ?N)!&SojDs@*&$dMLmZC zfjInEaena4<3#aj)%RnD5g*DFXiJGVzEAA5>Q6s~sKXsWc|TBVZ@Hk|j+?e47?Y#r z{0Gbd`nF^HAm1MPx5GEu_4M|N_z1y%38O!l9x~U)WS@9%OTtGD&UH_(qfHII;alc( z6l3H8IgaGqE{1>Y(Q+PJx<1U~;pN^R)ndG@Vp~}CVte-IyZAu0!%WrRbQEvi7QN2; z`Q7+eSezt0?LVx(ZODF6o}I@^e_Ie6+E(f>$DS7Y6Xe^-d{^JZ_A_5W9Umz7Nc|%|%=@{2_K>?le<2B46|!yx38Z6pjk4hlt;Vx^2hr)=Bo zu`m5>vwu-9xrSBTioMej2V~##rzzQCR|DSpJcM=YtUez(9+ZrB@kovdlquT|WwvFO zIY>D{UCu_7vkda)A+Bpd=b}-h6XA9nez(H^m>fI6fB1iZf2PL28~B~XNBn-^4m!iT zS;h(2l7v$Eck&y3+3jn^SWDu3Ht~;trauh1w@y7T=ax?AzJcj~(`iR_-X7%Tz7T2O z|CLUANvEZJ?Kp35l`^Blm)K?Hm1`gH_8=X0VGhcaNdMNCI=}2crZ-%RxhZi!v_YpW zC+^BXgNZe8-HB=uIc|!0rHG4^A*761=LA2U0y`Ptt^n@3Qs^av$ z!rATkCvDivUA(t2`#pqDMSg?#=NHcI0&F(g;NlHqXCFhl19LtLaqyo%`wPTq;o;7Ac$HmJP%(Jf(MxFO;lRkCezecX1ST|`?vx>Y|%R_mR z2je{#SymYK6N!ssp`NE@U3qsFYuI!P9&5@(>}#Ffc%KJxTUkc>t;{M>h&ZOXq1%3Q zVix(Dfp_0#FAJG(wfI}{P6+rO%6H=1AaAdpiv8kE->B8=`OKj4S4lhJQ0^(L`CQMM zeF==c1pMF`CyQA(@WY+x2RG6-am{S#NJSR^24f>B?Pcf#wyUlt?uj0}p&)1+?_-{R zSx|&^-95BLkv8Ix?ayY%t>msXqu4%_t$m3Zi$kermJ%n{w2b$L@iuJF{BowLc0DcM zOktZ-OF~+^_6>b%nQ-*8{lAl z(`|EH%1yv^%nnaD&K>5l!-ve-8^VYitSvF$25n2^xtV!T#;!ruV2;6`gnG}Lt6)H% z)P3IHge?3s;y9C_oh|7Jy6!KB-eDGPkQwHCMbUSak6ZfjuUo`-E)cC2Rl zRk0aCylZ3bWH~5LzMF%x^YEQbpF<7PF>WQke=Ao$-R_ZhYXCE+etr5~n_&I=^t(0{ zSU(5or5e_M6ln_ zhR!K}J=z0jXLxT*kG=TmQ##JUIF3`G=U-Zn6qA0e*3D9WtWG!bEH&dkNEhf49%q)J z?tzE`eB%y~(adwzz?vc9%zB|NIOiHyf%>?@C=7Hfy^nd{eP_TX#@*c1u!DCKakg=e ztiOcE9D}czKEkiB-T}E@>CnMz*wJ*dmpcPhSzJKO`?U(>k{ zu!{(5>Hb3I?;ph(fjmn0RfQZp*-WFUAep7w2+at6!N4Uqu(j6}}DU zeH}bK8o`~3CQ-z@6LMb)e_;JE>vvZ@A%;!feVd!-D^=jPRkz|F)`+T$As7AW*O3#C zLUs(B=HzxECO=YgaPs#`_D{Z{fN{moJNe*Av1)z?xo z9b<4}z4wOB*lV0`^0-y(mi zMY`-8&(&fmXrj-qt9Ps%{BooLo&HpzYB6Oe6NO0Zus<2wGQ_+F*Ix8r-+ z>ha%48D@C(H4pQ5{_3Sa;cxBg`+mybAljxGzXALbr!QJ@C;+~@D>j+u&|-TgP37Wi zdwhxUmnyo3%QnV1E>Jwx8sq=+w?TMHKH5q1uJ3{;{Lk;CW%3PmmT1Ju6VzK#Yuybv zTR>mKuB2~1X=eLbd=B*92!6dMvo5Fu)@k0a8>Z(!_^ZBOhqkbMfM#I}FV||t-`B*n zpF(30{@$$kyOHI8T;B65LmSTYI1S$OY}K|icOXn%%o%4IiXLwz=o!s%LR=O-L1DBa zAMIvdk-B5^#bPyXvgBGgu)dd!!B}~3kosaR84tN6Z?DF`1TyM6^o7NrGh{fiEYx$o z>|?I;_WM)RePIkAQ?$JBsFp3}oTbeDD07A?)6nO}qK##HcPb6G}njT+#@72ch%yR?;cQ@Y+Uq&=p@MdF(dLV{vzAVx?^3 zglgmcm+LlOaF+7^G1NO$%F38$hTQ*{zjUE*vmtZeab`Q6_SoAU^JBodJnb8Ni3xRx zf24lfZ;qnw51d(dai+aNV#03KKSOV1-7{%b=XMi10RtIj=#_*)85L*J*M~J}&~r)H z7eVi3$prt`eR~_;@W7gOMteIBsP^8sU$wVmQ)V4VyDxDG+S?`P#);>#N7y2Mh+EZYh!cTE1HF_J{0qfBtg!lavXLyKL_X!_4 zx)yIOuf2K4ruvP4egbdGxuX}h%QLHnAAb~StlaVB_YLs_`oWI6+El#vcail|#1Hwl z&*V7ll-9#P|AQib^^aR??|o!M`|XbigZvqHbLhLpP;!d#seDJxgG9 z#zO$GQ|>o{frpIggO4lEzk_aB|IR9Po{6=hOO2~#qn9lfMsvdstl25*5Hr@fOJcBh zJ*MVspPZWm*99;9i}x;xRZsod%2Kze^9c7`?+aBpyRSkSMHmP2j3B;e_OCbFCzw zL;W8?kbhZl8Au-=^88^Y^vc$~RFdQCf9X50 zO18LnO43k6CB8O8^b9{rL7};GJ?!!oKw^XN2BoLcQ59 zhf!d4Qsge)y9Kr<+CL*@;{^6$=ItL7PaW(nF&37gKZdG1hS^(~Pv0@5-+T)O<9BxE zc_iBt`>E`knTN6q;4@?jXE@YzZ86iKZ^4_bQg<;O=Tg*l%YMW-na+09XD91;b^`jY zVcC@dr?e@r^STrAE}ob6wmEP3lBQYd7Q-Vg**MqFJy?H#Zu@0V0rw9uH=!?P-1qKx zi0e@<>X*;5!Yz2W6a9&{@WjSn8ey5~y%^fI3vClJmv59hKY7lEyLEU6Rp}G^wlSZi zvlVx+V4Fny-cav0xdtoxhtRhR=r2P1F6SNhbt}CNmS1A!Eq8mMx5U4sM{pm8{4HV7 z$g^te{$6FkfxCXE2i)~TemN$IAGp25y|h%cL&v_Zk?pGPU>8;fgah{j+m#F>?KanO zKV;<(naiHhWpycgb*vY-GvME6l=<|VS>!!IXK*Cx0zR|;XpD$$I+sV1vIo9y-%s1-A8=~(`n zseHWS-0EZ!{XgLt@45O~p<9N2r(xfW(#W<6$4D#R+KjX~J*k(AN$*P89y63atKars zhg;_zSN;FJZG_{Rj{5^;Q`TgEN}4MJybG}JZ1)FtDj3{7s(JfabzdOW0QkT5-zm^{ zb^^GwQ-eD@>MqqhTe`+!{sQU0R5(n++U{HF`zZn3PpMh)Jq;uKb?pbx8ziyjSLt~- zM({4b9831I%8>cL-aLWzs?v@5#`(0@ov7b?)X}iFCepAMZ%prfeGPpW(SH%gI>sS= z5*1Q6jdeJJ{Cxix`JZ$0t$V@{5B;BaAs*>dWWL9oqbiaY+{LlOyoiUtjx+cQZ1qd$ zg?hyhjzOL8!ir>*oWCmHQrI$sj>nuAkro>r!z$fz>Lr@&bS(Ae=PvTPML#`DpVs+{ z2IgP-YU4sFzsvl})+r0Ns&`>71S;yhO%Aqi;0F%8HzVg)llJSAk2%={9ikKCneQ(1 z+re+r!m?C5EiJd`V!0u6>CJXp!!59nQt=}zlC4(VCtB^bq{ZwCx0vlAbBU~fMbha& z8;l6I3>9nx!XfR=isXCkx_;BbEuL&EZAps~Jmop7GR@(SvV9G;|1=`-?z*pd@90ZTIBKfr0r{@k#FM-di#hu_sSvox{+%mSgKmXyCZ1Zr-3ea_Fr_M9Bgzr(sMh8kIkHlS0 z@jU(YQ=kKne(Z-o67W&@ zhyacQXQ3HqoxSTxGvv-}&F8+vcOljm@XS6Z*Kf#xp2~pv#-f@m%U6QpOSnIPbr@@r z6YGV%uU^Qx0$KEn@7*sPRy@uKKAl)8+cyt*VRLT2hhHat;ZF+pO1|BJUb z+x^9adTxC+jO?nPHcscg0G`7`uIX+>`#~gABcA+J;-4Jjr}|9B^;Dg6$3Y zY-XMJ0_>&5^g(1>>?uokgmB`^yeQ)|@_{8d-vO&};ou=1Q zzv+S{vC z%w5FIeEKd1<`4F5&_l>Qn$*=@Is*9(<_CNtddwf;TNy#|iHy_jJ#mn>#k{-J4|@L| zN_Fqo`3qBhLZo8w*8*J~_NhJ}=Cgd4nR{~ci#v&rIzIGQNyV+WQ_QzP7QbH>>iKAf z!f_gMr@_6^E0l4%F)s109pUSFW zaaZ7lF}}r@E&&Yv;H(U#-jn-Jjz7S_M){UU1Y1m0Ma-)7}hF?>Vmj}8IX@ah*}k9wrV-GP0- zHU6zJkM@Y_KEas1^as^Xz&CTMYdP`z+wpJjrO!hDA>{igc&jdyO7<}ga}Dpch%&6SC_sh?iPnX-Bf%85aPOIF1 z%z#;B!GQlf5HerqTW{?s7kdtK7f|yr?PH35k;pStJAY$Wm)<*udZKa+vaQ(WUCVa` z!5a#pGjo%-!v>0Hv+#uv2UuG zT33#A^cT)GudXC4`x{ej7T%n%z#+w@&6yl#PLpNvyd>f;2L{yL5Lx`=rymt5wPr zYm6}MNID)9wbWsr&6b4pqRbd*jibF@(X_@9N1M+P_+6ySlz!==OQG)zLl#JVA?Mlm zI-%qK7;oT0uF-XaEmhfTq11mLWxeKooKvx4o8Y;Nb482j!*@V`@549C#G4~IVZOg+ z;fS_Sr&#tW%E6tB|K3PEtc$K6cn^HKC3YR={5o5`)Og;ogL#<`I@Y)^Zkph;l*@_` zH`ra9Hs5N0+x~L+?~I^bhnc- zG4vy%;_?iUNFkHhen8TSc$cy=vlJM4liXT!2x%_WI*V5h9 zw+i3d`dtzF0F>jv#Mrn*}%pugptRp9mDGaKc6 z#hogy{rVn)^eLE2c>=a!2IUXXlqK;dzBvszM~6X}8e2>6KzxrJ!-;?T2Jp%Gtzj2q ztkEuSL8$lb<;pNR{+M4{gZldE^#1yKJQ>59evlm9AWyM$K2|^8Q&Zi{Kh+m;v9TKM(n;u$Md@! z-{j3neB-Vh@Fa!j;RccYlLnp{A>1IAbytfZ{?+1NbI>_y1Bzn(=qn5Q=cA_pgE5rT z$#}_kQpq}gSGwcm&BJ5fFUr8PstPLGcikx@?{7LK|g)AH&U%9^fa34A7y7eUGDe|Pq$d2O- z4ssyxi65GiXgv0 zS7J>LfJWHHVGVUROdrQL{?r@1)WeE<8^b8H`X&q zt*ddvuerJX#4)_{ZbnM#&5jr9s=J2YW^%7@Hmms62dsF+B`jEce@Y*s4WFbaA7xql zqM%siW%)>tZxwksM{vU?NZ+q? z%;&fWIxm-e6W`}m1_Gko5Sup^i+Y6f7uA$Wy*J(>UW&N2`1kLX?po->FMxesneZRw z9lzXz-7t74pCVpbNxDEMN^J-D&h_}0c^ z$JK}rFLD+Ne9y(VuR9m?qU}(x2N5@KqhWlP`QcAU<|C|z$p*}Br2ku`2qPWz0k186 z2-4q2`aYFz@#h>yIcOlTtRB;s|saj$pT6=|v&;7U0|2iF5hx3asO}(~Dn(9rH8f7|6vF z#{(_qt^;#up(TeH+zV^_m^RN4kN=7%Rb@$7E3D1(k6?!y_4f1j8?YsQ$rV-Hq_dftdMCv*%3e#4k|Ga)BT zoOdMfb-sHH9g}-*Y3`(zUPH==9D}hLkl%?*m&N;7z!e6rVdMp%voGknST00ReYp5%qcm+h$H8chy|iys9|~ z;K-J1c3#v48vvYpVZG)14HB*b?HgVU{o-QbCarAm$l}}sHo67>=ByWAXIaP(7{%N9 zi@FcszmjJ!HUbvEhQAZJ|5ke#^AP+J2hcXJ5GVf9`!Mq9If+7_m9KFec=KWrpX+Tt zztSrLv669`C*Qp-jEtG(5*hx|_$)juPubO~x7YQzr{V=O#kCH5_|v|Fl{=xs3HTCM zjP@-aJ~7m@Cl&@Wj5FR3$irTN@_-NfcxkVWdP5$4>tx3^=qFR3+)dc>0M=x*MQqya z0sFj0tamHDakg7tP%M}0Xi)T>r}Ol|FY<2a9NJ=~A@18+fr@|WdO(&^_jFp0CCNvF zt_2+9#oYT>wTlr|&|6pEYNo0KLyT%S{)r*RtzViWZhdQFp1=NzOYHl}#W&@*E-pW( zb#Y-qE8Zkcgi;gaI>IuzpMmX3!q@WAA(rEBr#v)aQCMJ|LEZhETHN=3x%Sw-Z>>Gn z5!F0n>ATSD{7}wit=Dl6vTu1H)Z@95cyN~VFQL@Gy~FQ`qeT|iW3)>c-;N6SuB=E3 z=#R}Or7bvYzqALb$A7R=E!sZ-UnnqrQ>mrO~)@KJlb>q>OVgl zS^d(l*|zi<-X3)QQy!A|XT*o9eUE%Q7WXl%F%wEn92o|{|7ncre2)Hnm&&^R5Azwe zd|Un}M~42GdWi`wx;*gTns z)~9ir$X`A~E{P2@Pjt2C;Pzc)5cASk{o zbrT&(2mEusq-j>f#R}N3Wrq_6Wa;m|%CeER6F>Y*`=#UlFX)Z zyojencH{dE+}EX?`wG@a{vK!?C-Sjg<>H(6OMTpX zg3oTPKR{hr_myhTXu?+4G+Fof^I{L>;zry}inL4}I=7^2HRS`WOWCaFoJ8{cCHThJ zsf8^{E%yi9Zw1A7{uV}MxR)Rf#^HWJA#pGt_7r#d5{5wDyVRcIJ1-F)G6mbB2)LHv zcNe1gm)0={UH^C`+G7#xE$;%o54#ex@5+go0~oUpaQsB;m3$<5DE18ag)D8F{y6VQ zt1_Mj9Pii6!`e!p4e;?HX`$~1(0+3mg{rk#lz^tJ)Z>6h9(>DtIeLwUerIk+d4;qS zbjB)jccEQ(g6?CK7x3E-H2h2VX-dDfE$0E|gPudsTf^lgrIka|*I;_!Q+RD2lo=sLOq zU)VTSK#o7PCh81|80-K8I0wP~d-AC`>x;S;Aq)T1+x2o^GiNjB_WwYZ=z=WqSidZ> z^YSndWylg0o#S$rB|j2k-HTdZf%%C%A5*4?bmWF3pZw*+Q0fD@|707JF9cn47Mxg{ zv%cbw}r#%lm^LF#S%^(}7PC+w-d*zED&^k%-l{INSa>h-#P z%^$ID|LdKkORw8SLGgu@1yrAyIHxoF(60}G4;s=(U245d52e0#ROVYQ=hIH;Z>)Lt zB+iZ>^|t~i_I{0(q9W=#1zS|}fuhyMA^1oOM3-M!!2XJQ+sT6=4}~R8{}Jlh%tIiD ze?}bpBDI&C%{j2L8giOH$$Czj3VF_naxNop^Cc#E$>%6XpxFkMP`#?t({H}ENoX0T&+a3DqXj=Dyc!>FVw%4)EsOfM2_cszBWuu_DemuYZ zdnK(&eDW{7?+?0eSU)(e-Hn>o#Tm3#hJnCFYe7(a-p4r741Cf5@ASSW>#yj&?r!4J z^lri@|I+l@=V$-i?0Qr3708xopE7F=LEAvj_qH47iR;5af-w>I#qkS0RnS>1X+~SR zA*Zi^y_!?m2E<)F=cmmQcu9WwLa~lETAf4j#+!!qbsEy&D}52e2DGDp{>|#A*I|u? zS>q)?wCPj$W(&rV!<6oMmaqb6QwJq#t>PXN=U6|L|(QjkCV;*vz0(Vj4bi@te zUd!;orXo8!)PuQdOjrF{yA^hKp2mfu=)}K^K;*G(cU`F%?x})4V4)~P{zA+hxi)b3?IgM;HVFwk)!I%z--Z_g~;Rh78e$HfFpJ>pAQ%4BihnWczzhT?P9I zywO#Q`1&n!EwG2iygOM8 zb-^m~Px6PAF7g|5BHKXK-E=pWu*?-o$6z%h4UH#kpXuDjr-KuEyDex(^uA(m%NIMfCz_$}_F$B(h+}NkM z>OUEhq)krrlS?C&HDZPDw`$x=fAfHmamV||H^vurz@pxSJm=~zRb%0L+|!inXa9L? zmJnM?;DgDPe*|T=k`Gc&=6t1Gspsm-69;m51`K`eBk0JRU`lFq^PxGfJ>rGzT(V}=D=e=5o4$FNj#%(+C1f8QK{^(*cY0lzz zR%1-$pEyDrAiP_R0#x0uJIwFv}jI^GA=?oxORZA?;!7C z`JvR|^GI_;r&v`UfLto?6VJS=q4S2wU^0EQTIx75NT^UFJwzO>8v_%wT8`H zF8LPgX}i-M8#^r$o6J4`e=qV>aO@euXjOY4dV6~8&7rJ^F)#f`3Y0D}A{GM)P0EQybc0zk5WSQ-1_J@%U$@x?dp2%`Ec03wZ+LZqCcuRgk^2 zC|Aq&6=+{4?rS?|UKOnlaNZvBRF8mdh}l;E^Kr)-OtEU5DJC_Txx28Jxdi!kQomYn zPVb0L4vL0P{E={n7>2p)sdVQHe2-*2!iAOKM+ie-E%~n#-$Pih=)v4woKKO1cRVTQ z)4^#G){}LKIiWlLiaJJH`|B9}?PsMJf8-UEM+?zi{F6FGYYs_!3G^-2uLrF)Z{0`Y zaP*ULQb*m4Z^##|q133QVPuBBueG9G>R*iTC-_A^`uxD!2iu8JQ!E%?JI02_i!}Jg z7$s!|jF))ORr3Z=@h`ou2Ph!I1gc+~j8Rz)!8abYCGGS9u^CKiV%F zx4axiA&{@ySWmUj%|1YROMmZK`=V$%u~x+AVXiJdB68-vo4eX`k=z3p$~WED(&p*Y zQJQD8O8ICXhbdT@?iH-6dlI#1+4q@`epUECVZ zY0xyJ*ej}GuRU~1qhlxZQ-Aff)MnQoykJT6rEfkJJy=>0eK~zsw+tlUpQ9DO6Y&e4 z?^wq=`s<Wg(LNS?wQ1PT`tffz{)Hly z?l^S&@KK}fl~=5nIzZaTdFCX#R`VPXx+(A~kqtS!FOo=l{5M_diY9nxF?^JDXF1SU zNxz2H`O*^j{N+AS!#(pAa1I-s+{LsNVee5fMNgwQr*wwwa0-T4SdkAa3IT3sN`79f4;jZ{lt{)EHCm%tVAsoP26-te)Bu?bh zdJMCOI8+`pzh94GW>>#}z3-93?~y-;Qs14T^TFOWt?Q6JJ=u0AX5`eay9BpN-zJ@NZ>BNhW_wfr^A)7GuM{<{hQs3UoJdhuoh@;`a=LHY1 z=3AV1yu)}|-vMLf@MoP~@EAEx?pkEQY(X194&MXWy@Gpu$k+kM=W)oLO8%_2$)DJN zgXX6@p0e-1UmHbS$bUHgTM=NppbZVx7AqwW0$*Q+b+(=IP0%&X2XOwS{jo?LGwmc> z2V3{uvXAF{l2g^c_K%c2^sCqYk=KM#2w~1#`$u}2rq=$chw;h3^x6;lJweyh%e#tC zOM3*Kk^2@Kg;n5}lXw5=BA(^=>nnFExxjFepYR@K7w-R+d#fb9-MD{__*}f1;R`y? ziG@)B@p8QSRvYsXC+kpNWyDuM`R~#`y1U9)hktRzHQ~1z|C+!{mojhF3&J<~trmB5 z31t6!IOgwYWIV?+_dV9P?_K)h%Xx0fnsZi=uBs2EPZG20V!SgAoy&$QV2*S&-XE)}K?OMIz95E4l z((HL=@_g_q!memIK0C756uwwZ?k>vs-8VqiqfMguULDf2Q=dcLc}UC$J~zUn@H-!L zIp@{@-wk4chP|#bAPnSP!L*e#q}-I>d4pJnJu%~#5m)>n)OC^Ax%%3N!Iuny{I#Lf zIdUzLy!I~aeWf01M53ZS`#9PwJN!w00o#+~-2W@W58E3SkTsNyj&v!b3rj{Pj|R*} z!svNQS%CQ+IxWVuigw%uRdRxQ3rXdZd9= z@=HAq>z-%BE$4Vf+vR28zBvOo^IN#5Fb)0ci+_$W4EljT-@-rZTjDC>3Z?Q!X&fVL zFovq%*ZMN>Grz{K^vuBR)AKiFkoiDVYiIe!t(^?>l)j1oV8*f1U?2voC9}z4zK{t-bczYctP; z`5^1n&VMZ6I!AEkePfPSu7S@s=96yGZUt$Vbn98t&&mD}4;i0EtgDDQM`yWM2kn{t zW1fl2U?WfH18p(S#Kp!uw6nnI-=J=oeMG;p^nrN>%szr+N}d6;&+~-I_!{gZ;F|r* zQ~m4H!F)?y?qv;H>$8}7DfHmzNe_LlW|@@wX4_*@@`hVr4`f~n?BP9O=N##!z5zSu zgOogxb*fJ}LjNhS^HX3y+Dm;Cc6CbL=x)4oZe(5x?9nN(&G|RynDtHAGXE*{3yu25 zxZC5sv|*Mp=G>mz|HAGz?9zYBoQ&l^snrT-S#P77>V|0#WQ zrofi|r_?uL3;jFWZRWTYcf-yW`cHvf+!J>8X7|28#R`LeJ8t;WteZYLEbE5gS^R>JjxE{d5Ayq3Yd2q*epZX znU?zOzgZu)Bjw8rnUtn*OUfn#ewM8N6!<1zGT>)P|1G$aH#3j$8x(%I(l`IXvs=+V z^Cv0%vljM(zX_jZfPa>?|K|KLkMO1cly|iw?N`*z}PzVY3WjuQlsq&TPyF9Wsxw?>6%+<2~1aeJ1*68N%+3{+UPELjR;Aw3}_T zyTE|m&x*H&4ww(PX+r-gG{`)_O?%IRvkCWB1vgFRzncb?zG#d!%MkW*OMP?AGLNwD zwB)55I4?3_%lw*x2C!{=PE#MS zZJT?^3n!RM9?Sl=kbKCc$iJ*pSUy6*msPVu>owO%n-sb z`fb)1o(}k#MqZ8qJJJnX>tJ3A>_|`8+FM3_&Ld&7z5!d?VC0!{X^92)gT26Bk^)=k z--0*mo3O7oU~@gO-K7@TV|szTGzGTMzXdkyE7%GBhymNA&1e#KLjS-5+uR35d%#ZU zFZ5F1g#BnrUcCi&buX~%Q(({NrM?MU=wIx72M^{N`#e+cjtVU#^u??XT|dJZw+Fi0 zKN+{QZ_?5OJ=;Gi{kM!8>l^K#eA$8<=VZCS=KeBqa!oI=mnUJXGSYtwY}QwLRN~}~ ztnbd#Y&Cg2L-{H?c(SsGgzVQ0sPn={ue>OLeNrcE8hxFJOC~msCaf?0>*3t*`}F^5 zGi^}5uBxMi*KKFI?tJ+Tdr_*b3Gf`SH_;|zPuggdZ3cIs+gKI-`$&Zj_g?I&{+JK^*hvjdF36BQ3y>5brnO zY+LPFZY;6ncfa2tHh~+3Zy0TGK2aC%H>f<}8ypYJm+}j5gl@(68zldR14;baF)poX zvDG?;TzK5r=iyhY`ou~@`P^)>{jfFB@gchwC zKM%N(_w+?M^|pb6F@?Cra26+RaNivrcNt-nT%z4ua+MY>$p4D@abWEZ|6N=8MnkI@3J4jTX?^-7QR&3#}yAV zane7WT^xZg6V9#RCkh|hNs6u-CumIPc{$UA^WYa!i8Be5Lz#JmYpIuyv^&i-l+X9i z^JtdxS2B;byW$99m9>K2)@p4Muk85D<;=qwOdI@=_l;kYzOk2beSF7fip+?;F6Y2) zfo%^};r%tyFnVwSRpQFbTSxL2#+CuMEg zPLywVdBBhI;ja{df6{pEa0oWu4ulpBm3#26-LzcVUuh5R2 z7q-X4=+E}x=Mw@U_>EVW!v}tpmbB{!N6Nh-S(BE&lpB5dmrsv%!gtRG`*l`m4*h;l zK8G{ySa?HzMfhpnSEQeJx~&GbWPdcbU-+}J<4A|Wr}Ha1H*4E*M!pmF<9--x%u8$< zY|R@(`Pv=f7~TXJbsC%)AC6~dJ;(Pt11I=*#4x7X9l&E}__L|wIFD?LZH6X$W!~5> z=dMRzM|c?BXKyWotsL~# z7Je=JCDL_xKm10qW{h>}SH9JrJ*2aeGRVZ7omzF7+#B7AyQI0nobBd4(pKD;v!~pT zi~8b6h0o*dL-?;F?o4tW{V3np^#{qrT44-jFV4UG-D~MbGkjey9$>pj_i{a2>fVpM zZOr33L;joH^*($ZdBR&jGk)g~8~jg4ZbwmmY-a3hlt--ASvMnZE&Nx;Q?3flRQ>2> zcPrKpyf82>%9o|%^tx5Bp` zXS$###K4%f$`2$E%d5BiLgZJUA>WVu*=NXKhJ4rQ^Uq=X=bs@T?az##As_9}+;xV0 zv_JFZ)8`LB`!iRaA)oD6pCO;^&ptyw+jk}Nh5u(jCS~A_;oMf_4emYf9Ol`2&l`Ze zp9u}hoB?KWEBq9&h5+*j@`MIY%{zyALW8H~4Pc(gqT<$wq6MLoMZoiWx!rL$e|8&j z>ZMGfrOcGx%Y5GoXT)jBSm8+jPi?XUF0zFRq{O- zZ%oX1^jpffP-gMBlv#)}SAR>HRVY(%<}&9HAK^i#$N3!KGhO8D8Oi{k=`G*V7Vw$A z>da*Z0H5ii-<-a!0mNt8x4Wgn=>H;0+2=Zj3)y> z>U?}(*7*kCkd(Xq_3)94;LMkPArd<)uyr1Eb0>Tjd3WJ?Y1@pUL0Dz$80#mkf_IHo z9F0kJY@0s1z3i7fJ^h6|(BU)RR*DlmgAV2Or z=)Bpv^@CjKR={uO`M$1ccN=x%;pDzyrknB(c?-=vr`i)?uQXHMQp4M@Q;aC{*&p4gnA$LRe3)>ze4n8+{wT;*LY<1S;@DmpvrnQxQ*RMwr6gCAb* z>-ymywm}~i(ew4a)O}KXm~aO(uHNo~pGYg_AHFLN_$|uE;;Zhs406_t2^E0c)=C_| z)GH;n9OD@v=4-(lUk4k!F3*y*;lUQ=Pwms+?kBiLop-w7TO#{k`i3Qb-0Vja7#FQ( zLq;FQm2}qej$Zaw;>vv%-miHy_J*xo-Ybhp>=b!R33Q74!p}CaK4R$U;%k=hZsTwC zFXfJ@tZC0_+!_Q#98Fl^__X9O+c4p1|@R`z@z0V8{Z-viMOX&o_7W%Kb zw?3<8bsp@1BgF<0ZgB@{-d?v~{@#1}pUZxavvZ`mS3dw7S-UoKb^ciYLD(x+XTKCU zh1!I{&7dR<$^%XhO8>Ay?pl1Tr1A49tZH+2fT1Ua)ZzS)`P=Us^vnL zMx6BW0npj|8u#$mpIX<&H(OcvHue`|gLl|Z?mtca1yUdS9LIxwcSc=9I3}ns{b$St z=>z7&tBn)h2VSs9VpK)o1LT0d{d>gZ=UYM1d@T??KgPL(zPkwJ7ZQh>E$7V*>lX~b zJ&CE@Bftl1DsZX*ZgKpb4w~CYy7uN=l#Am0B>kklIfFNgzxw0D;0u<~$I$77_I&!A z&5EvESGkuObVZ+&#N-?O07AMVJ#}HuNn6B=wB^&^8E?>&*X%#Xj5OtLguiyz`Z268 z{ws;AgqNR6Q@F1l@A#7Ckhh5SW|TftT7AhgLuVBH@fFS!c;rvie8Xo0F%5#D?B-<) zwW-jdJ(hN{A9X=bex`-6iXWGA0oafpkn`B-(fqMWf4@?A5NP36ccbXjb1`m>wC8zq z2DPIqKgs)4NDGNhyz7c;;1;)z8`^(L?l~Y9icjCNm}6M8X6aVo5pqw&ID(&xUtVI; zKtF)W`fNy9pO`PTKL2u*W63q`iNJRN;~-;>F%e#+|3%&!H1e%`0F1Zm0pN!CmrrkI zJF3menf!k0c-!9b#bY7n)m@|&*UZaw)O5_n-69WSdt8b&80hx@m1 z%Y*@SgSAb;{C3_)*fiR$$==o3n5#whXc@SB#Jdw2u(xytwq88kRZt?d9o+WA0kM11 z>b=?(GuQ{VTX!S;vG?JQnnTOr9MI1bW3n4KWY6$+)|NeiyAK6d6_U49kKERuyrufb zoBu}M0{ao(Wo;7vSNw$5pKFri%{2%=s$9mdP;~P{kwLPCxyG~~#@Rk>fAmqytHGO7 zq0(-ig13T27Z88bXc6y{WYy^B!3VQR=r4RSf&0*kCI}O2S!5k)60oZY8*AEWjw{C# zeR|H<_0%4fr`suG6y07h2*vqH-mwhHdv)hG8S#qbP6FQ>;GG~$h+|^+1!i3I!oxG`-|G|JJ26m(^7Hq$+UOrxwRh1EO8*Q$ z!FBkDF?Tz3y(&-FFE0b1!v4?gp?_e}>Y^P*dy1Nit}a?p^k9uE`%T)L>)gD@x!r?% zS@IU4SF?@1I03tg8+SOL1)uCl;7v04^Ioj(aPsbEb@o@Egz*j_;@tShKWy{T_RKN$ zbv^m6A03C!W~4Uyr1)I=5}qce!M>z?G4xlf>HAK7%cs9$zRA_M2!(LRNzS{JOn-Hn zrJV654`MKN!x_WxMr@ZAcPuLVVhPx_vT&PcT*!U_((|3crV9++<)zJ7j~TW5Up2;XozTCG;hTR5a9k9QWjC_D z+PRMz^m{`1xY;QQp&Wx8`b~GduB|(dYivGwhwUGwhA$;*29FR6a3KS)Qp? zPojd!Df5>e-ay^R6X@ytyjIpfXxF%RcJ3m4 z$Cy}Rvr-P=eqhN2zOD1)m^bI#Op)Edc`fYIPd=%wWBiElecn+W#aM68UOXH!MCK@T z1MHlLtIgP!xQC^6+=3WKxStzC8s5Mg5<~hO3mvl8plm=tp!+D?VXw5A_XcO&$oK0# zFLFI{otbwB)xE*S*&GYj2j0<8-wD7Ku`kQ#JDlz5F|?0(sqTkky0(6Bc{*&aMFWDP zJb3>dF-=Y#C-{et&n#>8Xs{cl^&G1wj<6l9(c(L9@o0Z|LtBS?sVjPi;rTeFZZqr7 z+?$<7`ChX{b3F1f@7fPIj%0pB#^Vi7`eLU?wqVS0M=zA``|X0awY0%_8#z?qsTh+D zda}E6t?>aMn;1W+m-r&yS&;Gj(4W!Hs_~OLjo5a+#3Ch&aWA(-%h5u^vR|4}u04b? zk@!wS@OA*xEHs{sJ!0PHW}J8G69&vm+>PzayUK<>Lceh>7Nrdkcl}_~4C{FJaM<*5 zy#ILVQ#L(Rc6{8B;Wj<2=T(FXrsRfoxzkZhJ<_;OA^zZ=HHdi*zD^z_d!Mb|Md;H; zqfZNciC?<>7(Cc%jr(WHk9_2l3x;ugex z4$x*K{3&F26>QSF*NNXf;;uTXz#BguG!C$_XZ!dz)+3#GbI$3+SAHxpBF?#+JOgo4 z`;%|DTd^OAXf}7tZ#(PeXe)-4YUhO;yx>0-PGdj8eFop&$_tg!wyy6c4+DJIU(X5B z21;3^WX{RKYR`}O*Zv6k3(jNpoq~ZuqjJyDh`WJz2}DPv{!Kip_P!2zo4wVbi^X+D zd@bx70yBn#pA~?g6*MviBJQyHV0+EL-h%vWl<1_`OUPX|;eWlC&sF6MMF05?<@1d4 zvi`n9`Fx}N-F|dh%7^*pQm)26nthP|^z28WQSa(xJ@oku{csxPh5wrEa4yU?QpU-n z%IANXgfDo_1zt<{pQeu2sPl5qI*v2d@f&q|^#2TaEilT@=-CGR9M3#ni;Q~R{Xg?~ z8FAT0O8-f#&@phn@aBVTm$Xcp_U4@3hM)W-*Nb^K$DqkNlO|Kf%&zEB|3=|8a`7e2BpaJiIZ2fU8bcuJAHP84i z=SGS*CzTrzOT;oS$9TUMTKLxMcqCMP!yIPEiKz>f%g|csoJnwNIYh?rL zl{g~WA3oL^u@~7$dGbP;wvKU|Vdq@0c-67n?pWxd?$eij^yc(?M|79O%I~p0_||Ux z;YVlz^UgO2(yqptpVGbhjThYudKYmyw1sz2I_nsV!P$x+16#3YufqN1y^xJt2aSV1 zki&PkAZuasM8C?w(_w$3+<=S_9mJ3iV@(~#)L+7X;T~>$2k=YpDI57fI1c>V*nS;QB^J`yJgpY6y>J}4d8y|Xpv)x zdBJlN_K|A}XM%XgM@z%pJGj2geq%0FoXB3+n>I+Tw_zvzXvpN{gxed;-q&mrMo(UC zfkAwT*SFNKJ{=CRAjh-^t=O=J885_)y@jz0?!CgKk;SAh$j9woZRkA0 z3JyE$0bLY#%u#dz-DkVGE33O%hWqWo`OfXIA4UryX1u9Y?_!sIHc>-)D@YBed7sL zH`m6xs_lsvpP`<}Jk)!umwLr#s3)=y^&V99vOEgcxx{tWx8N!|0P0Rtb<<>={97HY zd;7Q46O%Le9oBBv`KXlhOKN0W61vkqjrVH-L8rD02- zN;u9|WlQmIg}lR@Fkaq_QTMNQA|3+w#Eh3mo*Y8#Y6A!1{f2%EzL5Qz$tN(^-85v- zF!D=8mu^K&)5c62X{l;&u~yYm+-mZ7;6VR44R?z@iq~Jc$FG5|OJn3UyT9AmTrBcI zjg8lH`mxz$8Jml3Y{&HFLjN-0@Az(s!(rZ;?OWpTAby1KKqHP}R=11?Kju2+Ud@}6 zHBI*H*t0QSWL*aKYfmNjYhrg#?$uxm4UL*Kj&E{?3ceV}7|WrOYXcpVG;PQzn||rD z*uP=V#x?yN_G^t8yH$oxfc;vf$oB8BUt_(;jC#raTJ@Y6SHT~Tet)uG3+vYg0&mEE zZ4u*1`;Lzhy)7w+Q}$`$&!IO)$1@IFiX2`xkYgPx!5w?rTmdV*!Rw^|5B9@l;d{K} zxL-s}kU(%xjCTd4{b1AQX!DA9S@&Do>_ztfW-aT{Zjs`fgFWDTBICe^8@)Lhr|iG5 zUJ%PN<4&1V&@A3(>F?hA+fH{Q=(JYb=)Os7}P+V*g5!2gZ5 zHoRlkQ@;ssqpmmce&CAa1SOyIeDQ@&(VEW%>4z6*udil>CBxyn4io{S|%}Ok+I@ zznbLJ4-GyIziL!*S8d%oZ$DQailzU zx0?2?l@+J4ZyisyZ*feep3u$81<87+(v2qeFP`TMU+R1>1y&njajeQF^ne+`Sgmwl zw{qiYU@JaI+gdm5J25t@y(Dk!tW2gQd1L3*DL6C|H}ZVM?kXV-i(Ihd9UsC^+U`ED zRJOa5-&poP-Lk`2U(~g{_LFMwOWDzLtTy`e!@Ew?C$-Lc^y%2z6kKJzzT?>2dXD|E z8L6<)PuO)}yRe_=j&GKs=!E-Bq|ZLI;dc`#|3EWigkB&A5$m0AVD1!|_qc64e5EHF z-(!K$;h}1-JH`YS9vnPKgHh1iyZA0)bP<27)%{a5nb^8(16gF}A6Hj+J zAoKB@0%ENt?J2aQ&=1=gos2fENa$I-qnmZ9g^qe zdJOTnTihD(+%aWG?Xf94hzsLV*X{t`J8UazkKykz;Jrgbe13&DWZ>(l(>MHN3f_&t zJCelPYvAn(UH|t17)OkkULU-0y9eW$>%LAuNk4VqcZa(M-)rRif_&Ts^3|^1 zuP3fpFBhx1OfzKZ(ZQhU%-W?-qM!S#jl4X@iBj|Y%?Z>A8|j5cS_kT6hll+&;KsP3 z-gqP5YpM5^^CRm1h+()D=X#H)`NNkKN`1ys;a=RQf3w#4VUIF_Udn{nR_&Aj3H(0Y zWwf)*($0;RJ{h*HZ>C6}c)v*hw8^N`YN=CT>C;Z5PXnc$PhVx7#YUaO-F5zT)VbS= zYr6eZT>tf+b5D;lj$X<}*M!5O`t|*XZxXK4{t$ zaM$d4>eNPmgg%_n-!DV@qrew^A-{@GA|5~KK08j@T)p;A>NjIV1_O>MTgrywdz#k2 z^b`F1Gybi^za^G49-Qw$2U_p0`CY5~y5FyNyMEv5&imcYqGP|`S+wK#hl`pYvlp#+ z%rWF9?L4kI?Ar>-$IWvf*t{y(r=C3b$i8G+QYIaF7irm2E-90ayo|KIlE!y2L{=W5 z{8#WYwwP&@{~0XD^L(Qm_5XB9!+E}uM*aURNrUf%kv1cl2Hy!IZM>Q0T$AB3(k_Dz zEVf+mkRb8}2jtH(^7~oR7OAwuaZ6ggk#+!S1Be6aij0Nqd(V=#$tc%iN!xCuZRjbh zPCmeNtPAjxj#Z>v&cE0G@8h69euM@|Ur3|gCpuV9{pK;Xr#kiguDxel`fe-jh^+Nc ze|rq{u{zgXvzl?A)j8ml9(ppzO3B=$&hRzoKSd6I&G}dJ+Fg$GpF%rdEB%l9K$3R8 z=KQO2-D#l|{O5M-Fh{@P_;>3t`_?G?kDmMOI?T}!#!-AHdhCBAoP*fF-uJy<9dVwr z@BL~I(o*)lUu{EL%D(rjX6$>_8SAP0Ug}0_-<#aeesy=seztoLVm}HkMn1_s$Pd=T z-j5j9Ui&v2q+ci9fjY#TKJ+QxB`VLasJ;<$_i^o@K)g9rDCZ%%aY-(yc|eYoHB zb%n3?11$7}Nj%4>gU=H8$+WjpH=y5=_$)2K6Igw;48WR17|J(fayoSgz{nvC`1R<3 zW4?Kcz4fGPd|kC)syyWL{Mi1DXX#$xeG%gE*5S?33D8Hj!9NauBB4>q^P)N$|L&T z@={4VuI6F)fn~Mw`-`<@eB)5xk1G5meXPIZ8Dxq*^{f$o071L<|J^>k&oA~9)t$Zzx&-Yx+X>fo6Kn^4;4g8L z*bZ*(-A32)mb3xCN3Z=zKAPhv>GSBdAKu4x3|{SYw=@-!N16K*yi?%-o#*lF4*m0p zPQS&tPawW_lV?RX;9Rk*CgcZDmREXKrk^y&y0LAH1^f9B%yXuq!%xPrzeYK;zcx$S zGF7hQ%Mu6TV$ac5;$DQkvf%H7s|%LyuPk^)V|;${3*Z#MQpu{cYyYR(jy(G_+vy*K zbU)HFh+Cz7{dp>{4fSM=&Xx71@P7btS2q)PX&=18`FN)2w!|+6U!+58_1GbtAsxaQ z9N*1`+?-Ch78ox9hV&mWum<(lj69qlPTq?=p=;k2eyu)$H0pP`+D@f`&J!2_V<4E)ZXihHE^ZlfN_ zH2$^Lr#@leyPU9a4u8^FHSJ;xOzdm%PEZ5lwu3j*PtR38(;2+QJIb5Ab%P(kfPas- z^$hr@_H^Mp^S-UtZ_M0h}*R7zT@YM-4knQ5$_1Y zH@Z!DE$$`Ie&&pN-$vgcy8_FJPw#8(mD$WwGUv-~{+gz20fBAf`UILLZzWEl5tria zfG_89?FGU(>xaJq=gk?-dPv@3I6i2wz(QGhd-CzD-^jckACzfnV*#4ur<+EM^G>#t z3fCM5D}N+T#DzHdj$f$88sCLyCfm$=)Q?7Dl^B~|&jDT*8dv@P;tL{#Px1^Du+zQv zFRIbkO1|4Ly$LpBp7Fq^0cSk+y5U;i*itPoQl;h2utzSU4>jZ6BW8gklsS>H3b2RM zM_^t8u@kr_^*YX0KGu(Oei$=dejC znUEgr@Zb*Fug9>y%MIR+_Xt96;q9xr?+Wk!S3tG9rY$&VJ0A6f&xtBGMwQd>7PiE| z&UO_{`H8a*vCKhJWjAa6DLO1na6y9rFQB;dKu6ymp;k^|VwYO)# z#P68HoErGo*Np`n9MPlhfZBPF1?c-OYf3+HBkWCj(F!dWHYdv0oeDSIj<(Pb{hQAl zv_4BVX!fG((<_SnTAy0bhO}2|w%1ruI|g^)_u@@ow)I0xTO;ld)J}TDXzTcqqTA8d zHE8R0)fUb!&T+pFp4hCP2=67__jb?^BG}Yc5zg9zdqZcp6W0Yz;01d}4Jg|S{p5A~ z=wK7att*rD@%wUl-x1|hdg4nX{fCU@!ZgD0>4^_8#=~*1-BD-y!rNo!nRB{%zujw( zZ6j|wv<18$@fF}FPX8YA{zcUz5c9S`KY-tSCkAoTvm4zVv}GjD=h_tc7o8%uBYDF_ z*5&QO8-=Ih-jFjoJ_$1Bb6h||&R*RYNwY0&Z0G+$z7ll&HaKDJ2O z09?R%$Jh0%6U;-ux_K_nl>^UYZ{?fKCU3!7B#r?TO@7D^ScZ9~j55ok>XqW0SdXK=htz9{b{f>)wXZ;`LY9@p|j z4uF@&pqB*0i2!wzu-@)GRHwHG=O+S@TIeg#Q>d>fT_yg2(N1#z=Cyy8eE*yLx4WH_ z@sxJbzH2+YYlw3eM=x#Fu}`qwJKd|F+HO53gAcyw{lPMcLs7`Kf!hkKH#^qbe(r&= zPQ|}ER;8~r_S^4(p^xqyGAx6{iV8zc*_@WSfXyLwJzVM1YgQk>mF~oQOAoI zx*f*tRQ)c#`_z6MP`0p7&<`DU^+Ms9qB}qjrHwj?tD!4TmAwggE%_{BD+Sz*lo#b! zx%0|@S#+#?U|lhJs=UiK2|P9BY#niv;xG2|0}+d_9Ns;cRpD&39R zc(Y5#J3+fHcehY(oTTpy%jW=wSr>Z^eLLnD_SpSn0iGXQBuA|=w5?GG@Y+9ZQFpvf zzDIfJ&P%=xyq<0O;vXzV(d1jd;Y{Ei0SxGseDBxSbzTYc746wZ^KByZb2RYDErGlv zZ>la|y5AWsUt5X$3wWm#F>JCv3l>zc++payU6{XDa9?~)Hyo|do82OCOCalA@J&>F zwEVB!SL5y`_#pR;|KImm2Qr=S8WuvfYPbWIZ_wQXrS00UN*lFb-F!?dyt!FhDen{0 z)`LFyy3UpP!aAgH!X1dcdKqC!EH3iju8U<(g1^Q+rTF_oTkNB`4vp`#EHoJhO)kP) z9Jz`nt8llzRCF8z_oKiWw07H#YzK6{zNZgKCg}XXMlg@%)g8iJHv!LGW*TlKFSLFVj5KQ`twpjZox4U&nmdRJ7tlzICvOdFcCI;~fOioob;&sX6(+ z^uJ~#^rK&0$a|#i%XF=nu@k6I3mqZmH}LwBv$h3 z?;$|v1?|Tc5{Ldd!~ycb!QZv_JQ={+lzhxF;+Xd9V7?(AR!Esk?oXBhEwId6EJK+g zd$dN)gS9!#cXC}F(?_T4I@Z4vbS3s+V@|gDx)!WrKK3Jbs69jT3I2wz*GPUSHiE6Q zA_~?s)0o$Nw)AhjL8hixr|;xb{iF`gnF8}}eM+@) zv8cp&eIy~eU?@w#1hWB`PPzp2*)BLB)`$>xJ zhUHSzC?iLY1nd#yfw=QuWr$+E6t`f+^E0u0+!*z1Td=g!>oSow=ZzT10|s-1vAQjPu;_<#fm;Q>bM4Q<-*l`=>RkEw zT|s=#fX8-cqa9H)OgyeU;0F?bk!iv}JFnq;Bj7l~`CZjNcp3DGH4oUi;~&>NW9IP8 zKAvlhgJ*mnZRs=g?4Pr(dsSPr_5&{8iy_Zw1Dxk-PJ|o5V=`)5V)xXz&RY^5rQK0; z;Kes ze6E8{9Oom#dlilE8Y^-P?vDV?kLbam3+m~MNNbpjS@?M5fYvCb6Gu*E&gbbzca&-aXFcx&{ncM>c$6oFV|NJo8V;m>k z6`GxG(ylLYg~)c?W6w)%hjeb<0ST4*gf@+Ob-*ozGeH~1!NGeu)N>ibvFlUYdf-eO zB5dY6v4&D?=4ooZd+q-eA3)k}Y1a(mUHag0H|c!@)2T{$Kj&X#EfwQ0=ap*e;WQ zl({9`esDDUu-i2SzT{{l-dE51sO@(ZwQjg52wKFJO#y z1?Lgt;Kg37eld6;zE8sU2+xp2$Azx#jLsgT_MrZ^{AjSFOwD&3FfsmVF97BXfP*oD zO=1Vq@a{D7Hiq!8R`@`S?O{CH4SbXK6_X}$zX)@CVz9IsoqfG|b_TjK_P_oqY#VJL zmVq9R(WcXzeFM-LRUUOZp=42Yby1$j0bwoXe$&?8g|lay_HZg>GFx z&N){7(hKqZ3^Hdv?@(5D^S2)?CQimD`4+m@{*UDt%V|QNAAvp-_iyu~5O^&2C%L2* z;II+pp2L|J{kUl3`r`K2&c-^`_vgE{v+;iLZs>Ghf|jwb)3+~$zrVyJ(i78YH&fp` zN931k{kR_%UKPRlY8bv;9GC0X!QL=?tHdCQCxjo8?!%Km6@6)L6a0cB`pH-W&RMXB zq2A=v54=Y_xrV4W1*)>!F`xO|H&(&Mg73o#U%~9^U|ESvrR!r^26*n5HW<$gI+;5H zI0k&c{vOLSzveC2LXaP9`bWUbH`*6@A!X)o-bx%;E>JZDclz7YST=a@LUngl-@lpg zu;$FO$?iB^_P>8GIsV`eK^;6eao=Q?S9@UV9Q-|hX%%dR7pgg^**21TX!wVPn;oUGo*)Sp{=gE>!~8_0*$JpDU#`v#7m z{s8Qpk-)rl`)l;2`-4qNcg5Y!Sv|*LzwiRkVul(6%8B%+o$at;hwZIcOBpqKXWEdF zTA4GVWmFIAyqSKlz}b%9n(IQ0?Y7Hn)iP~eGq5ZtK!;(3N9&R zY#YSqz+I^XXP8~;|VvcEW8{Q|^q2JV?56W*AoK!x^`7`BnQZ6Uz8vrVG{XiCPUuIoD zxHr^dte19k9k^S0f9Se%70n(K`UZb*<=m2B8W$>@9o8CL) ze$B80Azll<@{>H%^xFS>EoiQi@u7T)du6?)tar%Q|8BM7n_nD;9-u>?JeH|!aQe=r zCqX~J#i##!A$4c<{hJc5Y3L$|F?`+SfzZ1BwA;>a(fVUu+Ew5FExew#RjwJH4Rp`h zKEXY(@HhLwC#HhGP`9{&ax8o=;z5mE`ZsGmtlKP0-PwM06uzoD%hasq8I?8`xFm>! zF|K=9M&YXG6}N-m;|#!mFrtO;75(Qa6P}5iPv19>bpqiCXgLbIu;_(tzQk?GF^^YKzLLJe zEwRpUOZFYGXWD)7qhjmnn4?A1IZ0EY3pQ`8ecJH(UP5}qsIuvl!h1^ux{(NBa5rtP3lmjixY#f^10 z2cl=U`go0T?* zAMwIHiV1>KBwY(O4Ea0!2o?pSwiTuy8gO{<^u8CrJ7BaQ`3^c{-Ty=T)cgCLp?z7` z=>Hkp7vI7sh9&)$lzx0f{O}@6HM`td*7e!2V2J7~9Yq~x& zFJowUrM5jhsNES}t=%zfHTLOZ1NmwpVG$1X0m6b#mH}M=z8AZv5MD6c3Yolr+)O>I zG&fegT!`VH5Vuj|?l!o*lY z@7y*TU-{|r7Bu;i?46?5ZBNoC{2~$8%MKXXO159S){ly?oA=@RWgj99i1!Q~60-hI z*xcn_DrHMC?yXkiUZ@}UV4Y2PBVokoaX!7>)WIRY+q&;Z!S77%@7ib&OrCoyy7>Bl zxvxWC2XRk}zZHm$DQDCiPr$mh!@yPetL*Ep`%>Y2&6mX4kWT~E-^A?4Uf2BYM$sW( zYbaIUN9SBiJ5!UAuJ^>R~+u0iK_v-<8+?HunHysH?LN#yVR` z-2(UF2OsPbpI z_oqIHSag7<(YN3w{QE}J(pohw{i!5RmHEFupL2(^MVWud@Rf${Bj*_K3HlAsQ97o8 zA7c(v&oUZ-j~bIx_RTmixqh-5lW8Io&<@Y^Qv89^JALs-g#V*|Y4K7Vr-^=Z;z68G z_fcyhu~4OTzU6YH^X-OPmClV=cz(pfgRT}TxbWdY3$;wdxoq|6|I9>F8vCjC9f;Eh zS~TVzXC~LrQ+QRV{jXn7V2&0z?*adJfTu>`Qv;d_MyKp>T|$0~{UOfYhCqkr*~&uv z4MxjNzhB||opszFB?dwtUxxFO0PDji$Af(f)@%Gu*zB>tKX#*%?bkobGyZOQ_oe7i zl)dITt;>e9zV;NE{upKYrvX!@vp(fHXkr;@gL0U19`9J^u>b6H40T-UT;GIeLu}ud zDAVf~ri}>(KMg$AkT%AEHRe=Ux3>^hPkg`X$27xlqHlPO_yWinCuvJ!R$JxuPa9YV z^OwOnv(BMq{u1O_p<(3%KnD@nL*6HE;h2dpOZ58Z6s`WWgs|vCE@RlJcnQW^cuBBn zD)`9mRV-uSLuuW7DE_4-9fN4{r}zhiDZb;FH_jpbz+wA8L0@pvK`=UP$LZ*Rx`LvE z&VHl=^0;nV7_Tu#2+wd7O&qJlSisNi#8{vHp3wjQKW!gde)_gw>95-U_kqw<$UfP( z&Ud)9^jLh3O=AD5xtRFy;DWO@$y-2WwoQ<6HlAs6&cGjbo{ks5E;Kx4zW$2&f^4~3 z)<1b0VW8YH$Qk%t{CPX3h#!k>Vt%$QZ7FyX&O_VSE_8k; zc_n_YL>^>-ZJE%{ypJH~Zi0PPaIyK(poxoDOP__Z>EP)*gIlf$9Gar#b#7)X_s=@; z=D;G-J=1Geufskpc9dz|FmJ4*j;?5O>ITBCX<53zW)jX5)_``}ai8*_(g{Epi>jIs zOPI0b_4h}xEYDdfACbPyNMFo!tKRF3-ncqAU$vol*3|JVW8#mp^~T=5utz=g6?I#U zI`&?PS-O=Hhn(qYpgco%FaeQbjr6X{DZ z{M7bt@~HNFYYZIaOcD2Bz5!n@>>ju4PpC2T-Komi(nuGPC$P7b@i|%-Scvm2j4fml z&bMs2W?O_G{9?k>*rRX6{->XN^?V260v_be4Htw8oE5R-nl`u~Jr?Y6KkV!BeUEJ@ zIkNv<$OE1gL``^tlkln=JfshWfB7EPVVR}68YjwM=M&%sW%>!O|3I{Jf|3`Y5+h`U_@-A+4>{WfIGXS=AIP;Xe;fV-iPr?|hZQ>Mpn!o4X&2ZH{& zfPJ;}e;xbp>zcBUZJ6Uj`<;yWn}~%4+6Vst{r;wvZ=8T;rBpO`d?`fFJqHcZOY zVzUjx20ac$zV!cF;M=V5)%yoGP7E$E*V+`}|KOiOHzpq=?}Co`5_n6i*Zz7hcE8sL z3va<0GS6NNolEhS#IGk4Z^I@toHp1~=O7Jpa9RTa?K4n1LO<@gw97E>uxdyeBxull;YF5&_E z)xSjsGTwFkGg(hO)1b}mOfmdqaTa>1r7MA z^RujTpS<_NSl}7XCjBJ$NnY&&kqzK4fnceJZ;UN4X+vmJ_Wg6j$B4E%jssxJ`~$Xq z&};A)$${^;ebj&sJM4D(Vz*AMyXaoM(@ZldlE;v$Ryba*_z8mmx#_tL5b&ez7mit$tr|!G*P$k}N_}JElGFn%d z`sc|-iUwajtf`MhzP+k!Kjs-A;#SH14gJ$0y`BLU_jU&O$!*{t|LGFr?gh)T-4A4mD*&p{b$q)C2 zA@S9G<$H#&=3?AAcNQHp{cmm9tAnroNcrB8rv0c*zF4*nw$qlMw<4`O?&lu950O1p z*#PM~J5bg=`Kt2Aob#ryD_`WQvdwB<#S7_IiDbZptOxM z5xn*IKFXzmM>v;{YW5JeJ4BnA|NEuj4Zg%*x3Mf?FNMz`-#S=|_miAa|D~7{?As=d zVLoWQ&AA!pR*gdA=dmsNmwC1GE@M8{c^i0YYjLCIr!Nfb2!&E+0{qa1q8)wrgR(Q>}uAvz~G)C%0A zqn8%7r04teF9w%5zHWs)Y&Gi?tx3nbhi4zDxz6?`@6X(Id-m4Kf{juq*yIb`bxrnG z$Wri%L$l2~n44dL<`Jti19JQ?^ZXj-y&Z6Y2lM;~&tA^_z`>>aS;l{GqZXX+4iLxA zeT22`UDoLZ9^~R*gyxFd&(^!)ech_y;k~pf?~=KIAE?@-5ia1T!d`5J4c)N~{htW@ zhoIkWgvtF5aj72Bi1&3yPelLPaF?_CRDAgM;vYBU1PpRKxmPx6<0QED?JWKrrDfL4v<0`SmKpPQ78@$()Q*r9sljjWZ#g{a& zywLhvveplR*5QNg5gEt*(O@U!)f~u?@^Zu@^j;3z3GNHoJ0l-LM!tgcygICX=(4CY zo3IYug?mJ=X!b)GU#|axfm(XaZ*h*^ptTyYZ{93(tUaR+-XG7(PwFsYA!~Qf2VEFG2P0PLk|Dcy;2VFDtE*Rl32CPL6NL+OFm$>wSqIA+Qc^=s7nBzZBRHX1#;5Y2)j%-XFgt>&=dt zI8Pd)wKbG$eXB=ooDgWLV2md0XK|l-L>lY|+n0zvxT#R|J2jsN&j%hWoh`OJ1CJrZ z1O8|p;tQKdf8g6k_35)OQ_{~e(mzN^S8u}UpS7f<_cO{jq0L6LS*Y49d7f>mz1u%u z;J#P!73firHDbTUIKY10A94qL>a7th4;bqK!)w4;Xn}DX{M7>r-mbbD7km`})7(o3 z<}KZidEG?V!6w+*WsKhxS%h|Nl##WJ$s*}7+x!t~4{p*g#%&{TkoAx8-h{c`=$~(= zuZr08{qu7);%}BmJa&;qgemEOuV5c+GSbr6rjfQB?PanY`f4{}P+q8ZRcv>*X|^$U zK3JaWLqCo|i38&{LbV0HBmHE)QU7X|vDPo$q1pWN!BYgr7FnC@S8$vc>jUq3wb*9S z4_l3Mv5obyCKekwI!qk#zSa@uiEZAJ&%GVX@Eq7^V_pi3diS%`p$<|4dioxxOxU3EL05vR_IaE5H*y zfp~IU)R=8KpXIG%=AWM{YsjbnS=PU0yk27aZU@_c{{Y%w#=DrM&{28buEwyah45z9 zG@cu-!CB8d)faONRGWWpVi{|jW$>w=J3q~ASJr9JwUKkw8u>kJnRjUsj=l3K%|0lK z`|a@6$wM=w>e4k2mgrmy?L@K=iwR`I>$SM9+a=>E+2uOK^yO8nVSgu zawx3&;?w_e8+kwQ-{g$moXI|M+{FghC3CcrFU z8I1iVwl8CfGD62FS3Q!x*{y3?25@nI>xmcmU7Z!!Zmq-pKgi__*q!_h!?g1V1HT>k zJ&AozwkK`1wy{644RpHaF{XYt(UPZ;jwJZ{D_BT0|z5%YF)Jo z{WHI~{fC_@FMBK6^kA$+_W1NSItat7X>SIq>>}p^Rh?i5#WL6#owmecXEr^2ueckZ!aopjFf zJRF}?S#H-17|zP_45aJdRJm{$S3jOx4K6lxqCQrg zD0^yb6V4Ew(1-RR=HX_%Ss&5*mEEj&(U#z@NvrT`eWr0Nu&>#P@!T#n13CfCT;c2b zkZdF6<@8d%pF0 z^Dpq$>j>x`Y4X-<;v?ZDz$4DNLptKZ{OmKC+r)==6IUZI&B!}yb8TPThk!>iO-LZ8TEyxe2GhE7;q8_{=>^9wjI`k zFR@E#in#GySmhgU)FwWB%c#db@y*`t$v0{f{}dj_IYQkoRZqP`i#KC2&byYgZe*vN z;TY|zH*Deanax-XD03Uj7%<`rhI+3SFmjPE{qNa^db1X>V)BqL{ZGl4cWYq}v;&3* zWpdbt;FuiW57pbXiJf0s@{Il&Z`USvn0@a;-#M=yvE;tPeZ819tYI)l={fF*&ifV@!)hp^@;@ z^InVj+UX*Uhj+rBi+xGgPAwL9+Pqkw_PA5~hWrk{*K;v#;ya)GQ z`ykjc9em3Lc9PI&x8T$U8MZ0VWYdC8j|503WsY>Mdc>suGiw~BAn{H<5@|G3ahryNAO6Mt!sos0FB zBBQf*JNKl|Lj1V&gW@E({ZNM>MHm=g5R{G zF@G7puV-KIm*qC$Uz?HFi0|9g_s_TEJA9{Er-S9%jr3N0j~VGKe^|y-f5!%xwpFEC zrTD#qPsUbiF<+uacns~j;P<(PO`y1OuXYjdQ=>om&Zeq_vr=pKWg^$PK*dDDOK?0j~!Ar~19%tOLIK`fC_7u~UFvaK1Iy z?(4c-+CchL+_zkYbghnYQOV4MwiJK8m4UKDil zjDqh$;eN@JjlRV7vbJEOxAD9RzOg$k=TYy#*7t#&0TwWJjs6<+S;VoCcRe;Dy}lCX zmvQ_Bj*UbHA6p~?a}67M$<3Ox;}^}|(U*4L^#g6L=(rSp=rb{Zvmpdo6q`gOXwUaR zcgjUtM$leJJ4Ii_b&J3O5BCy(0iRym zqbR>l?j^=geBhU^%YRAVT%|9rniP!T9d!HJBbb{?+Wh6t7Vi`Bosk0g0@H4X`;v?q zM)?dMCZQql68iMM4}2TL_hJwA^dZPI&?@fhFjfs?ubdh~`m~?Cj}SYFSR?Pb?br4ZNvq3Q7_K^Dh|D&1uvX#w7Q31)N2cB$`f&(&;EZ+DmCOe| z5&Fk@?N2n~CqI^Z2T$;>%NXzg^>*13o1;wA`W3a~Jfl_XhqtAVlfK=JT80~ZqAE|D zj#%g!H}BAHE^1z~_7KjVgQaa;+p#j>>aNu{xo^@pR;|`5Vy|d9u&dKnW~1%H*Y&RO zC1W4eO1Tp(ZY2-eX_l)!@T%D6P*&#LD7yk@#H3GyCYJo54BvqhWY?0P`ML_O_M=lZ z8}J@4eQVJcX(#xRca$6Vj*x#D^Ycr%XV-V9`Zj^Tz&~)}!ype^rG3t_P{H`Wi*Hk@ zz7y-r2LBWf{=%0)Ka8=2U((|a;$ysjjd9L@m+(7E^-jtcjKu)@K&gH5ef>$J&`Gk) zc)^Ut3BN8|jl|wONxhf-f_zwlcm?@&gaz7Ii9h_MC@_;u3^6s=*Y!bluxL3 zArJNNCeK3QgIo{tc8PrrtMQZcpEhZhZ3r)%cOlzhpP(aQp7t(vchl_1wSH7C2Ca}L zLDPQpbp-guaq4i+rlrHU?^HOByfdY21!QpCQg)i9tgHnK+-WHLuBGe(OWDCGaIZnx zwU)Bs?y{F0Nhy0R%HC-y`&&y{wIA&6TNTPqv6Ow@QdZW21>fl?JJ?cotEH^01xwi( zpr0d_vU@CL$EV=?Gn9SHQuZ55*?}o#XQJ$??y}Ldy33B}U|I4Kul=!E_{xukkNo~+ z#6CQN7z@YC(j>mSv-A^uhkPmp4UmuEy!trKoP|cLzBTR$*0C#k!kxEv6X+Ljj5Jpi z9gFU9U)MaVXh-zRqUPv}MJw*Uvgr0`1Nj8Uf$ISD4nGyG%Gb4ZvLDDO-ijwMx_JfG zRCK7XYlN(S)(C>GFVoOwkX6#vceYoM*zjLG=TUw^7DAOXMg% zr!NS1bn)I*PH?!F^#?Qz*S~>nk!vy6<YeD6c|Eb|O7E8 z%<>UcUQ1K+)45R1`-!$Xj2HH-8vEo~a^ep68(Q9DA7M`fUtnK+v#ckH(}MdLzW5u$ z>v%Wgk&m=OU%d4w+d=;!lS=O7cR7cWx^KvD;NS?{v&a+Jx_lz~GK+M^{l6Nsq4QXW zG%9%O$E5$`mmL2B_0;&Pa~zECP#4Sg9N+t313uY^eMhd>{=2Ej=Er8nXe^na^{*>m zy9s%`53|_46!Qt$UzZLzkp1EM3N2U+f2hZ4>!weI=4>cvt{{ElcP?cF>N-HTog5dD zYtg%JQDgJ^qkc4|XtnD-jw5xax`MTv)L3e(4R}%RtKEAQT+Bt(+75o-aXR_kagpX6 zT%h#{6->Ze_j0eri!*fkK6(Tfk+FB*#O!VpFfi>LEJ#&nNF-};z0cbc5=K$Lw z{c~sA0ppVj+thFBKzE+TZ9;#A;!_BI44mmJEcyjt-heoB zSNj2kFxd|GQr+>j^@{F!zUMxa9m290tM~DTpJdx!`-*{}`)S~AC)-{z=wWeh7NbnZeQ0$EzAQx8;K9#%3TPI({!WUENApy z8^$NZavV3rg7W~**lsxYeh+YjCooNo-@R_``+f00KF@mSLnCRSYHym>HAk~|!3T!# z(04td<$)%g;SJi@@Rq*@W)$qI1$=EG_Vugazl3@VP@SJ-+;-afy!J$yZiB1q_5ByZC$V94gl8R+A?iY^{A!$;ad#7`q}_& zG#b;eceKqY$SA#S6#R|*=!|9JeSzt~-Bqf2*+<~V(M>cqb@F(t*UG^kD8B2cBQP5mp{E6m_9c*3}9O?B; z#a&(c-A+a9mRL+9->3){IOR9X)s1Y9a8G|V^0`lAUzQ`ygaLocZ-OH$JjR+>j=jI9 z!wxv*&I-hdr{8KJ+C>}y_@lRLXZsIM@WA$=h4S4G&A?Sv_^kFgfgp4i2I4~`L#DUV*C*gNbTiHCTPz1sOEamj*Z_} zjcUeTf=_+O$D+qBqYt>)B*0^7-;}uGIGfyKtaZQG7WA)=QYXOJ+*(%x+~GqHew37`bTCvgh0Uagao4}iSPj-M86w7zKF9v-=Pw;Ie zx$ow+|EA52JM)AE(EKEAK`D1(7vp~5Y=a(dbyg+U>z~ye{HdQGl_AHR#vIVb44gO3 zW;*=ZPpUEgO@Q_l&W+X9g7M?KB~FwtQ00XWZAU%EuW17aev&*jg{B~XBD?%3VBk0D z(5H|&WzuJ-`{N|8e^>UKKUT6V`2u*r8uj<bqX3UKS;S52`n-9Wh33|ScboDLbo@{*;dh;+{O+12bZ(tr^abnRSzpy0{R#LJ zx|{qc``Lqx_b>5WuqMUtwCrY_*-XH`Yp8y_YV|*}-8El0+}G8g<96NqKfZXa&?WoI zF*juYMz8%rKWu8P)S=P;vCfE1bGBpLw_-ou2;T|Z@xU4WzTjMub@|=;=y|gKWnSX7 z4*~_zDL@BLbv*_DVfZ{nLZpxQS~cJL&hRAY#nv~N<&xcK!?+6@+e?Y>y^*$%!L_808*E*{%Tjq-&tYclF-$a}6WLKGH{{v*;BNuDj z&*eKKDqbw*p4=S^YkByN7_rw2-6&l%^eEwQOwZ6hV(Ciz&8mIG(v2Uqv0fL}9NJRx zBxxs2v?TSA@rbcc9o5`>E&C(puWR6!tOs8F66uVz;cUgfM*NFtHh5YQq1==U*FMZLg9~impbXBYV7C?7IPiO_ZOGRM`cw4<^sBBM zrlG&kqUDpc!FlZsanNL?uWP;Nwu-K8gM3|2zvV|!+$VGKuQ3KT%$H9pL;D zV~Bn3_ooq-F&?~!l8$*>>5IQMkLf*lO(yyR*}#67dK&s6^9Vdos~go& zg_s+UGkRapS#{q%|ChD*fp4<9{>PuBr45K!x2kobhJRaw%#{sU+VoEf=+XxG6fA;H zG+3ChvG2D`+)|UYDFg*&4g?(tXbM!RqQgxXPODXs6{nx!rhdU876nN`XLX`Y3VnX> z_qk8fGzPcN_xsE1)#k}_?>+b2bI(2Z+;h)8*ByO-HQ~e0tcyB0`iMS2*pvJHw$-kk z?HrG68~pOTINP$r9ov11`C;D>zbo*fRki)ui}`-d87+_W=?-vxsuz^NCa?Pa?v`Q? zbm0=L6{0P-GcW1Ee83mFbbM`g_b$-nOLx!tmog9dw3^zII%iHwZ2VviRz zw!aIz24K5mM=xL*j0Na?y-yRSy2s~ko-G9*=ImoSaRl78;3s44cW2)kH$(BC%d!l6 zy1k$Jan3Msm0R5}v<~}UTw8wW@CJrX^af^M>n#GGx}(2pA>8`6y9!o}(0&oH?VwJM zelX3Yyk+dRGJHoC?0nTxxN`^gp}Ih~#cV&?j(v3{D+7gYi2)kfw{tgq$7TX&oFT}O zHl3*3xx4dP;5PaI%WTE71$|AuaFw(Z=inp;?tquEhQG(J+I!wX;sw1g2ASe1UV^D0 zv~!1|)d#|ptebeOTClSnHX(2DnFOsd$KGRg_ndb>VMOZU`qn)a?&za$@Ev46VSf=V zgsc*KRcIsdvew@=J-5U;vApw}nl`7hs;aIsd+)r;8I$K!R!s8eR}uee;_pCtwgJB< z75+eTmO7nUsWne)_~VJ(=rB#r*EIb0{oFgoX75G*9DgCsTn4p*6-A)&_&<0bd+t?=}y~9*OCp|;Op+r!+tFGa!0?K3ts_^_Z`CyM7b)m zt05o$=>6$=P|p|{H6^Nc=D{on}C~1+br(a z>F&FeGIlW1XbK{I8^rB30&c z4KcM(zRJ2ZF4t1!Hta$#OH+(BkQpOCf5k^<0vH*}e&h$HB}+sHGR{u!X+t$FKz`hXzjuCsJ+$8qlzp3^c3B{AB4hY34_t?{ zmp^pJ&ooCkzF_wkoR&73-eeyZ+P2M~d3D>6nHw`pF~2OI*}CK~{b3mk*x&pC&u6rG z9|1pOkG1pdv@%sy5#O+%0uG z?l@Y|KDxeU1Nvt2SmMAvN}gj@@1~USo$621!@I>vN2WJnNwc-sCV{3DxJ{=~p(8*mcY3>x)fpKF^v6nsR{;)xlo zv#Av4RB^5u@{RLbOzwpUV^1s;EHP+ymEcw+c`sZ2Lzxd;4Xq!L?x$~kB$I0ZiK}W4 zt^6g++}fJa=8iofdIt0O(AUg^^gE5Zj}Rt#4(U;QbH;`AYgTF7TbV}t_-ec88%4JV?4ljTQlW5w2L5*{8;n#I()%LoVd43&Qw$P)O))LLwpLG zS1uCTslEi=e)2BikY<1dKO2*$F>xu@3`}Q>h$^Fg1&$?JNKh7 zcEf>;iLl+)yQ+QHlp?-o7S7~H$lqcg4rG>k{nwb?r)LfH03gc56d&mAC7NbdGI5tS z$1+O`Sl)O$V99)zOiYvW|n(so=o6ZXN(>AC(;)sy~L@O*XElMTmXx!kJ+%}0FiZs=nVA+90z`u&ff9@J%T z*36Lc=14R8qY~{M#kc~niDgan(v}Y1k+o~U_WcWp6Y3p|b`EfK09K>+raHGb)au*F zb9?Wnp4&q&Yi;;z#~=3y4&bi?ZQ4rS!JLLQ&&mgICydAf!biVthmC8{_g)ZO7hhVU z6$HLhncGyMU3SIYS`hn6dw^5h(iZK)5-Zkjwh4%xR`EAq%S&d)H{{wEvDEH^EEPF9 z=2El?vJ!oEBjT-T-USlNXb^3)&Y;A;HepUO(XI%%9@tsXPcp}j1RjIIA6|T8PfV}H zH|)&%e0+Zc-=345^zqeaCE%uk33}~7-wZ;&F0Nbf&HJXz!2esM?d5t#q(ANze{*GD zFLPBA?qmK<9h2jAB=R}6v~2`FRs)YNG?O2RiK=9KpM3>=I~e_=O~qU*^D3S>zql-S zoHEYFH@Tw=Wjsq?hF1O`%#j{7zniK{urEyAooUh0yA1oy#vFU-*Mrpgi47$HDf0r} z<-v{to!K;$V^+zHUr$iyA-1hB@nvr0Q+<2ch1)G3rjHGpx>dta};hnrV+kj z#~nv}6b*lq?T$UXOwvR*&pd$lXY8RKiCy9D$rM@;&;By2ZY$pbZ?z@_p2}XTk8!lA z`AFpN>1)>$w%>aR{S$JrpQ7%2ES38n4@n;EkwjjAY{I&U{nuH!OK_WGgN(8rc^iE- zD`3N%Cwmc9Phx(d{=9to8;EZS+9Cap{49Cr#Jf!6-5-p1WAJXU@$Q)ME)Va{#d87V zl3Kfm?!6H0!Fnk}@D;qGKNXsZ?x6Q>4KLEnM&Bv>N$B1m8D-|NjTmRIEt!Tj%J*dr zI<#@DoX4V$orC9GwLXGxqu_im&gJNPC>y+YTr`*OxF2s1-Mbonx!fJyAmfof!$zHF zv*$r?xDV@F^AAwA*#|gMz;7#gs`PzqYS_wOsGoj! zF8k17#h#9eIdG=v%ZHyYwDsJC-(iF2{*LEuct)9$pWt~1*6S<%1KTOfy||CZ+<@ws*@#j8WofyCLj|R@~$-S7-2Nw~)B%J>RoM-bL+fq z7nyIx_qE{$Z;agh3+=LBEYR*n+wwO54QInI)Pk6+hmOJQ;9O#^;yppQo3emsG0_jM z{BI&1YqG}a+K9&U;fpy9bJ?H?xlI;r5aY=pEf4Q=c}Bdc67L4FkM-yBFXWkRNe^MY zIhen(2JYs#7oQaU^F`frv{kZy9(}Q#c#{79pHk1#{qWbeM9Z}U@bfvBX`ta+tb-2t zGq^T01rb|H3rxHgI!c@HC3yz88v6jb=b(ML3U|{+&<=j#-@O4i3F zif~T;k_m`0`uPCXU5L9SP<~Y>%HaHu74TLj^+k|yS-#G4(Od)O0K($gUiieC!8^F` zPQj#Y5qm06pwB(3J^$+ww7CGfazXxuCf?1vA9R~VIxF1u$#}OLc_xu}?4jIq@Z5!W z?JO6#Y$Elt+~N++T>KL35qp(v@f@E;*^To!lrO&-?mx~mU0X%B#hAwWr-yX4G*(hy z*0+^6_HM*)oOjR{;v4>&+8C^vb^!KT(f~d&?R-yOK%NNl-)Q77rr!+*gm^`JiFSdMFG2V5M~)G{DIBbF2?vq0BEcQ`flfPnai8m%fDi-!?Uh^3>oGA+{L4E)&y8!=kVbNkFz5xF$7-fiPB@ojDqkll zyC&?8#a>~W$U)S_xyhC7EaW@NVf=O~J*?;Fiq{OEeYs0<&3W$VeL^?j%=m+1cOwjz z>A_hI$jYmV!zPYR#*Q%Yy|(E)w_n z>PBrCg7QO*vcrtBRp?)~?>tZQX`z|2RmMz4dI5bc>$g0PK0LIE`0+i)W-<8#?RBE8 z$=&6zQ`rC0%WAzf)vwl7{_It@F;IDlw{G-?AthGE6kd9?@eG4OCV`t?n3Gv)l5 zr_t}yryO@{a7K;a-B>eVEDv=@ZyH5dg;xAI<9yRw+c;5sQ~0s6=GD50VF>OpIwn%p*5e`VQ)eDY}tEUf;j~9QyJzBM+art1LGXNT{(`{wf*jzcE`TB zzyn~u&Wq;a?l9WSY8&=-TxoAZpYDNQK+glRjyBGa$a-IVLPBe)b6`Dt>MfQBZ=~MY zFwPzQ;U=bmr&KIN>@~BD*t}z>BrU&8yBurP5aeB(eHi1)|Fi=|_(_U~_|rzTX{_2$ z-IbHPm&$o+g>9Ft_n^a1;_pt|t~~O%^5roVU|zr(!L^+p0BzXLc=r(}c@{P!xnJ4d zf^pvbS+2h|zY6l@z7nemb~5ZW2Vo-#!$8z*wtAn--YflFj94q6ZNr^7UldwT zz_*Uy&;dE_yhi-qAlfH>4a(2$FZTs(CvxcrC-FjY=Oq1%vG21|ctYl+wQB)$EMXb% zV3WHaF%v$&ll!N|*eJfER_1=cpRKBL_-XR3DJkMkzFMRZH(kjjqI`x@>Nm-ffoOTPfs=42}B+`&8PGw!_En*14n=>kBsT%3*op$9-XY=mFmK2tS-%`6wfE0A!`%XSoaOSG^5Q zDtKg9AJO zs&p}oSLnfjv5`CneDd7UdxXy^50nmYR$nd5rR(w`7cN3uUa;3Z@CMFu2;VEZtGj&N z(OClT-@t<3mB6}K!J^;Qiz|pH?h5Bx*@;Jfl4RVx7Pi}vZP0Pl90T3-Y_=%Y0pBuh zWKtb6M`6BcCm+yOn<%fId6zluuk=l0ds%*z(cZtXkMc7$Gxs*QcZv1pjoyuyj||v4 zMjNmub6kVZ&fyxG{KfeKJT}|iv+5NO8f3@O{`bxzgbAu?xPr`rJWtsck{|ow8fqp;gqyGyc;-Y5zUZRXE?p`e`xkU3(Z0rlV*Dg*B`(V?>KB<898jgYWmT-oi4&)?OyI_O+O|M<(&( zOywsO9|s>bu}qpVfY{+zE?`>ld9)GZN6FUTj#A_2BS`}vd%?%hmF7H+to(=^Q8IOd zi$1i_7p#+E&*3~|5B+sN=OUyJSz&8>4r4sq|JW4?p9PHbaPj*%*L>V=@h->xFxe9x z3s{PB0|OKBIlZXEioa_71&u%2*w9SG6==C|+niCj$1`Kn1kmy6E0$h8q5L7O`ii9! zCSX6W3hPu@SP{3i<)VD#YXklVjOYD$e$9C9z;m1N+=b_-XN<}!!uNKJks!Xe0*?8L zke1DMtZQj{j^9nTrsvByX2eTU|x& zwmG&S*ClQF8E6CVlg^kh0ppJ6AlZh=brl!n&#|>ZAJ7k}=ly)%ubw;jyhlBE@p(7S zy08rrUk=Y!JZ}Xo?!62l9|dl{mNtPP`q%W-=%(l0J#Pp;6i+6Zg8XPR?<32vcl? z&$q=LohtIsUzX=&9?C)MZL4^2<--a*LVazvS#OphZafN-Enmv{w zjA7W_2!DZ|-$Yp_{Q-?Rek5g7L)Oc%d*F;G&sg^SM&@azV-3&|A2*__hSn`mW2aN( zpC9@^_(pW-246ny1+W!Na7r1U?Qizb`iCPeoTG5=@!2cxbH^g5SU<*Uie796Jw~@O zjcc@qhoOr^AAN$qNjK<=uLb?2!4w(mLIW>pV9JlvV96na253LqUh}SH2;u=)sbiJC zkN68;!YB4iz}NqN-{rcNOFvlYck0wwOVz*p2ef^~6shwkqPtZ$z&|hHn;(l5G7WMo z$a3*Apd0TT3c7n%N`D%3lYKkN8^Qn`z^lFOXm_yAHWTMz_2-C}_~2qM1$R#KuCT?V z8(?dy%sM&$2Hxm($Bqka0Am$li2e}N#t4iOtgUb+nR+{9kN8pc!C3DD?>@)#1`U_L z44OW2y|fSfgZ0Tq@=eX2ydmTp@(B5cJfL{x*RM10*YnD+TLhl)O0O%0S2m)KH){^( z4PoDKe}wp+{3GgUMIW@H4_cG@0PADb2Y1PsRdm$V+NO_=QvnlecddhZq|5T-kANaS zrr;p(3gmNl+|C{Q<}nWvRJ-D29?V2;pi@L@B zf$zREqIjQ*1L_}Wz~50{eFM!x?5mx{o@!^LH}{&mP*bsrt#c2IY2oTTOaydv|(F#~gQL3_s0Gp!$) z<%&G74GQa;w){Oc|3eO8y}te#ZzS8}J^s)4;e$Mn_1*JumJ4axn4uz@$DO6zr%C0$ z%W@kwiEJ~We^&u7&g=Xn>A~0Y?Ax5OTe!E;7i)@shLr7{4$VwHma|P5&%^=qq6d2& z1IZV(i*T=uwj-BT`Ew*Q-R_Q^Fa4hsk7_sclpK@;@9BGz%enQ^eJrPROP%*XVNExy z9k2LFS~Jb!*&w+i8{Q9IqN;hOuC|P>9bY0ewcO_`J7|stMXhs>W+>S{)f$( zv0X9daF_0T+t)39c>Bhg`?BE&uNA5ND;+*^!v#kpw$_dn6{#+OYWpDl`?dD>SMmeDg=TwY?aVvn6f~n%OfSy^h-$*-4=)qC$*!#D$9l-fC#@OTr z+#f3Rg`O%hPw$;Vd1vV1c_I&y2Dy6`%6x$`c^h7k*b_XP51nx)^&ftu&#_j?K|d4( zY}P9D#W%={z(MY(#kXl{8oNeZUd7A$SDdRHJOzy$qto$d0Gg_RE#_0U-=ux0c2eR>#sL#9N>b>6P@z-E3GI5_FUVmgM z?s%wyEDmLqx_d$*ds5*mPzo0P@nAD&|K>DiVxQUq`@Kc8Ug|AB8LU-4T@_-Zu{Qa%a|%k#A<4@tRo&Ij}Z>H5iKnE@Z+ z{&J@*h%?Eh>V6a8=fb_+xs09F5dN#H;dw%w z_sVn)_d4jC@b|RXV!(6YJ)XOHCh`#IRS$ZBj?53bmAQMCiXMdg8$mnBp6b&Hnq{i_ zf@>6m=KW$CfzPbQ+HXw22H$qz>-S7OfiawFLqNQNVZvJ&?{R!=k%x^tI)INIYdD2Z z@B$AIh|IxS7J9mY)A9sPCWRC5JeTs2_9MnzqU@$ExXg1_I*wL!lyem6==ek$I<8Nl zBkr_0v#1{(MIMljOGF({O;B@-k z^t7zs$9%zlw7j+$ulSMm$BGdczXPq>6k|>5vP^Fkx`L+bmTCp~tMZ-c&ceODnI~Un zEKzlS%^uQz=nduND_M8uUD54e+aXW-jxk2}nTriyypdd!P|o^v++TAHdaTI7)3+OE zRwosM_xa8fGDT2sHTpcvcl=E`=w55!KLtwL|GcPW*9jwA{Z;5q)sjyr-1io1{$EM=><9@@vz7owk&o;m`$Cj%h-yz)7 zhv%Uh&bS@*+2A|XQgtQc8M|Y@kog+r*Fm0Xz{kY#;EvVHJb?FM$~nGQcH;UM**<%R zHm3fO$0>u%Vk?5qgfh)cLm$*~|3u{vtdMt@!x;xM2~Hi~Q%@*#%3NZ>cLv7kHu!z? zmQGN2GLp$+g63@&1 z``VtQyaXQ_cR*I2LcH%w2?OaVJ~;zEa!0=^06iLEgM2C3-Y@VTFyOsJnReM0Z5@3A z>R)&)ch&9M!k$y$L&(3Lw=gfp8iilk&qzBU@-)^cYoj$6Xd%2iL_NQL*%reWC@6iV z7C1Z?U_Uqp_qHCSKR9guyn9jn zSir{@xb>EcNngmS_9WcIhT(0l&-6A&UgEtYD8Ihb1iz6Zh>xAz$I^EjzFTLRXLiTl zdWx_x*4m?GqHFBOcYc3*fBrgX>Lnh$n-aD}5AlK=r9WajaJslONW11X8E?c3ZE~tM znj+rjhDpQ;_zXOOv=rD}dt;6>EthtNHTwA!yL*QmAzpG83w0wsn9>ILgKe1Qj=lLQ z^RX_McG)OCBc8=zk-fKow}Z4BT?=`88|3YD$lI~++>J5T(RdU5OiSUbjx(H? zXYKXZz}J^Dk1`E@U+!4lGQz|=U1X}ftDntx-St{N#{GQkSLEPcCbXz9M%BI{hfyx--tu5WmY%m6#1Jgb9uhc ztv{-CWY80Rx8K|B9a(1!nMvn>Ovw2fVVj;_hut~pvbA-^Ui1cSr+-$dFRbJN*Heavrfck1f1S+^rvY|oC|X~ zk)0p$87{PfFTR8LZ=*!cYEOMz&B0IM9d*2^DeK~N?*PNZ_v^79OwDh^$6iOB5oaUJ z-G&ViF@c?2E7!Kjd0!XSKHg5Of0YhJ|HSb8$YbWe1v_CQ+NYWO>^py(f~tUEK{3{|{_ORL-k|EadZVgFwFmxqnVoYX6Tm;*H*6eywY{z?}8Jfcppbnx^l@>FSO?w25`wYtGpF)q{70 z;j&D4M%Mng!+`NGI97pUqhS*basF5Ls7ZXlYOe25|77x>BMAS<5*2Jg5!jS*ZMCEr<48gN|6cPoR%YM$aa|031AL5*F~F{NtX= zA%Nf~$=*C+Anj>oVXfAIwQo(U33^!XzT$9jU~y2ZrLQscHw*O`-1jPa3g{y-b~qr7)37Dr-7?fSO=v{h(?P@ri5am$>~7T2JH@w0v(7xM zT{-45p%KoKiTvNe`cQs3*K_t7b35$svr$dlrhe!o=9w`LmeKd;{C4Mmi#m(GgZDq?xF7+?IDf|fYn zI7yF1jxg5yZO~0?7z5@^1^1Q{`nkj@J0Y}JI-izLzCwSIhtOXgGA_iX1=@>FXK~iL zw5g&m;XQ33gHZ?f(B)16(C7flz39;duh;O!kJ!%@KbJ|oAOCvv(X-&^!|10o=%>pO z!=;%0bh_E^Xs0X{`HDV=9loq9-y!tw!X9)#{%M#87&tqKdvQxVsEGVeK0^6*4T#61 zWH5Z9Y}MnjAB%Zi-=1G~o({jytqr)J+}ez>3jHmAl=dC;=PEnmN_m?nMXL8@mcq7_ zfp`8ACwyFSX3$@ZcpiHfZUz6J3;6jZ&K!<~^Z6dX^GcksD_CLkz;DpSg69Fg))7Y= z9zh#lLF^jpeBgcV!GiZe-_8}pnLG{p<>8SZ@s(^EseJ?cU55cDB(f7WHkm^q0}R=* z=;YC?D(E^<+*Na9eB85-#Qqms(BuIy+5ly217FeUVz)_ulLuVc1K(wy`1vRo{M%3J z*nfhZ+8Fzo%S$)%xu0w@iwroczD9i62RKJjU~khT##UV3!Dd+td7e`pY8w)S-;lZo zDQ61zr`p4tGMr7Ug&$iE>!uAmrLL)}t_9^D z0HsYky*@K2Y5p(SNCfan=ov0ciR}IPH-e8}01q-25#^1X>x&+t-Q76TY;|nJoje%t zA{QF9&(Xdx&rSlyP|6XefzLkr3U40+T=9=MKF5Q~g?F}`6yEXaFCwnYaT%Y5P7C8H z=w;xoy2~T?R@Y?p)@(FESI8>}ROS`;|Aij8yHsq(z{Rb9_zH2u{aJd$ z4e#XA4?wL+_4Y!i@O3VIP>4HyP{8+Alv&L(fZu^He&pP}k`E4?7sDQcv46l=Er_MK z+F#P`}B6bA)rWIQ?+03jQu?erW&=QQt5RDnVbX@rQjDHU2&v z$oGX-;kmH7ufeevulPy$+)_44}@+^{uQ$f;cyd z^-bik;fEb+x-a-4);fEcY^3Y*a@vkA|RSf!x3XXy#xa+8*MWievN+cIDEE$g~w zrk+#Opij^B=|6k5L09L7u>0n%E9)KtTjgNHY=v))JHAga4C~oiSr=oBkr&dBvH1sC zFa1K`!}XQ8b&U7WFJ2wi`^r~FD?l5)k>}M~j%@~iz7%N&{{r{rulBVM?NKq$^qx-4 zZFS{A{b}Ed%i)8Ab+iG4<&8BVbjPNyv~Xg33W+PuVq4a1BCW`uT=#0>zIAWmVr1bb zBZ!qpKe-HfB)-6)2V~!`(U*z!2j*VySJzb87kdCGctl%_b5Z1jcsb2~{9j(g;#o|4 zuRE4KkNKFVxC8nM@_CNq8+C|181*2Jl4XR`0o?mw!Jd>YYpV1zZh(!j(MHI5d!K)# zIj%$43o#bU*{**60{SUaQ>mtfAL3eB$M`dEQ+ks{>QeKKdF{y$B#u1qsYDFB%~N~c zLmVxhFNytJWDN7c<{QMA9tU~N__;}I_sv7o>-aB;>_Gh0prHe=Rr@|$_hX+!ceFt! z_`15}t}p0yep?ss`YHx&FKlE2zoCV?)aC^o8|iuBrSFn1m=Epl=qCX_JK%Gs+J&)* zy$9MpIbPi{(>|sfX)Dp5RltpY)I19>v7B(%JnneB>PFt383|;;4n9xH0G}X#$LDZ& zOr710yobGjZ)=SAZ8g$+a7N1tI^tZ{RXCG1Z-vx}@ryhQPaevu>MRjBJ@(LnLzETN z0hU%^%@(_L0rST70LVttJKS*1-<6FPc>zy&zq2^2Dqf$e>%gbP8R^2SXwxdC%W9J% z3$i?_%{Uh{B5RiM-U{DR@~GK<{L?UCm_egbcg%c{IB-lTyVB`>;KvjnLDOhdV#9c?&?yGqd~nbczt!+owhHv4|2qn$zK=Q;H?Hz6iX z^roly9&%zN<$TXRZFxfnW8@*GD-v9mtJWczpTry#kHJj6_kT8~Gp_akQ2H_5l{BU^ zgonR=OlOGx&pclp(;3rPPx>)!n!z+RrpI;Qk)K4oZuXg)%f>C>T*mfsod7;6UiyJ~ zKYflGblwBrfcUD6ZMk*7_+MP+(hz$)*`Fy_=m(MM=n(qu#~DPyE6HzZKmC^AvqOKU zJ-ByqUn6us|G+Ntt;KntzJqx7$=7jtf-U81VqN!xoq(h~o`9j6L*&W04kZP@lc;`Pm)-#{9YbG0p%7tZ{#^>g1NJ zT6;}at-pkEt@Vh*33<{Q4LX7t{~cJn-GIGv=+|F~h|ANqgXKY4DRjllMptong9KgX!%kzmakb--qy;pFZA9^80{%4JPZp zTmwX*JA+Q#4?o>>Gjtowv`n(YlyX_pI%{GRf2c%yGRg=9&|%oJ$ob8 zCupN9dvLP{6@$lW88d1{1@Ipv_z$LEE89VOrscuz-ZxlQ@!#< zgt)DV;}ZJ8kkzA`+FX{gFQTuf$(aP~6_5@%m*8>SpyZmmr{+wdv0feQ@Q9qQc7&Df z0Btbt_5Mc-l8k+I*wISjvJ3Z$EXb;oK8)QMU>-wu?*TpYd_7$w(D%EsSALrN^o|=g zZw60g%RSD`k4QXj#=UdLo*O7-9pH^Z*mm#(G=aYXeASUZQ0Dg9pV2(QeNAnuu4k1D z#CkT^AnioFtlqJt1Nuv18^X?EmAly32GOnGImSAu*I@j-37t#bn*$dG`U{}GD)u?P zl@1hDci50u*(TankTy=vMLdK3AM9BZH^j@GLbwh3KOP{yYdV|Sz-un@IlhCWCBFOg zVA#o&ZeB0|u^iqYT!ZhnvF~7~;oVx4`^x{N`O{j?L3YB6#-tz6@AgQ|E)Q)kSR=&e z4i|mBol7z{f+u^*5Z|fLc2!ZC8F3okSA7qD_A+b2;*6I*Ps=$M_CGmzFWP zd%-Rb#}dx`j-sE48e{rL5xhqmwqnoIn6ol6PyPw(+dFtq=jmrC+hGs69eP<9duHo^ zt2t11$pbj|_cr|Ta~e9ks~V1RU+32)9u4Cl0Y`g{{6krDvPoNa01yVUu*Y-2KZRPw@)+nlD<#>pX%o9`oO5lT-e&6 z-*3*BxB;zh#-q$^Xy$t8;Q1a@LVEF>CEG#1V_RV7)#!%?`Yw0Js?@wszW|B-V5*I$ z%Q%cx1qg@d{uSN(oxiJ1vVWej?|syXSWpd^&wppH>A4AV80$H#P1L+R2;W2TTWn?E zS@Cm8Dv$mBN8#UMo~O$12)m;z=CN*;!8q!WINO=F`g^Z&x_9E#MBKwXvid=6vfjI$p zh&4p;cf3cf>B8Co`UnXAff(NC`~ zF&}_zJI#J6v^_e@amI{$x*ypQEy>7_k4=s@cWlZ9gw-7|x2G9rTggvyM)e=22<{2E zOO27uALH(-a+V7|06)P8XlsM+0h=%OoUbwb1R)FG+Q~fNndSa@;7PFwt3CY2@sJnz zJ!9lO8)w+M>$$fD7z1b6!Wa5OZaGtebp~O?`3^AP7ouPY4`Xb8#8~Eqmi3bgOEU6; zo46;DVZ~owBP1N`3OR+7v@FEw$QZgpqrdXdz~vJjsk4NJ<98PJt)}9gWyVPBcz{JP!I4v|gd8(CAt%+i;J{I!Fw#{Jq|`wJ^S*^m2~JMh=V_l34kI-(P`2!7M| z55GUv@VpJMWPMVGu~4Q_pF3Vy+L3vp2KKv}JoE0-aFj6-bP_(wI)=}8yDZ1^GuA=E(!J@IBR`utNzn@E*E8fU>?IijgeHg|3NgWJp zJj8TFJKeFFP0VMcnT@nlM%pqXErxQm9iZGjzcC_XEHA%9Wg(ij+d=Sl3z zIJYxJ_Q2-IcF-08JO^bEmVK|5MZ3B?cAXlBkRxadaY3G41**R5HnBdGWg1~4ZRfX* zw8o^g^NchjHu7~c53pRJhCNj5L17FI1&qPShkGD*ik}4PE+GusMA=>i12I^H{@01z zFmRW(JpI^=G@%3Z%EUU~9(v~zrl~dajAMiwc~|b}G0MI(Se2czk7-7mBqj{Xjx^F_ z+z2jgFJuO2X|(CuWh__d#5@665IKWAW)p156&a79Z5nOQ^F~7lg>QUarDm;zuY91g zC^zcT22RlQh*4hniYv_H-Z77-Oe4@Ex}|;JOy$qgwj^WQn=G|M>c= z&`^!9^G>Vv+4->LG{?si;_+=cm-|K{tD=>oNC)bKsq)p)2K`<`OvVlkdUlQnc&fW< z{()GE;Q`S7sL$N+AU+9aJzr0;Y?#sH`c)HLsR|n zXXCm8Z6nat{`r2O!d1|Cv-wT>1*|SJzX_Pi_ci*j*&YqD?k?;Pf;Qk|@{QP3+|hrP zOB&yI;9Xyu8EMismMO*%BgnhPDI^ zn1cy-Rq{*00!HsEzxgZTprV-{8rWIa@ zSfStf3-RXMK|19S7SdNR9qpQxRA(OROw%qC^BOqB+r_-Vq3c*uTD)CM0}fpx$5Prg zlyxBOi8SSyMwqJq`}Z-^2vhnW?W#&?SDtED*HdZWT8%mjC9VG$VH)ea^ep9AXL9@F zZ5*j!exlmnA12cP^Al;mLAT^`fcZ(f_U9Wg)A2vk2s0i3GmS74{GT+=#~3h6(zKmv zgqe>2nMRnx|0!d!K*9WHXBwDH1I&NEo`#-G1I&M>>!Wc7%=^>8WEx@CrGd#b!knEZ z&2GRfPXm)_ggNRgN&atV3DG{98;G#Gc3#ZJJa zZYFJ)byxJ`zV_2b(w9!Ss{MT5A#mem30JjWrK!E`FyIF{);{iQe;-Vy5vFQ?f0#@o zOld!4shPNWjwj{^1MWwC{jcD1&N1MAr24-PZafWeKa&10?oek)Jjc&#GiWOFZRo`!z;rcrLPf_w56Nuzw|YYWo|vn8oqoWGbxn7@vv zp}siGDg)*srp0mf9Pg5{#`zECA?ztCPn+N@X^MZDPWa!A!%vbG)du|YC9OZsOat5# zr_NFiFi#vzlQv7iJkgp4CesMB=`7_4b6J|S8x5EXnAS%J_?w}P1|67(u&+K#dBV1( zN%I)6&y}?PG+-KFc77T!hrTfx{mnGM?0hSpmPFUN3TEdsX<#yqFgKm09AP%b(@FlfKf}PaR>6!& z`_tgcbi!Sq)Xw<$V;bQG;%P~BE;iuKk+lAO#Wcc9*M6oEW^()E*N^(+S96!W(@A#C|A%8y9n$LJ7BwL!+Sg4w>Qq#3h;Y>Zv+=uX%jVtux_`&O%c zg^4n5B<)Vn34U1i(8J?ZTKV-XZ^wQc)9s;$PQXT#&AuSrs`qAI$-Xe)6f!Su8$#>Q zmJx_YTPN>sf!(d&S9qfl>LCg?(PoZ57_tNtD8F#+4xD=_XFZP_swI5 z@0|D(`FC?4YX37O*8gpscfh@bi1RSV_QEF(`&@-MU$)zU`_zdO?J}^}6cX1mr?$u) zbqH={VQo>(4Nk45)fuD>UDk*C^TJbCa{m*)&hTB!_5L(-IPNnU?jPNTeSOn>#|`j% zD2l+IqrVn;0X}6q#^-y2C;b_W-6*>O*tw%)*_!-*fpORaCvKi~Hae92txd$g1| z0^h^L8*#FC67FrUBA!&s=A$^Hyj;;Zg0qvoE0M%cMxT%S`4a!skFybFZQ7zT5BT9Q z_`&B4y7m8_E%XR&wK|sK-cRt5vh@{~cmRZZ#)=ltNu5StfdlFnr0Trd87^ttE#hks(6BKFtoW0lXY$=ZL+ZJp0CCwN5d13Uoc+j_a!sj&~V z$oCv=(@yN8J-di$#@uAM!672DShV8yRNo5Zu%8I;JWlb*7+?0>= zmS?B=){lVm7l>uMf^~xC9{P6B$Hi}Bo!Weftq@jwId1(Kp`Y@X&<~{2+V|6Jk8c6^ zasJ-npm{yV7~rE%AG8s^$j7K%<|5&D%tbECdDVoo^j$5p#Ol=IJkD4N^K7Od`ObIv zsaG48Lna@78T`Gqwc+p|#P_1%@UwX5_Hul>W9wgG9mN|FoBtKe^PzmBZ}ZT%&q$qP ziZt*a?q}6{a=GnK{TeYk<}F+qp-j@ATfj z^D={!{}=r^D34v1A&h%7&3_0yfwKkl3Fdf0-2F=-e|icn*%w8&Kt{d{+x7=Y!#IME z>|i6!%Cj|ysw`jYzB}Eqb)9UR@9>&Ij)#rd$A>xAo2QdsZ#}Gi53#bJ zVjkF7FDk#!G!*jOi81WNS)r~PpE-y9sK)3sBCq_Vd89=?+pc(i{97!Ke%Zl%eRAmB zJ>WZwzeL9Fa}H$k%}wy0(-$D`nYyrdiFWRm@y|VWg9qn3eBeQc#7j8K{LnmK@!&cc z6QH@A?*oOvv-1;9+Nq$9}8w(vccjDCb8Md4v?3rXb79dVi#pLmUA0Ha;Xan7R zuI`lj8l}97Ycd8pfjWbQwz&0O(ua_}t1xGp@{5Efc;6djo4(rKKQ16F)!qa4ucq%> z&vdj&?p9|y>R85ih1f^yhd0jj3f{9nQh39E1@#_4R(>*qS>S(GeGow=Q zpYK!OeZ^NBGO$~nBW{F$c20K_W3BFL@u0kaM3)nC#7Tc)>`%2}d}or+c{divEoFo~ z)RF~%4K3J=b5gPU&#)ZcGge&CWjSX)p4raN@tU86|7F~_w7Bh@4*F4f_C7E1V7z;? z_u89Vei+9IdqkP>eRuA+gKq|M&)pu%bjtY^T_4M{;l*uPPWq{N_Rht9el62EJ_##b zSy_{WGF_^C@29G~S?c%fwX*!e2L0cb_Za1$QsvL-Q1w60@^}3Q<>wpa7pd|&CYCqa z-^B97GadeDd#zFSsx)Pv`!{99UjaT}(S{GO&zGV21pRCQ51CcJ4eW~#Wd}qsA1?NQ z8G#@C{VnnH!HIJ&csEUOg8$9QrCQF?mwC@Y?-|evKDVajGvB*sWsAfG-YlaFxvfarqk2>yz~{z%N(NrUg(K5S8;eQ;4VGd*N&$;4Y;1-ur)R9^(46S zzY6!MCycVS#n46iP5KpEO?tpqkrN+_7_-F~54@{ZbR!Q6Ar9b;N&bR8 z_PvkK$6WSf$mzH3HRTK3(K%N!AI@ycz3bGe_`7^lzqmi~a)`q(}B0HdoiQ4Job}XtLJ~E@NN#Ye0h$-9wx>L(hKp*IXg7P$ORx=ONKoIOk@xYf&VG z^>u;xaPz#iruwcoYH!25(gZ*3xtDl@_2|!@f@Q=VX9IW_2x8A_`~3@Cc7F|G0^m&g zFvJ=0*GwJ{e~Xl}Xt=|-`wgLk;-}0ufZ-=WbC)G^3S~f9FsQ-*U+&1D-a!8*M{84i zF@q*S(gbH(y!1(N_k2%uX87KdEhGY5T{ms*H-WqhnH|BrDc>cc417d(~sb^C_x4)iY8rlXMM@B6k`BAd-5%hN$ zYX%%ol()t|o^xGcgRk>I;Zv9BAQ+>?OQ5%h0V~vWAbG5!{4-3WUW9b`IWeEu{yC8jiPPEfJ_^b7qcK#3ROKIPk z=Cjvl+L)%k64W;*{W#d1mog3_spFu`=m!})(g&OKrA&(&J4PQUyA1n4bm23m6gd0i zaLR7ch9KtPkR*7BIlS40^9i$3;5yN_DR57>B;LUWrRq9;f8t%jc6PckrT%Tk+!JCboY#ThH&#s#)v`-74*Y9#{4f zc>Zuxdl~q|aTDxwrKSQ$5OUq8S=mPF=Y8|Z=54CKqKoVgXZubWcgVOW0Jq3k!=4 zy`Wg`c-e+{hMCl9t6RG3tGC{>5%f&>k|Rc1rCQr+JJ=^~{gFi;P3fO3Gh^geb)Zx-NRe*92>XfV{Ml__f8Ue9qXaq(x%QXeK|0KXS`3$S%^b; zs^2>~pzpeM_klEe#M7;)2 zat)+p7tRibwyf)~3xD=5ZR>Mz|BtbD9g~#TkaM3sQ)0Esz60jO!6jCncY|#g`m)6G z$o}$ZqE3@)YZ8pzuIw+l)+v|q0N$aT#dx2`Il%r9>=uD8@z=$>X@w79&=0@+7Kca6fekl@du&)E!p;fV zI2y5nuor|fZ`uQTYwZG-H(Pkt)}f(%2Fi~>c^k@yQ67H1T=PnKE6U>;@vm9_DCGF% zz*qc!9Y{l5$3=Lj;hk0S#=qv^cLRP4Pau7ok?u6o%k6)HzVMK_s=B$mp6?n)_+P{9&q})IEa|4}j&;{x_vQFsNV$j1wJb|G{^n;zR{#y)HSi3oIzM~ez+si~ zyVbxkZ2W%0NZ(=n-eiAHaA-@9!)F1Um0%n8pCx_v%mLl?GxHKScJ$#W_amX5pA{N) zb{Tcr_^sY$Xh^@9afLXJHeg)`K5KJjpPGz*c)qAZ8_av^F&|sy zhdg-#FEeoCiCpd>mM=Ne?H?LZ@LG`$d51Db5p$@QYj!*zAilJ5Vl7#f7!O#J%6im@ zD}_5L&gD3ecmRf7oaSAJdGo?p|{(de6A)O zd+4#t00XgW>YEgdp!SFN5SI@yXa_+2xo`0<*AnA?DulE)?mV4%-=(&U2yu%-2BFQX z40zYzc?Zzpr_bJz{jWLkIk681m|M!UyfWCOn{L*=MILapK0crKbktSi9=wuDX}8gS z3u7vGRzTlFJgd}u@K8Q)@dB%y#m1U%9pHW|P?`NS_ZH&&3WSOFE)2|`&X@{W9Otyuy%@PL>z4`-X#yeECTD%hY6 zb!C6n$7hC~9^d!iS|)Cr#CqoZe-|4_*(M@&#<~YFS)s?koWgR9LjW8*jQh2C@1ex~ zmHt-k{;&^M|MkSxju=Ra2S43|pZp|v!QX=WDz@TWvJ+=@)EHPD`9QNoZq>fun5`Aq zAI14{w1;<;ID?SmD2wxnra}wlu)3Qb_+fA8aqJ)aIz0dmTs@I_7;7W*ct`S$c(%xM zqgHikrIuHsIkh?q{7QZ_JvZ_g?)`gAo8%u&+G`6L(+lUHGL7Fdz6|&gz+XZ5XwQeJ zqkwaZy&3UD(U;(jg^NbR4+LqzOYQ){-4!RI-)4UN8T%9Qy$R&SeYDKW>j^Tq%RLvE zM*(L)s^G^I#6QZ5|9@$8$15Jd{ePg*{I93c9rFm^pwXDGp;3<9lhXEIC;lCM7>CEm z%X>QC1bBWD^l@eX6Y&jukCaWoy$i!}CxscY)L+6m(am}cXGa-Vo#&)aJKAur?1t@O z!`{1&GEJRD{Twm+fA;4nY`2cQ?Zb-R{@hx$^Cv5c)^7h^(QP+(6iwgmhs=|6`{KiL z_20efjPP)q2Z&&NF)jqpZLjO^r)9d)-TiDK^Kk8kyOm_!#Mq%S#$s2rNE&ojzd!rw zp?=s1YZ(9Nn*;eCa|7sHz* zW8YfFGKdSN@oYW%Y5?*!@*U>bk9=F9`!yj>CGx_)V1nHPF%0tSqzs-LTA`bLspWP5 zN{?Xe6~sD!6?L#c<+pO{N8IC$&cxkMu?qu||3&OkAjXs2weTyw8+BzPZu=@bVsH76 ze?J%X3SGbhW8n8@b!C4%259gjes9%xN4n9crs`1E^8p*qE-$Sb%{8un>9o;PQ}(M> zUcHxj>ulB>l^1sDv*uNBVecPJ*a2IfyueK_*4bXlm;#>hhQ{Rk7YvvCxtX2{E8bVw2hH;@MV(7g=iG~>&UE=v z_ay(_uJIB2U*YUF*1@htm6{p-olD)d(8)c17tVE8=8hQ!xoSO^^S`CgI#}a78E3(7 zpGd_6dPzQY4&n^`BQO3;`4pGiUpFVG%0G4*J~D>muz}-b%;`KcOkJHk>ii0Pyzl%e z=6qYmBauqYGBubvECO916+U5bJ(ARpbamxZ1{ie}|KHY?t{(IQd|flGtMOf8t-=~9 zdoRwrXR-P$7A}JeV#9^J{sC zA@Wzqm?!Kt+jqxta7VAcnB_1wy8n~0fvo%_+LeIQ{g3Z@kid4taWS6rllCSN%j7l0 zGBMp|;IYdcowT0izI48G64XjP`61{Q!QLw@Eu-v(tQg?+Qzqz4R-60`~#ZPxH2O ziLXkZDd`Q56cix+CZ6Y4d4AE!JcwmyWj;f`6UKV)-dn?xe=p>Rod5qtYahH{#p}Zv z{U`orz}m!g-@f0_?=}I?w|B1xd2}O>$a%yN;(bS_9UEVBqD|Qj-;-ky3s9p^G0F~& z9MUYrhjtyKoba)u6@Hl;;dk09v8HLa2)nY62T?6Q2_H)TT_e1-TP!}2L;qdumz%9d z|1*Z##*zt|y}I+dKarM&mAmFc#$L&I@JLJW0Qzu;J@n)x)Q>gNq8B}=fVLLsLB-ui zwD#^M`lZDxaBqi~cZG271wW06^N>;AXOx%u5BSPD+Lirb7x&(8hyDhAZo5OLjaWb0 zIMI~vA7po8{>{Pj7~^?p946KTngRDo3TjOR_9;{CuU%cxfxjhnl zWbbQj7+cRPzI^hCJ6bNf9eBFmemi;E%NXRH!*Q3VJ~>{05{y?282aiS-(Q&~) zDc+QrKcOeTR5Yy>{>L+Q*}Q=as9$zrcktwih!ND}4}{KTsCKU2A&W#w9*+oHxLOLmDfa+j)nIEBk08 z#(Dg{c-aR*`OnaH7yUeiZoL1Y3HG5EadxJW_lNhcXB`I4*2ZC4Fj}H%D~4$eg6A+T zgmpOEp+SdQ-kAejDks!oJw(31Uipr=pT`bmhdb&-5rVy;-wFFULDP_ngoyJ)Ne!NImjn?s7*5uV8+o&taqZU@PkU4D|hq4W!I zabNj8Td{6;qTkNPw+8ulpyBY}a#!E1xmI7UP3pnDLx7`EUk}(`%Ef(N1<04#^Dt=F zpyfcn$NJ#y?RE$D%Cn zszCb`4b{9JgbtYxKOBwm;=!x6_BQx*tQel_?m1WFzR@C|SPF_-#2Fhi!3FD^b8APn70f7%!qIle7_9*)>ui&oVm4Kf3H zX)mGNSX+FHW)Ifv+CEdyDy5&~n$uh}=|^XNa80jIzvg{^hSQ0d`14uLKR>Su^-~`; zH9R&6_g^B0c&E0XI(wkf4j&_!C>fswd5yYhlSACAp~&kjkbGE^tj2gn>}-r#-M`^O zsc$jR<|nZ)<;s4K{yyc&eoxdBJ8ipmK)1KnjlMNZ*syIC&{l}~YowzCWB9B6b;#Yl z{dy08i|-J}Bz@Y~LmxqY@QVh%Sq%ABtL_WvU7+R#{b_X<=cze7BLG~t24O>kZEBO^ zDSa*EaxP-J*3KCHqtd!>|7f^BTkLJKg005fVZ&ai>+j^PUxMD4(-(*wWB(=VbN%PK z*RA&OzzQ{H8PBfhevd{y-CVk=hkk0G<&wVWqdf3c8^@q4`)~wV_(|+P(kGd63Tex@ z|G4yUb35tD_>*HUL+ro;j4{xPxPTwViL3%$WDLEiEykL&v~D778jM|k@kGe5-k%$? z2D)Cl_@1VayZfrt^$c2kFx>3eVTRYi(2i>!)-%&M&ee)Vq0@i=0)v$FMyWMYTEZelPmuO z{bsaD;Fdq6#tCGRR(_}P?7;Jb#xwFeACPfCT8<+fqD4a2F61A1yDgJ&!sufQaKyZB z9#mJk?|u5+Mim?@(x6XCz962FC))}e?cgY2mp`E8M%HP|eXaLwZPUEj~DQRxOeCkPF}$f)8Y?K|2%SWA3-!Zd0^QrYUqT#4XoM>;vPynfZCv2JgmoUWPlJZZhRS@A2Ml zF=H%N6*QL9zK&RnqlJG5&$Gd9i|6PKC`Ud4PXmv88(E|*e|efE(LB~Co47Wo=)u?(UC&GYi%q-+~ynL-_z+TI2l7s zbKFV7Sp0~=eJNo!ymvy~b=~&GiTq2o0?3Z13o+l*CezR{^)cQ*L0F87%d>)zeXpa9 zS%{@M9C6G={z)R58d+Cf3ndW*QfK!Bh}@*pwe> zFxmaMzY6?Y#l2GTUy1#HtbKcYl-1Slo+M-viWqw0*U$zX3Tl#*dV*Cu$>cIntOJ8> z@Pnc)%0NIvKRm^vpA$1lCS=s0&<4SNp`s4eSg=~5+=6lmf+<*3P(Tm}2^X0Vtnq@( zBq8s4p1t=w86vj*w-MCCXL%&}Ez98Bk z>8(J1chpxrvtwV)af>=9+Hy9PcNJ9~r7d6aQs94NY*`JD#HSw1EA)ROmf8cJBKc?c z4&Vm4(A^QstL$6FyBh&~}5x}S$m54+4ec6)HpPn0}A zf&7#=zQz8)|0VxMd;sq>O9JgXC%dd$neP&~Oah;`z$Ti+carA=4)PPo1L&J!+*hmk z4cZyI_?M8mCxxpzUW|pVY@hqFh=aa$OGVcB)QT)Ob;eZsqH>+km&W7y`0=T8Lma2< zSL%f9Co&a%fM?3@Fpeb$B)cS zJ}Iz$@T7*gLf@nCp{)|Ox)0tvfbAR{KiHc!EIH`4_ z-)s6??2e2o^T@8`e^I+zc7kC<{fgLENd5UO%z3Yadx|LiVw@@5Tfmm_VA=S6^SKvU zc8#bXl?;70Xbc$Tj^wm^(1zx-9^CVu0lz18OuZ+GZo>j7K!G@Lun{M#QPgXG==@+wE}K}5nU zzTov|FX3T&E#M5oK<4JOpE>z0mvw&y*2M$fXKR`zeztT-A3t?t*dSG|V?XtZ-Zcx* zFXbrl1(-PR*rV+Cv#l!R7eIT%P=~pClcW#F=&dm8z?QQ&1NtWFL9QCRpwo51FWdzk zlxw_Ew(%0qw46Ele}GGDM!=7t)1u$pm^K#5a(Iq%qh+7LIN2Y{4XLdX^`#|pKT`@+ z@e|!gMCkzhgZu?@D0-izzXUe0@LSSvju=mAtDc;dxD`5pv&xus3+CI0etVcN8Ga%3 zy?QV5=*9!aG{jFV!~V`|zQ^-8JWoNHR=leq{o)yHXRH6gF|jOocCv4FnCBzX{aa;z zP@c<&Z)Tq*KZ=8=h)Azxe@OM=zbII&1;-^ zZUY_EN{kREnhumKTQlBeeV_Ly$hbep3+jBhJKS}%2Ng(Ppt(fqI2Bpeb)+xGHBipy zI-p2x*JkB>f=IsJhzN6VUDlrOggB>e|10lPoJ zrfA{QecsuYfG@LE(je>~5?7@0D&!mW-fMx4F}8g7#mls@NZLKZU2;8%K+| zm!Heh4E1HrZyt!Nkv$Lh$6`JuFK-_W-T6|RSDicj>@g1-_tlKR{C~W=&wgg? z2kkzjbTnf##wqr|XrxGLGUset-O` z{=J9mgZ60DoyjpPyW3v0AU{!>K%7sUx1qJ!W!*d5qzSC|AfCtL?;iZMc$=?l_BOZ0 z`EYL?o=xRgNT^gz_u)8Yo%!6mbyH(h{&%*!OgmryM?ENPf2_y8N zAm(8v>_t*f}B^&gU zg!tvpP{)t&tb_ZsE>|axbsA#oAm%L2kMes+(|xq^UR@M#@uq(9-YDS9w4(x!OLYn0 z33drwm^}PzcBK98@H5*zXpl0HdOPQtJPQ5pr+tKa3V39v9s6G|DHdT<-3wn%*ncc~~F#R+rYwa!r`^X?*UYRyKe^M${X`i>uM`q3tMgg%3J@gn7b zDs$m!2^aQ0;NLFrM!kXT>&uLfTz`@TbK1+R_7BaNF9lBZt7l9>pM;~ z$xoE;xU75HfFpUX!yUP8iw7^cw^Q((Sahk5Dq1;T)VcItdV<#7#v-qR@|6YFe5m06 zayr|RaUqzW@*{Deq0`bI0{o|IJSbeFJ-~>4$ew!Sm2ldFwBJED2y_;!82r{-xu4+v zj{6DVOUi57PGD2Tx!~^g9mlbEVSKd2kRMe@U(er{Xhi>e)bBSy-#hR_ucImMMTdwB*0x!uU=0p(G+FT5 z5~ObS$cNT}l^6aj*ITX=u~buDAAI#QMwmMC4bby`@CN?DzUr`vfsCuGpe@v}f z`B5r)Z{^2tc$sU%?jN9amGg^C{)T5-X0;FV&HEm6V&wSl#XO-N*9LhY%fOC!7sm=* zw^q}SR6{?~-;1wo%oLPmeAo7e2dQf5Lw+77tBHSCQGW#ZY#-llkIHk+=Qk_<-9p|= zJJ~tKzmLw+YvyP*WTfIX#zOc^1@@ubv!pTpQ0Z}h&5uB~l&|{h9E{WdpS$C>I*yBA z#3E|pLy<90i-*JZ7Vf;;gE4}~J%~P(4zXS55I1@$Kh$~+f3}R}Fo)#lO`I>6byqze z@e}pa;Th%L5N{>umHtBRtKsY7Sh?9ddTuk06(_M$ty zbd?940XN_e$J!8!M%?TN+^``Y8K-fz6TDa2>&W+|{jv%4s%3=YJs+F4`g-WEX(0pn zy9Q@~r@==B9yf0s_JXiC0atavRXy?&rDxQkIyIg=p=q!*5-Osd#hqRhl;s zk+wb7Y>s(=_q%5KR+fne&5mE&c8d@4$Ze*Qtsh6|eE2Bg#AI3jJs% z9=_$L9^!k@j302mN*aM|%Z7i(f0;IQj6|_q6ByGgj7RbAye|m8YVkGOa)6fr?asdD z@2ASx&Yj2$vz_#KXm<#Isb5fcG5v^Df5IL%@Yg?SSO*Xmv^(jBd?DbQzll0jS=N<1 zz;iPm%Cu1Lv6*#Q2Xzr+__KF(z4Z2buxD(+p0pS1{c6x;wWPCJz);6~4?wr|pxYqY zu15^lJ+m^amrA)W)p)Ki8@D}s zVj=EH+m3s(c-IDSEb)t&PKn4 zAH!N*i2bOZ$F|#*T#DZeo%?Y`7xAl!C;k1!q7%YiV{BNtV6=EF_ZrM&5`3c7@e5IA zVUd8+OeYMK{FWxIVBIG2_Os$TwA<_=J8a9+hWvl!6|AOu2RONR` zeXcwg<86|%V=2a3Cv&?Qt_%CS2d^dZX1{Tg^B4(fkdw_6g9Q&wz;m=qb2g~SNa2p@A(>F=Glf2i_kGm~@0v`~@FXOH~(DA$6 za{L&(7jmIK)Q+{3jCF*#SjOFA9ZB8j6mwtq)GuK(POhcxCj(>GHnX*P?ym9A68jKJmRP*nX%&bZWYYp|wkL#UPGT)}5g1LT52{R_+{`<2K$OrD469Z6OBFf;9k~ zU$kL8EzSR)H}u&vt~B|>*1MtCS*Q=vKC1WX zyT}{d#{S8d8GC=5c#}Q&tv{%-w~s?i$^nj*dH`(UX=q~z^^4226C2M;9I_q6Y%!j7 z?eDo>AQzMlF&#P${6Mb6-%2_pY{b{}OuqoWmQ#;$_>o7GJTiQG7W;?HvOwOZ`=1wS znigxNp4xCi%a$)T(J!1k6+V)wIQIlPtiqi;phLu#7`v2R!tOx8C@;DL)d( zf<5HbCmM;nNYqfVH>lQoh6Zx&;sL9p)Bx&CQ4Km7nO|xkB!nH{Y*y zGmF&Qao3TI{XqN_`yn6neFfiG+K;^zxo$7_Dd4qBz!#TlTF^S%K^@Bh`vK}r*(l&Y z-6!oVSXZ=pT}Drf%W;Qm<+zn^bN_C6H#_7pV{vcz5p)f#%h7(3fqiqIZbknW3t{H^ezE$=MQ27g2Mx0T(7*dfpZ zVwP||3ONyPgMYak`sRMvdEytMal|FprA;j9MJvDSA%#)l;dGBjr?H;Tz$b+9; zMVJT!=JhTOALl3teSHl6#YtG6f}R1pNBN7izq}K{{(dCw*BDCx`UPXng3u`jn-;3+%XF8Sa{+$HM#ueXFj|2gv_`2r#Mu=mYZ_Mdle zRk1t0BXQp-c-omYkaJ5(vr>iz@wb|Ee|R|8IL@K=;5~lt9jbqyLmlMLx_FAVRgPK8 zap-G|o8h?JM*9Q6$ym}c$0GHaYT8FOnK75~{(*{L@!V-{=AKTxdlh`^0qAl`zAP7c z`^XyTc;qz?!zhp6smP0wLz|^9IZR!35OgK@S~fL?DljKKI1?Q>iMiObNx_NxlTLL@ zdoFRT@9#9Q=2bp_?2}ke*SV{Ymm4S{KT*40tlbX%B?Hds;q8Lu}%vnWwQfasbzA2|MgLvG6OnkLCAo%Za^wOdEed8(1sG=F60;iz)X! z%B7&((#w=9jVbpc$_bR41O1k9&6QZ^GPYT*nS=LhxipA!68rgGpj|)K>fPM$K@X>W z2KMB4mQkm}@1uS8!__{s$<7XGZPMzx%Hoc!>ysfA@2A|ro;u|O*RG#*A z-vNyds=gZM5qgn#M#jgCUQgWOc{-k_;d!dRMdc;eb;js&i&##pnfW(DR7XJv|{zN+ADk$Mbt*S(}e_6vAdvn}{~Xv?6Cc%{1y`rdZ2lzRx$4tRY% zc!1OwP)GYMLdQ@Z`w3&E^C{y+vwYwCQ&*ua#tL>ud21bc>$;UfeJ(jK0A1K1Job_c z&==Psr%mtn*f}R3TAbB@`wVZK>_L5uO}N7il8=!V2s?1OlFy6ca6fvu;Z>F^JR#4& z_UQAlJv?vfNL%-M-i7sbc?Z|O=lI2jS&rNdKVr-a>iek^hpIL}$9;MY>uX&Zc5CR& zfeo*ycTY(eG!4e+=K~sJ_JY(cTkXM=E0k`zVY@qW$(aD@IBWw_g0wmQ||Cb$M}r$KR<{# z0YC86_1j^5#&|IL2%z_*vn|#`TR7*m?n`%9!6udo85VoYJDitVUzi7)E%d+#|FHvS zU<($@a#a@OIu&tj-<4P+;pkWIS13D6mfglUGsFg>EbT6+=l3;CxJBApx)xQl4E6Tx zVVDEIWwkrpeUZ=d{DxoQIBv6}3A%L<@x(>22kjR!HjTRT@J#WYs@)TQG$9^LOy}LCE6X3z!vQKJQ zZdI^|))-haVqn2u#1aF`kwqGoIKc95A1szwSZ3>(gqUaVMod`b9z6V!qzA-cJk9?3 zj`|C9sD;R}lS03vZ=lp|QV|Cy_o0ebKOd*|23@K?a+A^*h&Z;CkU7WC!havYzPJJV z;#t|ZFvp2+ONNY1bay=|`{P`L2c=eRg-s89sH$a&m%cBRi)zO{#e-qHPKPZsS=ut^ zJdo^{x>j|*NB}+)h)>P~^BHlD-CcPRjwRFsKAa94!Bp_%eC)xdi~4X|e>=fj4`6?m zU~lcS6{vaqco^D;t=})#W*&G>-W|9n^$zR0j^oM>S-F5V$U5%T#%7P=*o`F8HqU;$ zjW;)8T;V(?XxAp?{~p*lV)0oFTVSW$dmyF}HVpA(jrdj=`^=72h^u2f&~g`a-6Lyq z^M##xKB&7^IDh&w|5o~cCjTNo!pn%w!JacL@d>@`s680nv(~#3s~#dQ3dTT19={>v z8yKV>Dr4)aGdH{<<7<-Tvr=iZ!5p}ax1>zPJWuNL1qoLa*N>m{%KKp?u5+dQ#~dVd z!#0qM@y5ysNq>|RvfUHwX>$RLF8D;kW@Nk(y2+4U*7N3F9)$}P?%assG%%#}P+ z*O{ttWqj!iP0XTykMj;*cnEDB`qypQPZJNit>xd?*4J!n+~YnX$4L7H;KDxl)jep6 zpXj|Zj8`QOrR-szVtZgC^(bpt$E~`%?2`Y@YZr`xOw;^!0LqQPyKx>gR({&rz;}?7 z(qBmbqtaQ!AzdDJx{W0$Ka}NJPU?)-SE(oS6Fr*<7#aH@V}?T|m$e_o+E6H;I$?W4 zE<$Ph@VO&Ninn7eGsYYI?K1rO@hu*7DrE`ow?SM@Hs?gc3f{J{-re=1`&lL*x~-=K zIh;y_4LWWJ>%<2BeaKq^o5yo$IaSB3>&myR{C@cs&_re^OX+4}HDxjAq8N4@A?0Zh z>$G{}-h98X6nTP>`>#vfF{TN)EAH3-ad+1(pTzWk5%l732mjBVW=TV?8n!)?Ic8u- zOe*sD1@5O#;_t{n=%gc%yORC+F*oJbW>=ywlWmpYPM!8?fPJ>iSAe+JBiO^VXUVk> zeuq1C7MJh98pQs{W&QImJ@#Jooq+26#Kfrlxqvx3w0=|1JyPu6%{f1THd=WWPtjFl zGUzG^*R*U{i9LZu=3Zhv2DaM<2haWI`z?YXc=FgZX5~`r%`IolEUX9Zg3a0_kNbf6oh<_x{RdRTrURYi(-#jX0M&TF~F;_hsgAZQL1Jg~i&ASP`rRu7mJt z_y_SU{3pzN5NCQ5%Oo9!J{MucBG$rB>F-+?&v+}YpO}?53U&j;l|M2{?yIvg9`fxR zobj}jFXyvf7viUTIR@wai>-64F>Kw-3a+NM9&#`vP_?D=j ztGCf9Qm-BFzyKRfV|xI^EvUPXrrqP{pxpn*Ylk(y$NRP_u~Cv)R`KpRB>qz+~ZSTPFin#hq%YQ?=)dy|A3ANzYDMc? zhnBAr9{QRqGEaKxZ$^$3p?DGU6!W}M_}#l$5AtR;>Ck0un1G-BL}Mge))#r^ccMJ6 z1>2uH{6ANCP~hL7gB;-gmOeTVWvo}VsvjM!`UW~ExGWv8%^zJt2PdQZT}6{ck|z4o z0qf;mLI=}KJItnUD_s1&o$&Eol;arYvIXZ+3XpHVu+xrbIpv2jZ?r>`7Pf(hk%uCV zLfO_geP7YS4RYOGN(+0=v)w2y90M&J1ueXR^HG=+o%hh@d+wuD<}CGsKB@};?yY;? zauePwTEIS;wp&M(?`??2lbWz7TG;e!ch?O!vp-1-(OkxxufVxf4|UjzOvrffMo9}- zlMYQ<*mFDU_0hr$PWwg_^Kx`C68~PrKdzri z_%{{*=IDPjb8X2<)dL0h013#4NISFNSK`cJZcN{=zMJYR8K3I1uINFzG|bx}{Ka}V z_Tp~_{=%P`jQyO^L>(_y2fNfx^e*Awr{Kp~Z0L-+wj|O4?B~g}H~Rq(KN8QAX8)vc zQVC!13CO_tFAVGrkoY{7XTn~tYlXU>%@p<)n&?|?symirf;>D&na zz#*Jpioh?I4!>NU8#>PhnL8wkfAAsLAaI7FUrvdZIE-OnX><<4J-2$$6W1E|`}V-5 zO~7HXJG|pjKEDhPvau}efL(Ug)_#^{JD{4}oJZl)Fnt z2v_%yMMgLLE+~_LbB9UYHL%l1zB814Va}Rd)*$DZAB#U3yc98*(K9>GVeEZt;z2&F|Il@);dJLjgDVc`^Ps9$ZuxCud86f!^dWawAfL`ujhW(+R2mAL! zngZ?Z2JI!ftk0gNJS@U92iAsT`Rms^JjhGim9d4eyTfNBZITu=Jj_3uhCH9#JMG1O zG{qhM-ENlc7$JhpF?5;Yk7O zR(ZGSi?*Ce0nc^5z!%K3kNZAfZkibo7Ed6tm~nixqrrE(y-)5JVCRKjL)v+hD1TVj-F5u|!n33jvYfV=@W*n_I9||G z3SRLeWd`=9o!lSW&x%pBJ>}v>g(!NN&vLVpGNfG!IX(OHotS+r^X$RC9kQYCujTU7 z`$zgi{J66zgB_cn*tLoGPP44S(S}l!ZrsLep8FMSzpXk_JTtA(bXQ;hP7u= zH1PWU#KAJW!(GDfc+eSXM(NPY?YeKdR?Pm;H+b)B3r)EAm#|gH_=aaHl;5g%vGgtc zQP`1hvxmNisLx>r=&TiV<_F*U#g3=-em@MDgk#HwQb+EdG)LNBvhyx9-JN%^X}P0( zPbc!n+w;~pNFQ+(a%AC<(w*MZyyAQ+?G)Q4zVlHB@jPD=KTFyhaQ1OS zDgEiR1K1iw5ysEGPh#Dw4J&c)5b_+HFJ!(#w`R+j7~b zTw{&Kxr^t`6u&zq2*W7XND;0@^#dnK^$d`U)*?90{y*_#JQRo6doc(oSb@e&fdcAnpIK@9i9k{evj-1{lwA%}96nrKi~s=Hha3ZPDCv zkpHkt%5k4kxLu35w%!-9W~c`(q@RJfrGKI7E&3PG2V+JWt{Le{EUjQ4w8^my?v}Y| z!bZ3N-{e9ztcR@`9j0*&$}>~ar(xT~F`H$|o3FP4zbA-icbDy1^Idtf$~Rdl^G$9Z(3fx0R@PSr zKF;~@$*MBWm@<+_u#fWQ*gTV)%h(U#Mm}NND7Mpn6ZnOhGjhMq840_dhDXv}-g?8x zTPji>Suf9B?FCNLyt}WrWgrf$;yUEVDq=n#QLOmFGxRUPw-ra8n{SR2?Lnv5{4(++ zUow96d`GFH74z10jU2yAToI=351hjny15N&ERDQG&HrDYmOLr2`4iyQChu*O{-0L{ zfS05wJtm>4z$QkuPviL#m`UXev9PiR}9;5VJrx4S}`vuQM&Kl2rImn%aHpnXo6XC-cVH^B&8P8uR|1HK% z_$7>6Z&p6j*k^0rg6?PYjc`#fP49=Vh`Z}*$;Y`S>`hpUXFksWUT(JgEi2h(xQ+28 zTaGbjbAD3_b?=+K%{8aY_jtD*w44II_P>}X5q%`#JMI^-q^v-{><{CjA42+=an5He z_9b<>nZk;5umR*P&cWZY_y_&zrFO{8i`bXc*_#V~sQJwxDNCDQUoYnc^kolh$)f+* z-8D?=KaQ5qeg}D-q5O4-F_XAh{mz0Mah3O8;bhzl;L73J=Q<%wW!HrVt9jls1vFyC zKB*4vDZY#E3b*l)e`b4|4;Nw%;Kz35E#0ylXR4i*kG-I2X7zN8&(C$J`hID6`GKY} zkF0mf{UQ3!E^khJ(Zv1CX867WTUL4wh~Khrp8G@J(q32jIBe?p&3@R2YH#!1oI}P5 zKp*7!@;U8AaXiaK-PA)K2|xaB`N4B=_*&1x!ao|_4)Ef< z^`93|#uMk2f8uz=m8c`(0uQ$l|JcJl8dJBJ=S5%_!Wjo+7vdD+*q7$_vHeYqsgusW z{*8Wy_0{PP_3J18^8LKt9#d!Czt_*}>tpK3^DpLj+KV8UR$;G&J%}%O!`VJs-ibWz z-y7f&;FYS3|1xFDkh8swWnyGBa#rkI1{s~;4nHSphwBAAYZvzpF>`(o<{Nva@2+5d zoCyjQ*hJ3sOcBP}V<+Tu0_J71E76n8_uwnL%)0rljH%hWfb@d;)A>H8f4{wX>ss{h zN6v<7zSI5hMgKgz(ZhC7e+Tp(Kl<^bAH>*9lW?G)y=Ff}P1xJCa!qXIeJAK^=y|r+ zeO!cM`h(uHEe-u4U(&Nf_+IyS=+geS(O$(kj*#w8$|0O(4580MW}gKZiaoGxhVJiC zwwG&5L7$c+vrqK*2f$6ex>fbNd>PC2jcwb@8iufpN1EST4}ZQ5u}4Xu7rkHGc9MM* z`h&Qi;=bS`ORderlf-vsEX6%myPUURtP<~-d+>P+V=%&mI~o(z;<^Ps!#9^4M~r&n zJ+s)q|DqUMJq-FP@Y_d6P2cl=@$#F#Gh__=ix8@YS`wO6}>F;&!-m_Bw13k?J-(2mx z&QANXlyz#{+i%C5W6gc%2ZRZ^L?84u%ll+XZO!6(>Wkjy#8&ccr7vz@R&kxm$w8eF zFb(R{Z#PU|5Y}`9`X&7oR55t?TelUD128O$0 zU>J$@Y{z(WE?om}{-?X^DyjeB{hfHGySbU)%3Jbdsxb6&@@h^Id)`b(S61DqeHdP`)n^@Iej~5Ay6ZWJE&M+mmj& zA2xu?=1I$PQh(`ZM}05N9vqdF%YeSk9k(NXw+FN@VZj)84bK!m#8@aNum%!aDfhk2V@`RS-;DD$ySsQJ*o^jnnNwo?4Q0JK>ly#9wX4q7;vbEk~PONZW2}}@(-h59V_}wL9R_$ z2`~68b^iG3X_k!YYb-ffTV4nHL0?0#52))lEQHr$ud=x6j$0N&#?FQPr568OVZ-8I ze2ry7ca^0W&+l}HdnM1L-3EK3T0Cn)U4gpwXlED71@Ua< z=6nD3Li?ch@gW}CIJc+cIpLyx6a2r+9(Zy`$7WGnGgWx18sMMp-kal&tX#(S!j+=# zU$jsAqstz&KBLy-`8jBRPOcE~-P461{}`tjSCOyb%j@ z?E4VY^*#LNIWX8a?13pia!1xnzJoZ}I_~u#Kf77ae&&3cz4`p(C>MuvLs9O2!YRu^ z_s6`Zq3^3e7rg&{5XwO(JYVmQth-&7I|{vpZK5pR;jZNKFUxx=KF-OJcTz;wT^rjT z=?~?g7oOkdj;y)Jcao-Rc$TQXUD1EE&7QvUJ659nJ1mcQSTi5($ov^HUP#6-#+3al z%1S*m=Dmb3ze&P)*d1BBh;8McK=)@htOQQw`5^lW(a3XdJj)B;4dWe2+cEb<#ub}w zqQ3-=+t+NTE&40k&(Uut+Xb(ut^7@Q_@Phuo3S+6`CKb;upz#4n$PH?XN(r&H9n=i zowj60`OYkdpE(1JPwB6Br0wXY4~OTzE8}Z`qtlDfr=f6tp$`2(|Nd0UfBhUWDqp4G zc{QHzWpOaQHVu48&O2NTTpQteN{0s>p-f}P8_@HLkV67-Zj4>W-(tv1ztpdACxMi^Ta?T; z=he zU-bidtmONp{k*bCt!ZO9>M0$xKu`}h>&1UVJ?XPXtkF!=v+MqHbUpHn#BZpl<)4uq zI&SGy^(^6-de(2Kr*o2D;?aSk%5aBS&;UXX@&N$So-*!MCKUznLz zOuHa(xe_|+RodPJxifbt@#lM3`BQ&>p>r$lO@lvmu7%I5?-m}M=P&;Gg{Hx@51Kwz z@Sc>afGwxuS~v0od9EuP&^g!_cmwgBLi$uG$1&e2GL~&zcreQmUhpIAss8dR+tz$E z4Rf2M&)Q&a2R_SpEGuJ%g5ZUI*p$PI4!+0AKguWUn+APb$tRvY zJ-r9~$ceUa?nxxM5`VUZb-Op>xAq~~A4VKn;?E>L;kWWsJ>kmX*crng`4Q~Gz^S){ z_bxmgslHl_t$SFcESM&~g}=Cq@yFi9Q$#A>4Y&+!sd671{MMI%3v1)lQow;Y{e7D% zM(ip^{q)PlCivp_a?ch!F6zOMm){DjaqY}QTVKlaz(&(h>O=1E-xM#Ge&((@h+Ujp z9nBq5>BW%wiT4l2n*TBWjlem&ESWDwM8Tk)6eU&Rw5?>^_NTUQ$b%>lkRT!3)?W{lwdp$VrS&p$=l&~$NP+!e79LYML7a% zegN;)lzBK)l`{zEu2Rxl-Tl~whxvUkp z)0c}_g$Ecr<7nBaa{?;bDu{!>Hfdv;qeY~3gdlHSMi{wH!jQX3zRXV2pgwb4;cCx< z?!-Ic{NPnDba>&vzwAA+Sf5FZL3XqoVZhX=VUl^c^c|`8_VF8?#39Z-JBb^{uh?fJ zhjHfjyk#SZX&tnhPFl}(^1Q?N)Wx7z?!|ZqA8?;oFb1&<7z=1{ zFrE$Kd^n5-*neNka0HR>B5UJZmVv&-@A6&<{*GjA^ggrA=;?l5o~d}`LCkTftYh?8 zx=Tc9?g&wey-r5t$6_w{g@C<@HcT^KBYNI~F^P=zqMoSvh2=@`3muOU;`o4@g!*c+ z*j|IRGhIBzy<%jzsC{NT&h96=-;!`_72gF633!h)^gG)447^qhN37E2cxA>wVF#~_ zjT->3e4n;6+*7u{(q6J3y4`_cYzMkd=u%yWXOMo9%y-OB;Ifu2#aDi!xpeVt7;)KQ zY+Q-?ok+b0^izyIAm^WWAB%M@`J3cVlS=spCE421pO_Dc|c!iU+M%b5Bf@>Zil&`jhXZr zx3~~@ry9Li`L`PIFK`rZiLz_JeLf#|0UxSNY+_=(bP?zKkPWqtT&uHR*dV04@o@!S*D?Gy=h zr@*s~|Cr0%4vcRl&KxtrM+(9iSDZO67x^RcMgB-WNt*`xOXj+woWwIB;|ISH)}GB$ zmo_3-`O4dGQ1BYd2`D}i){J=ZGV?!!{-);sSlo!Y6qCm%7Ge#Iyd5@P#F4xQ9CJ_j zF7m0}m_I`x|Dbgp_{QL}%v)yKOBz~p$2aVy{H4zx`XPPBGEYC-B40)wyS-%N*J8<- zW6c-(u6LA+_d|_NiH+UyU7rS?bIDzt5yY4vw&s24ES6FBz`ftLm)v`g<_nMs`xc=) zq4H>7=!+-hGbx)N;$0~6j2-UeN~kRnR!IYfVbNX`kvUk`|gQlpK|^K2Y%%aFPA!|;Y&kkf0N_I>R1G~n!*dUiA8VZmSZ`v2I$r**24LVy6s)6o%5fd4feH1|2~HAt-6mP`d*cc2WO|yMgYCyfDbe_ zNPlNOl0YnosLGLo@G7a)p-r){?Cao-qYq59#0>qYUoiSzcH6p!kx71aS z*0syO`!RfeVtD^=BA`P{(f6gCwif}9u!R_A>>>QgJu=rpD|kW>|8k*^K#z3%Vq=mc zt=pe>;ou+h4j$Z{xBg(YiVtb9$@3?(j;Q;*BQHsuLtaR~6lD;{@3Q`8r`}(e<~qf- zxyWNm9S(7HDG}`H@Q*rs8vdo=8SklbM#dQ-{A)#=-cig)8gMDPan2n7l9;C~g1N!J zG`!2e-yHnQKz^m0Jp%a~U~8k!l(%%sj|yiXrUqx{^5z{l>W;i5{cRfOfAP&iJU-6k z5%+REMta&=pSk3u&J|I^C?-QhjUSXZ6}r#>5LkaG{)Y-4pk=BNhu89!%90KRZh1!JL& zFNqW495C)zFm{)*4a8_pt^Dy4w6Uy!zr)iZAG=eZ<#*;Y4bM}1Z2xAFgf%mjcev~N zxhzB5I((dIl}oMb(0AG+kj*}MR^50-t-tPv`uIw(hkS$gMun=7BdR-9!hkt*Me_ng zx}=^KLci|r|B(E#{fFR(sAr#z{gS}BdfU}!>0ecJ^5WfugLCLBc@q1Y@Zj(~xhEB~i9RZ{DoR{eBihp3W!s=|nwu)3_f3{*v603tQMO zaawY^8nLx1#+JIllJ2&xSPwzh)uM?%$D0Igb6G zlXK^?&Vf}zexmfkd5`W@bbbWxQ=kj>LN`pnK50S>{>lMUrUy-M&9QC3Fa37dZxWtG zxEF=`gW}tA5AxwN?#Nf?Sx3WAYSuydyHTEZT#eOz!1nzi{3u_&#q#I_{;z*MHsF2b z#x3?2fGSz<1c^xmYIO{irn${*{UwQpNjcO!I?L4_Icp~ zeEHPZD>5g(Uoq)WG4d!7wh0x#3>HIQzY6d<;RmgCq+{*MJgF*25@ZJMFgtfk%zcM0 zUuKYNJwugSBKaQn6qfSb@R;%~u$@7C1f_ zJA4Hz+wn~8nd9ma3pa@SOpJj(1S`s|k~s34y5zZ`kk2crUq)kwBVRtM%ShUUZ}dFM ztnOE4x-KJm8p`a;hi)eI)P_ULW7?AP4>Vh+uTa+xvZNZ?wl^SJEq+IG37?F9BC6djJ(hXyj7t4 zit#Rc1iy2g(64_XTLfj(9WUP3o9lpUfa}BE zWtBQ?osaR1Jpq)dletnOpGiD&-6FRG)^4#qg74(R&@-_I?U{gm-+;Qa;!eV_AWM8} zdS)T)fKqmQ3%<|uPqcr2_!ajcMQ_Tpc3iKue3y5j0rxl`uFPA1D9O=ss0PpLz^hRH zH5} zb&P!svgUB(HL%sYBb}2yXt>JwTrCc@c!2tax8$}H%&~}l$zHU12<MYqCMc` z{3hy)&YPHyCcfSjlLv*LYwCR&)zA>6-{0SIIaS}KBb0$F=)F1%00xrX~!>= zjgoO&I=^@mWfgT0c@M^XvAPIz=ffQij`EFurK5cGUFA>xt;7>-1A|w%UdyHp6TS}1 z1IXDA{0G)#yq3M9NF>58=$bgre>G$|;(nl0LO;R#L0mJ~bKi+MNvN$d?KPiFl>0Z8 zLudl{e~dlzqin+Dj{Kv+g8^xKN{WJIVH=+T5Bh$kl0&&RKkrb7Z`k1awBY{wJ9DRr z((3Q_>7uj);|yz3e!wws3jafkcH)deG9=T5?Yts1yA{T@<3`pBbc7WY|x@{R`$k!~RC zr0vhtd%1o>C5`fKVPDA|sXv8IjiUTS{UI*v44DI$bOb&s&$7|>s_ABqqg7aNe)Uf1 zbymPJw(6|w7M$}~gSoKJ&J+oV3*+6$o+=}fqUXr|WV=AdIq2o#-{0*)XYTO+74k29 zQMl{JypQ1&byDO&;r&1N>v_>pv|b)iP~W%@)+enC5*xt1aO z3bei)wc%kM^xr$Lwg(dttHixC?RQ%wys&wuk#|D_U1o_{fm!sXsni(Kqg$W#1nm7H0G9?A!MQ?qcEnO$%`6Y39E9m$U;r zHfW)r5BYtzbDM5w1?atT(y9T4-jxI3XS_@me86KB#Kpfcf1UPV^6MJzkKExml35ROBuDSrnM07V z63~l_Lr%l@jj zzwj0mXuC4b!C`EPo)Qb}Z;{cE1%^G)9i;ri?^lUW(2qy0`%ho(lT$|u-I35|#2xH6 z=f94ls%&@A<$9DN3DB>cMYi5Q)r0p<&wYcDaHOnL=Aef{Z| zK;jeS+ZFQM`qb^_KHSXDPkjdXoLSf^-YfJAqp(RFDD;TdLZ^7|2FhsUIdGsI??b{i zd`a8;`{ok9$jF&%Js3cYeq|Y5osdzr4Ry94Z#r$2U53#ijp5*qiV#h<|>{|Bmf3dL!&eN4`Y+!#%=;y)_eZ_Dk9lP+smisWaj3vWn{5 zjdP(lNm=YmPUCz!sULi;FF$SC9e0`i$g^vKqyM4CeR@6nT5@>fT#d&8lz(Pfxt;<~Zn+zXWf7oVzj3E}FO7FSwVoHyjB7 zUfh+NVE(Sh@AP@QE#~k0(AFSbb}eDhW7>@|O>jrLrn7IzA^LuHkwz5F9HIY5?`Mw= zQGCZR^+k^1f83E%5^l`7Yu@fS!n}|;RJ1Srm%tsA^BoVIC*lr*cTD`^H{1FGZT&#O zA>gkyec4Cuij8w2T*`NKWb_jB!{0c|NIHTI^~ettE-%iO?f8Y>vmcpU?Z7t^L&r};a={+Zr-np4AxnERIhjecGs{{Z{}B~K!f zUuZnMpkb0al7t8Nk@OFI{QrgP!&48h`Zj6RX40BG8xUxD2QVbNBdt=-KfH?T-IA2A z)}OZXeYi@mf5x}&r_KgBi98x&P;CYM2QAEV?d2W@?_>8HvETdY+8^W~cHsXieSv$A z)oq+lsSk3V11+Zs7xk{%W4J?xaV3%CvJ7H!rfS}skMCCCw=W*UiTi&LlTnrIx&?Vq z?rgt@b|0LfFT~&MF)zXX0~r&R_wP^+;(LmT56*=>aC`{*k^JxBRVl>RPSDpLn(Bls7#`6TOJ6&!Q&cAsjz2oc$+AN?<_LWH=1#o?QD91YG z<-|hT96;x-95-;6IwdnPe|DyL!rUj!%Tm5t%!$nTjC>SJr@W9@2swE-_xxH8yZY9~ z@&9s1S~jtL1@~W4|dl@&wSOAV~LAvq6l&r)`+;2y` z=4kkanD~!r`)$?sF{%HeZ71&|i=AiAt2xg&t10J~IE;*dFD)PTM963KiMc+wk2nhZ zj4*y@==pip?9(1-rCtwSbWGAe^ne-auU^VG_R6&Jrp>Ow;ttDM`LzjB$%=#9t?$;ob7-nFB2P zx$W1h#Jkza$tW{`_JK6V4v}^?3+p$_2H%{oyGXw0iHhEJTC=%XVKM%4etpAM18z$X{&LKP(Py&-i`xG_CQ`zuQ%Eb=(2aK zc{?JPNs_2{>^v39c;zxfxcSNm&H?3GjVdJ|LRE%O@n<6ZmlPsji~lc zmMdI|+L*rYQhi@Mr*PGFmoED!*rYscU;b7&3c4d7Eh3Kk#x@^gJG)HPX{FxSZ)|5H zy8VT!{ZlYjw?uaz!o4E6%eX0MU{dX~*gjn4%KUfs@lswA*GF5+V&wd@3XBS+G z%hm`sNt!|xD&jZ5f@y)p&>@7Vt-cjWM^G4ElYyx4HTw_X~z!|5ui{xo2 zTg|e3&b4R{oVdXq{_nd~eX+X0X`X36;jG9!3!Mpm!)lS=UT87%VXmY;p6`dPpy9+! zyC(&~_pbY8dfzM7+6wyW1myS#G{dSyt>(ecT;Y&Y;T752>4q30~f+jgZf zi3iA2{ zXz9>s!c2Y*8om;KQa{#(-M?Im7XoSGm zu?IC)`nQU3%sBP{7ODTs=Zf~RmKOjwXQXTZ%%6Xq*3Ue9DXkxSR?XX~;iMIJ&T%fgI{eYR5Pjmql6PdjWU$B2E%9B$quG!M2_ABm3JYiloZSC%x1k^1!dr&LrZG z_;R!~w3_;YbN+?>$Ums#p9Sw%PRcDLUzd4=w5|I6Tl?W|Cv=JB0F$41zrG{W^gA(D zUXN)vWna`@9sZ4!^%y7c`H$gJj)otU^aNWFd`j+_s!3&YmT@974#PO(Fedhx?Nm<_ z;{I{N@5oNx`YGi-`%?SjQ%A5bzSAA)80SGlIM=gG;;~4cb+EG>VZ%ML>aLIQ15$_5 z^N|L)Q0J2c3a)vp*oJ~@{2qMeC+cI1+Mj>*PwdZGlK%SZ#4haDCu`@ z#zpPMN9CwFZhVRQX9B0Ma#_dWP6%VPJX_+0y$xqe3Qvf^l!bX0Udt&2t>qnT*bClx z(Sy20c-LXTHdxe%HEG%Ff8N*+d0}%@@!Ux7ns&SJ)WEk?vs*BhI;kwTdBo)WHw#fV zqk5XL=l*ei=$#hsO?E09O@mF+SP*j*IDI{0q=Y+CnasZQe3GZ|jCQ&!F;&{-^mC7_ zhqZ=!IIsP|1FXaNIq(7}&w*jj0~zx6?X*qP7TvW2Ioy0-1yozDww{viEu(=)Az{pXpO(4!ech0N!KKD#gNbN6AwHsvwuf2V(q zc0e4322MxH1mj57;PC-%v(ucSzyI`+zV-q}9Q2(jI& zU9ffE_JLQo|0LSARusC5BP;_NqivivchqV1Z!>nu-}qi#WE|pmz<030; z@cy74t}*bal&V&dgdy`|@efq=V)6u>`DirPFYULON1oNdn!jzXTyMtvx8ocU)?bWI z7Jes_2hfiVTuJ|{*^b*dF;Ve5F$Z$W0-o0A!+ZNOK0NqXeI-MqKD^r5rVsCtWgY;n z)_?aRt|i=OFWW{en~tYA^s3zh8j&#nq!GpWiTb`JO$AOfZ*O0WVPv1Q{}k9p1q;T& z&mUBAYWbc~!AMpk&QJbFg$IpjnmK`ckPqweWb0=gc=ncl79&GX!_K-_@-D!0i4TZC z;Um{8*9l?{8*^~x%~vum<){9rZ&VgC-&0PSen!H{Im6nNdT@1CW4fp00j^)3fr`cv zK-b3oc8$=rTPI>(z|T)CL+<)?m8;BBkNVUFp+`@_oVNZzmr?q()Z0GB9{fFN|I+mm zu+|^cYZLPRB_judB`-UccRgk;K%QhdL|dGmqt6nKuZq0S4GF zj;~TMw0#y+emTm^^{@Lt{y~e0XTq=*?*%8*$ zJjbQz(I8;cRy2W82FOB7<%U__Jg%sjPK9N@8R`r`q_hcwm?6VvI*txhC0hjg1ia!U**Uyx=Bi@_Y zufii_2KbmgkoqlG;$S&1`uS$8*(Ma@Cn}@O7}JVEcQk&t+9mJFe9&7`bIRB5@cRCS z{P8a#kNm6=@bg*r18<4kzbIecF-5O|Gjka$09|sViXAy7*Qxvsn4w!rz779JA=W>1 zxOawm&<6O$dBrcr)RK1q#xL=hpD5pOS;z3++lh=L;n-+j!nm1>?N0OlX?J**#e+(t zM)2&a;$PsOQM;Qw7magzcqU_DB=2ZOPT62G_6qcKpLqtk)Ml{_^6Dio;9m{)X-;t^ z&q8#UAnq9Rg|m}sxrvrRh;^Wi1$h+l7vGVeJ_E9<6Yq=p%pU?Dwn|%a4!$S&tgYnd zBN20ndlmrWb>mZOc3V>E3+8j+0B7<>%k!|vfdC(@bsf%DY}PnAMR;X=Vab`b?#P}@ zj{tnf;Uhro1om>P)SRE$j~vKJ`7^ZdaysLnO66Jy&(eP6*K#Nq5yt=@@@sbd)$;l^ zyW2RKtYW8T?_>MGFJ(H%g54B5iXT5DxKzE zoS&uLCOrFY4>|!J^!ZumRO+xiKMVY%;Cq5^V2FE_*t4{a@Cn@EHny+O;}0vnv}6d| z0_-dCho7il*JUl*4%nwhX-UEy8YpQ=-lsA{uHA2{gU^@SSvndk!*4JDhKf(bqTMp>%iTZOIS&=DT~78gHoZ z>+^l@8utJ1&3D^SHQ)D1e#7|&{(~-SVJn*8CkAhY3pnRod))6j{P7{)jmWWOUnB3T zm3|GhVU_U$(%u__j<;L#CONN?cNHRs%IakvG!8x}W6rVP98lF_T}R&MD>?a8dI|g~ z;lD|H1@cZvd@B3Vo8#CY=Cuyh`H8}p51PlivtZox+xs%t`?WYq^-!fxhT-JhFho{IUS{*^&cZa?GFG3eeatB(TJPvsQ z+Jr2hw4zA-oPIETcgY-0D*p-mf`2PxJHc(@=hD^<-z;r1lN$V2^L(DnH*b6>WuCVC z9#VL3r(Y2~{%?|QfnOb0m+#|vionabC*qhe<^)H1=vBv-*F6qDV0OYTQUutN<-TyL zypxNz!*I!A!h&}e{qCk`o);GU8-TMWrPU*l`$g4(kN#b`m*I2Vw>_Qpx)Hml_98RS z^EdOJIt)>VxEILlFK_7ZA#N1Cf;nI>t|7>jq>p7f!_tJYW2)%n?XEG(uDW3Pmf?7f6W*lFjeU8iO7$x~Ba6JvHntD@ zgE{Uwp-NHe8xbb0c<_9H9`^(VXHOmM@OQJ0hSOY^*vqv)>W;kCz;~+c5|jQBBb(P* zUx{^R7$NYXJMp(w?K$=gXL| z3j7s3k5@XyNGPN)_8Q>E(E@tz?MjWAPnV-5&Z+VECn?v$(@(Q+=o|D+q`1SYB&{J& zy;q*)4X>KT_o{5kFj?N! zabMZqgt~|s*(vGY5kz|;ex|PTB+8}|PF?m>;Aa1v*`k$awzHEHxt^EvH*Dzmo>0s0 zXah7&yJx&<$Adn5t`2=%MYv%f-9QsL!8ZR?7xEL6n@Uhdoi$H z=c+lUZ(#pyzSp{5v6{QLC4O|B2#6~%&cr8QH0`-((~tw< zQL~>Z0}_CXvpn|=Ua;eSKA&<&LLu9$ymbj={aK!CwoaLxfEcYEx105N#~t1=*DmYb zY(8h4I-WnMpUe4TTjV)-{)B!$K=v~(p|En=60fEm+tu%jI_eva<&CzA{J*AFZC?Es z?PvALHcof5jUd{9fAr1mvQLFa=&6tWAM6p|G2uYDCag2+s@z-Xwf#XR-z)s}rYQXF zzBZ=LDYOlq_NK%sbgpwNlEu%bq$L#IKajKpV=w3;+b((d(OjpnEE^}{@_H0aSM~fg z)gI^=NZZNJH`RUD9iEoVwx6uR{inGi?vb^18O(8({XNO|R&z|~4>Zy-!X4TE8OzH2 zJ7JH+XRt+ln>3?oC9x{&?_SW4s4RWPYj34){utKmKFKp>TY-+*Umx@PvMn`c%S<)q zU8C5SuJb=?%-bK}GZ&i8NCOegH?sBk;z9ei7+ByJ7IB`EIQD%v>#*%G_%LuoTYv8RP#MGII^D#*)28y5GpNw^me}G}aJlKm!)0qD_z?M~(S+lb&Gr)ZGCY_VN zLxyaRli?Nam5I?VFICAwE2IhG%=mY0(UMqE92+MhKJg@nP zaTa{!oYFo1A7$?XA61d%k5}i>2?!W8YH&arMNMLG;)>c$=LO=}a8ZMTpbL62x&!`n z7G`iaOgf|^jV3;3K+u69XreF!u8xVw4k~Ek1Hs2G_!@KoL)0-5AHm0@n~>c9_j{`P z0W!}1@8<)3Q@0+cPMtdE)TvXaBGx zpCU~g)T7>#Hcg%!p&_w}e>u>KZWaCpp4CX231nQbj&pSNIMJcL0H}QATnBNOdzMPA z7GK>9e8xw24s@bjBR>Y6Lnop>#I;7v;=|e1B@v-@jN6oYe|+DmOxD^D8#=(6*&F`y zyx?=^K{uYc%|>I$D>@?jHd>GA^&(?XZ!h7jjmKvzjC5|pTsu2UaV)%zSrYS5+pGJB z(mxpSVRNXTkw#id;p>QB&`MpT8v2&3*ROk$Z6aSbX%)1LxhVFVRro!Ozhi5()R0@# z5pfX6C>#S?8h{C$c@U;Z0R|8ZX``adzah5etQ z`+o^&uF>f=dzzi~Gt_(&e^5DR-vPH9$hdy|6hoaA_>|LYhPtBT-v2WpYH?BqO zo6(@};n-gwp8Zy372^DP!zyd>r_Wag*PfZp{f2M;_pVi0Rky&t zRQVj}+%<-HC7t@pcn6Au*vTE)HTBm#RMRl^{hEeAc}?CMm7CC>zY#ECtw=w6t`SYY zhwJ-OXAWjPl&2Vb?7N&FsUHYj<_Zt>;*6i%PY4<-=o+YIHg@71Rwi(gQAQg}Ir7fu zd3(_4M8@P?jlXeDcNx~1#{UX_O&BZiS>DZrF<2|Q2LA>(8W!d7gby%VqUBPUHIekB7nW~*nVu0w<<;EhUW2K z&erSv=^Je{f_yHn^^NQpZbh#Bp6RHQbHc1k$9#cvnRZh?c-QM{ps&nv$j^#lzlU$t z*WCZ9*$E!DA3SV-0QTT(TUbX^5qwS&n-BP6j5W1sFJyfY;xzAL{d!Jx;csIb@Em*2 z9=5=N67vyx=#y8|dpH}qL>*)CF+KqJ7S4z}&(!-<769JA>pnPB$Y%cNsOOJ-0elhdQQv3mM}L)u4RCEIp9($;d-R7F zg6^&n{inL9^G`LyKhHLDG)H(@WV+^mVp~1$$fk(87Hf02w-K>6izW;0@;>xH`VrBa zkbYIoXxzJ;9<8?l+?tux6%mIB?Nt|b{mF_bjM0V}pN7t$4o-UrXkYG; zpNCz?bk8Cq05i7?vcFa*uI(HwR~}AY29P1(3URqRSM=3)*PD;F(1a&iyuC& zP^Q?=y!Cm{Jz!`%2YMs?@*B#!@^~kd%8cz-nYzvnWaRJU_*349ZnYma7uuD0{_aIr zvlY2o=tuMk)B(EY-CH(vpCL_?!5g%{Vop75v9Y*DR?ip0&q)6@+ahJcgC547HP-mR z&lbHlza~EZfT0dL&=-bJWa1w4CL0|>-}c~#PyC!|vMm9A3W7_GEA9JVM7zNHudts$ z|Dq}P+bB$aNIZ}qM&><-^U0v?n{l=@4|H_vPyP?KvmL^>fS)0pUw-E)#++!!99v~M zuSpyD`+&EGd&;j>vrYW{IR3Wr_wD>!&LV^`SgrWB{;L_4;WWs0_OWjmXbf~xIM!=l zI>Cz8tv)LM`mb&8(cRcL)V{CjN zuBh*6{+}DceI}!rPMm;lXrICuV=Nlkhd4~>%#+wd?DJrZq@TiXDxPY$dcGRQJgf&X zF+GO9Zqa*JSBid5Uo5m8vvt7s^cCWNY|3LfjP?A3{VKPl!-?X4&1h)~d`|W&#-VhL z>zQ#0^u~zaS24?<^VJ`HrO*`-XOMLPPMjf&!Fl&=Q`hLlpFA)AqgHg5&@bn(e>Udr z^@tw@e{7jo(OLVLU+kNR(Zo2w!Jq%6l4k}mHtAOMX&GCQm#xSL|MMAPtZ$s&TfE!U zP_{d5J=R4sZg(AEUD%7yteO2zCE-P%W+I7CT&@K&&ZS*%6#O37+V}uH88(NMv4Su0 zJN)V`p8G-DL;2nBi}ARxKGwc(SANQLUm4=Ub%3U^4<+;U2>fwj3--6FwE9VUE&@MU zU9bsbt`2O$e5HMpwqe*eJ&@UZS>L!;w4o4B>`}yKk6iU7>qacDBjcQuvYNhLu^-&@ zzT?2Aaq+m#UmY2@vb(%~cD(*+(3!3c4y4bh3VdFT`^#;0f*&^Jsw&!0T{klZfq`4- z598&I$DeY(eO70@2NL#cRg`4E#+Wug%Q~vohjKATT@q7{K7~$e8`GI)IWG&nLoSHT7HhP$ML4qv zm>T`Ok z%8{dO=dC{G!FRy%QZ?VaadvLgh2X>2sDW|4WO4v;T3*H) zr%l#4hcCkO_Uw=BT=Yi$p-v8QX zMXlK=uH>8{WIDzwrmKaL#%&?Tjj`7C-UEE`7_SR9gqkI|$F}F=%UB2Zn_{tTU`Iln z7PS!Z%S%C9F5aoYc`SNL^lIVTSdR=3!GDq8g)eaLAbRzatP5+(#26O|Zs(FWCe}q~ z#cbTE$-X(+cCDS@F~OIaX^iF z&sH&A;Oi2^8+7q3ALgy6u1VcpaJI->Pj0&G2Wh?;{`xl166F7rBLDrUPp=i%=VIKV zxTC_tUVx%+sujKFQ`WC}`<52^s2W|Uya02BbC!8UHm(19xfLy6&iulQ!e7I0hdl7f zdZu+DuE#NPYYVI%eUE{uBkEm0V>arzBB_q|!2`DfpV&V!hh>6emC-SN~jI@i#1_S z)^_cYF%79yyJD1@wp}4^^;Y3$qye-yllT>!uJ36?dzgzHzb7tWeWV%c$gH5`-Y zF`cj<2j>&5o-gf zLtk+p70}PI^u7iC`&iTap=+(kuU{pMq@mi$&%MI?t$q-P0q|m6j`4%-VLhLidT>X9 zvqfk*q_9um0Uvv|h4mU5CU1$2=_42e_#eDDg5R-zr>_q73eVV!JfO*bmX+}aFXMj7 zSmK#}*tlc8i)H9romAhey1o}=`~gQZ=*;Z0GOW=6X z9F{e8?*c6A$Ui}AX&9eOp*b6N`^bILKi~tp_=uR?NYcee%!VwT%JG-=EBFuF)V@oH zpy+TbgW$jzuqR?HHx*jmU%7J{05#iA3i}( z@}2lxjaaMxcG<7#u02C}wav%1knLlx;4IcRE-U)Swal;i(4`ysejHpKOONoEa$T9g zmG(QUy@9&v|Ctt9$5pzHZ#=;7)~dg$Gh@7!JXQ7u43D*(Kc7O_3~h>^k?8|`Oa36^ zjMy>X*ErAx=nECOO!6ORj_FSPEMW`z!Cb|6`ZymmS0h=#&)o(; zzlHwRupIcsS`cS6`wLJ%ZL2wK2ftaMzh=S_$oNTu&!+-kVz)nC)`RrTNTxOD*_{QD zue2xQeecmS@|*3%Bj|sK6`3IXG)9wtr#Va0rK{0$o)BJ&^%LpxHux3i7&#h{@sP2& zv|ezC`GdKWT{}tzmlp~D4MmHO$A?`rt)9Q>c@>w%Z;WRhq-XT^37pv}F!nU@<1=IO zS1Y>C@RY+#Dc6uUL_GlQ-i-5yIZ3o1McyXXiL$O0iy1q$wEXKu1x&!bwcrjI9X2bFJ6O9Q+pR zzI8%l7}r-YhYIFR!nuRv(Fx9fbG*mj+5g1Ryek6VGuJpxE8kABA`90OCQT##A^gIh zvcCZ|(yD1>SQ+6uUvQz_M#3O`hSMLLfo6eSZ^@;WTU}p zjyL>m4Q+^4r1%0Gl~b>REpZjg!UnSk<$6$Ve(%SK54}XsKln?64}H5*&%eipvK)AT z@rRy(_0XmfOf&PojBRN>$PupBMRzDqH1#OD8fsFF{TP4hIw z3)MbzlmWpPh}+%5YtVKKw@G;X?mYBCa8Dc>{IwhWVa@+-CHw?s?FqV0;^*I$S6pv@NHkF1AtBB>1QG2bmlnbkG`rtIvjc&=T>GyEOwoW9*mH+Wp8@p$SkGu{}t z?`)KvEoCug;_pX(DtrHzV(ePx@Ehia*x$PHq;8FW_&DPGHhw4mHGMe^$H4G1V7P*L zH2$@$LVfd*#3ybeG%)W8WSsJ;tX*F+K2(smKPduM@E%BT%&&}VSul5uS^nj1Kp23-an|0rdeNHP-l|r&O?_&oN?;t&*1kd{JvQL9t&8$2w`6-RE>E2&?%@l ztf+6!tqfy~RRLdd$`fA3u&9e$(nXkj{G8o}Sa+BS4p@kAnL0t{>yC$HDVV;Q2j` z=kI?FK1#oCSyvkQ&a*kKfsB)z(G@;%o|>$uVh>qljiHa6WY|$2y5q?!(N70$;jX;C zUBGFo6{!(^la$_ybm*KlF}yK9=5Sv((tmD677NcX`I+91bohoWe$NJAZev9+)NQ=v=({hsKleHByhOb&k)P2C3s@Fqj(A%^D~r0J>;+DH_*-#N%gEny6&6QbC(C-x8S>3n?KRTGJr$w41rxe#-CAV z6#90<7=G7tebAEIrf-%K8k?7@U>v}74oAizFvku*_{_^GdZCSw8U>iVcULE&u zVZY{D(KXZgtrO=CET`#vzH=-fXBp?5`YC8UD=|mRn9_fmedYQ!wk|e$-H+IR`luU^ z)G*VB`u}Zad&Y-4hd$Jx1?;T|EnuCR#biy)qB zRwnOh@s&wG-OEd}2H6#9Lc5l;L3p<5>wMUq*;m1(nFI8*LfRw3k4f*bzM`LV(NCN7 z4}0pTw$gklkTLiP=oi=kR{_k6iRk|i_#Ep%dH5s`9!0%TYzNTW{QKK-=xe2A>{!7c z+awO?GZ@WplQ{UeOL8X07I#xCZ{3S(AZ*E(0avqd-;{&3&;-2S7K4*})M#rvVFjGU zgp>8LUh*i+`-x^ebb21{lFM%kSkA*knHTaofckN^+-VlLA-7OplNJ5lW&E!B+*;|s z8LL=*f#5)iWefcE57vFUXb#JOPBCW(&BZI9V|2X3U*KE%v3T(}qAgi#UihqyLS3c6 z6X3tM9{VI`VjgYeJX$-O->4UXKP2(xUf50ceLA)=fiI20xw!0<#+l`@cWPJpM6{_4%WQ0 z>|rbNZ;M!#brFY_^T$5^4O^#8`7itU&}R>B1fL;(orYojP9B7I29fUYD{_{7=lKxL zk9wyA2e6Sm-o^ZYU35jRN!e%OgyR#)7#JkqWqfjPMKAFB;B@9+u~p>aUcB;&R$X-xyb!uUg;AOvraw7-IX2 zU3t5+s`F0I4vr3}Kt|>j{!g({j&ZATPR|DItfS3<{qt{Y%bB(qduHL@FT}1H`Kibc z_6hPJi)UY}oU~6Uw@BkyZ+yTxR0|#z9MCQPb}L>7ztU;Aiz1@W_#j~(1&1? zL0R#UVV#`2x~^!wZnJP7`<2igGN40?tu1jLHC6X5kdd(n{gC_O;5S+c-mDJdu3y9= zC_ue9qpkTY>H<&fn-1SC#2tTN4D08-!S@2jCkfT40{R*7Tqn;2{=@s+L}%{%)QaT$ z1V+en&Pl*AHuSPu*L0S8{IMmttIwT?eQMgi3vKsX(d8{HM;ffX6uz_Izhxo4z6!p2 zA@uRS=a?V)>X~oJBRHc7pU-=DLWZ27hSu!-mUjRS$NNe6m6$O#`*CL*&g_Bz7eW?h z$=d4?^+X_j&+V+E=3(sNI!-oT`G}0~I?HJ{d}R>g+_r=NKSLi((2{HQS*rU=z$CfN zf3lAP9pK0Is(}BQ2XR)4aUcxtJqdZ#2fQyc^eTHz<=oo}?L^Fo^ujXsDUr7yF?E(n z{{i>A-22Madt4#>jz-=F?hDZ-Lb@UScVVw6wjYK4rx50`@Ke%IbUby&l`>!12FD9_ zF4 zRt8_Mfu+DLeZ7h&fR8k!bowTz%GlHngx^r6_+~a!--GXJjyY4^i8>ggqOtnYYcPjW zyA2IwdBe_7jE~T-@n16M0UjJ-+U@~fcdU2;dr{DJQBL*?5F@)B;{tkqSo9s#pRjAi z$KHW$@0>-fN9#r_ck*4@-s(czr!Ry~6&`^*%m^R-6LIfVL9m#20XWm@^KjO@%!%mO z5za*L@aC#d(mtUKuqz5Nw;vtK`l>q9J+uMdk_r8{9sa|ag-T>IeCZ+Uq0gU;cnNFU zm{+fT9h^0V-YGEj(;fysaKFeOwD&#c=pJO{BXhW@!uc0(#pim>g*bW1dz~CFy_Rw= zi|OhioHIjx(r@Tih;P%p1w0usu$#Bw-gwekA$U!e+-r$B(lrURI1l$)!q=%2vGcAB zrH$$YuQal9KKgBJ#1Em*4N)6_VQ=_5_62ou?wj@aT1_wDEoN^rwub|r?EJ!t-nW)z z^;oPZ<~#Wi=pv+LnkxeTF0_IBiafCA!%y_e1b>Rpr9g&j9_V!{{m6t~A>)I45ijAe zA2j>?de*C9cMZxNkwu^PL4t1^zD{9hr4>25k9iFI5w-<-%EG-DSdXdZUd+E0FXDc| zSM*q~E_G%3O4Tav+hUCo!WwZH%UA74_rM3y!*MSPx&CKPrbjVG8P~4ne*08Ad=mPz zrkeS=hSj+0-vvC1Y$DAAhe5(eyZ}Dfg3l5j1-uwvX|)w+vOi!Q^?-#k8McmY`HnK3 zjHfZ)*>!AO*hGwtNGoL0m%wW?em}?W2K;g_t5Mr68Ba3l+`c;|bCpZ*9NVXO9(ewU zeBrtAgax!3v!`pm0DchX3)df)FO2>Ne4$zRg4=mFJ{QL!R>MZj#nMjB#rCARi2Xfh zmmW{pI>Lxu6d5hJ1Ux-@Or2(opTc-%V(-SO`3!j7-rp^LYB<}Xb?d%ok)QW10sl@Y zhW|(#_adAQD{`jvk#{XhThF<2kuGQHtZ2Q+7(G{=i9iLP__-6YCt*AR3+lq!*j72Q zxrworc~1JIOtVqYyeIGb9`t()h9z*9E!hIdiXU?4{>gvjG zZpCkAWFGc^Zf$N2xcd|OA?-i#*D1P*@$kV5Qosu`!3)kRllVHFs0(p4Rq$N#`79%U zkv@l2xq*8*WO!y2cM~xm&cVZmGHMiINBc*``QbPoMN7!T`U390CeYFama`?D<;?s{ zBTV3t{{y`j`u>_GenZ=|wc;KQrx3Wi4sj|3XG!S_>9C*8o5%dvuNGZP*9kiX^iN}Z zJpx}>tnbyYN;@E{TCwgze%fOFzLZYClL~vmhR&coh4o!_NB(Q)W|I8y2Z?qjwA)a5z_ zb%~7Pel+MUsV>(~(H`W^-2>uv@!U$-^&#rIjA^K=(ZDqj=@nMQBYcVb&&RLpu5IzU zr0);+KUy>_dO%HaKkT=GB@n1mh(8#+!1kjuaPx_lsk84>>GVc@$C=j<0(u7zB2K{ z#}zsOa}s(lb??}j0P=-n^y&rF_ZeRu_?%^Mo#_py-DpLZF0fG%=7+4;ycN51kp3HO z7d5;I@`SX$X6NoQ@D&+`53%C!jGxb6Sr~Mb|9maYAYeL6J(Q`EBKNaIu zHDAabmGE2N9kX9fqt6id5ALAejWr))klqPD6ge}aZPYTK!)YI4KDWcRdmY=Nd}W!{ z`GC_|v6gA%TYydMqgb<+n>7md?Dc)t)up_v(UCa?It5PBac(y}Tgrx@Gciu=Ix$I`snSA>LN{e_PQdlXO|hC)kx^4kJz-?wBkKdgfbpLNm=WV;xA>dH=vm|3eM+o`of-uWc!O!UP)+lq`VW?GE4*ay&; z>`8H~qWQba&2Iqxmh~Tfg{cqN;3*|<^I z?mFt2%kc}oT+MGkMtiW$47z<%H~l+k!*7hyVyq3=c}{A4Or2!x95MeV>~UsbU!~?P zsUQDon)tqG$;UcQ3Hpe6m{Ef~8<{6}B=$!hg^XXHeFDbS+@0dmF|>5vr;u0R)$<@@ zrxkT;oa!-ZB;Jzn6Q{dkk9vo6Hz&`X3fjeeJQ6qkomd~R2Qe1k6w`73byeLuHBfLV z?PlUm0JFz!t9KZ~0&C(z#06!3%B8uXb;)tNGxi~#>3i%G)=J2Kx3*`ula79~Ptt|= zGCs<0C-HAHpNbOmDHHwzEu*dIN*}+YJx|g%jJ13l!EgR{ja$y|fcx9MNa7RkJLx__ z8<`Ks`mAZ=Cvo}*AFaD!64v;WCuPy5gnnt=8$6wKgTIsYZ}_NYc`z^U!#R!TycL;m zTahcb*=QJNQyD7&dkLhk;R552(HH57a=^f~{D(52r4Vf({^AN*!yG+-98b}sK=1n9 z_a(+_0Q}&pg-2k%zDs1_k@@w1YimF;?4oTp3LV{6|1+koWl3#eUnSVZyvJ)RrZ3>! z@X081xb!8AMcDUu|Nh-8fxCgh#fM{eg)iWFi9q^`!e4n#0_*01$TJ|g`1EX~-*4an ze>;@mK`BNrNyyLz#%5MYzVGv3Jq~^YU&4*kDl)bdq&kOz-(`l+6j7(ZxHgc-m9~c( z(T6J#*U<&Pn)yX{GS=7bR?u)OZQXW78O{x!=uBI$&aoVQ=PT|?b&A?mEBHyf&!y(u zpCL~x>=)YaDxLEYzQQ7B0Z)q2F=z{;{s3Q@77y%kmB*ax7;E)xTg3i?2fA1XV9eI= z^*>qR2tT&LJDUhMVD7{?0WaO{D8eUB`$)T;b~~)eZ1A1Tdmo2ylv67_!`QsV{z5)2 z{=MM=D=@dpU~`R)e(G7kzwg92iO}89*XeZjwC+E>@g1bFyQ`rJ^G>dUb$z#f4A9pJ?%Oh z1#s4RXRs#bYn-!@Y1|VdPXG+Faz_x4@RO`n$Ug(1?f|?okSHuPXwPz!x>Q4 zb&rqbX(vFQB%U(gVe?GnVd zH$2dzas6AFtD<8$A>oH8zYazC#BmW@8xY1A53`(lnHL7uf&`Boc(sQ0H&Gjvz?LKN zJY|2v{nazNpZ7NLo1w{U-G0qZrlI{^u>aXiN53|vBrw#RBFM#o?d%Mt&R#eX?&i``LPIP$QF5v!hyovcS3h^l%!#7r;eFYg$tk^Q_RM^ar+K z#={5Py8`JiNWWNbQ{L`f&XZvA+5cYTSv3;R^yM{+*djW+H^{dM`n z5Q{DS*-27v9Oe$}fxyR<0>4sYmdM^3{q~rDGWv4~@`_x<{ab_B4*EK}?0$*0hI`uj zw{i^d`wV^&kEj2MvIpR2He7Uh#J=cnKssXL^{+%a_i)MoBt}&&V|>)sdIIi)8_)$l zsUZ{BkLS$2?dYHTApL*mU>z9qLls%Ed=&0k%NJPYCiXyg!yYp*qW29W{3y@|Nyn$Q~5k3Pv6XXi+|wsEvG;LKJu720t`GJw=;Vt( zd%kk<_Dz-E^(S0F{4gKRavuq#@2+P%qPOo;VSM+OrZ|T^OK^uRY`eiQ&blw6{<3=x z^W~m_^RKAKpYwJf_uFee&UnW22=XEiaGSaKZ_iiOq~i=c%At(td*imLseq~E&I9m~ zAuO-qJTA&E{sYc+<9rR;9Ixv*oXv9MdI;C9LrHM`33;rVmk(#xyo~l9MtgC%cI_0n zP6J%5Z$Ds)<%z)rcoQ(~5`HcDu+Lnx|8Vw-zrybZG{SLMj=2xq=dsQo(mwW_dY)mMyVh@wG+?}+?o-h^PmSdg{B_2h8d0A!M`AP!y%%vX zLa*b_TiEuN+=_kg%ipP_o!*N6@(LT3YuQ$*=PzIcZ+as4bjm5$u7BSdYSgrwQb@Z3 z=@hmU%=;7M4iGCcfOQrL>X4XV>?4*m_Q8td&6e)78vFer5PsOx8ippYSQ@ z{btxvZ#Q{F_oH6v4SBFnScaHlEgtOmh@K#@Mo#>cc@Y~jRGNZ2-E(%g*35XPvUCD? z&h@+PC6$OTRQX}Hi9=Ng-J__aG1NHjLpAoM&0@P4xcJ3yD!prv59jZA<__oaTvbu! z8R@8#M?xM1-v__iAcvl+scU{~woKV)^P&R45BSDJ z)QfXHS7(C{HIU}2^FGX8bdK_j%JV4lQ1VHv0YW&ZlT-9J)k^*a*r>0EywKMrJ^qWO>Uz<^es>R#Bw+UEdBL)*3|^gCeX4?DZiR+Jx;DZMk?C z9AeR!wE^!ErTjPTi99#$h0%v)m>b;z_a}SM1fTf4K-y}|2N%yO8rnyj#KFVw*~rK` zK+h54jAP>s4;oB*M%?~M{7#ydZ{a~#Y8o3fg6XBuMY&(^Z$+M}7LUJS(5Ditz@6lCzx&G07_l?bjhk6a#C<&xL zCAy{Hu1VDaudk2}#ZDYOQD_11OcfcBfaQN#p6~z;fjt?He*lA>1cS^;))7e8zPU;5 z?@4NZHem&w?l@ZKRM0b?x`^Lu5LX4`Fxvb!@*dBCyJHzX@rnESB5f$?3wLQ%W1UQ0 zd=SP5W7xNpy5&LNfjx}*1{%06)|Z3Bcu%%ckL^Ls`#s-e<68sQKo}G7wKV8%K^X(1 zOO&w>(1F1d2Ysy9CwE?lCS;y_!-G$eZxt;#H+$!JrW01`2LbniEv(aW^qC4x>)`jG zH_Q2z%pvq~YkbYJk!zM;_OTA_6N!B)eU8ml)TrQ_pAiCViE#}N9!D4fe}QQy&Id^I zx}9G>$@=sh8hpEf73;y;2b0qQN6|5G+`;b#j=jVWVL&^8gZHji?c{fZqxgCp_;^6$ zs8+_&wEHS}-d+u3^#Q3@=6wtozZ}3c!0_(TFf2GEc`?VJyKgDNdWUe>>&15KB2Tg_ z(owFenE4DWNo(K1f77spWUgYpB=@^%o57INKquHIy;a}|C^gZ*Cu4Z@SS^^x`T<|- zG4RP)7#wY&oGWQ}z2isbl2+UW2bnjnQK_4HQdN_eu_6%{y;jX6eMmnlQsmB%|LYyO z^n;Lla%-LT*d4k0e%$U=h*OdlbT`uuIt{UWWxP4oMeU%)@Q`W11L(7+iTyBbLx1g_ zqivXda9Q#J)!Ij{<4NiAW1N5l|tOJH) zjA~WcxP>ZfTp8x}HSn3@IaRDltMd_C2I~di)riUTE9{jFFIUx9i@)FA&sQQw!dMyK zW}y{~u{V6;8PFkau00(`&Td5gf9tI*$YdT>Fg-WDWO}JOr#Mr!26vX1`!XR5yO75z zFJ}ywR1 zp|hOwyvDf)vbkF5vI}`?wxNwf8PAxt)gDj(MY{gLKft<>!siN;=-z%{Hq!Aw+TdoJatuhzWC04If zStF5@4p}x2 zdmpmzxi1TCoAYF97v>kv5eL@VsL-S%-U{>=TU~*4;Z;fLtw_HT>9;dKYzh)H4fuTr zeF*vYQ`h{p@Li-mj`cWr=&vV9-8Z4l3c|l{Hvi_>6k{%;?q0Y$$U2v@IdNBz>n-Lp zyi@kWyp6!A+HnSXUbbyt%6A$x{&&nM+>3E(Z%uUJ3>0>q~8|kCgG>mm5u87#O-epS>iI}J6Zj1iHsYAH%K43bCX8d&I?zP375(*Y8?1(AOPWW>A4`Ayy#ChvOY z1#QI6n_~TPpc7@Y*w4lab<(n(2WXRt@AF+5!uTe&Z|Xx_5NUh5sSooNz9~PbFLA8hlFmqu7E9juFOB^$T*im|y$604!t-SsY<^vvK7x@5h^2EnIxZ%7?_SL&S zD;jX`sRa#;g5G)w6CMzY)KH0e&h&e+n2DFb%Y@ z3pi?L9Rc@Wwjzm7{EQ0H=nE?60xW0VMK(Udhq|Bh5%ZAuiRK88MBU(x&8Yiw(#xW} z({85Cv~DAPWp7GnIp&r08zyJhPgaBAgPy(UXKkO{yj@K|T@!ImefWaQRqFIW#tWEp zSy=z3RR_w;mm{6uSRc|d`F9=s-KTAD9m2Bqc9mJV9r3_NN<2mC6}!mK?De}yYw!=A zriwFDvV4g3$@6iRvp{f#wIKH>L}%oB1hD_yivFj-NG-`~*Nr>L7iCve#) zFY_IFVzCy)b{WBVSkBL1WnW`)6->R$tmuCT4+Oq9;4`1NjZVfTJoNd1`#qlbAI0?~ z=bgVHyOp{e&ms7)KhO;u-Vpz`mga*W^G+o-AlZgvMQzckV28AqoW=8sy=T+LE$b}j z=NH&$knPYn3;k@fdiq30(PqA6|A9_zmrW_>7|rngtfT;URmxa_#&K?7Fl^pKs{i$x z_p2vQ$mTr6IgjBr6(44=z%OvvC}WE`TRr>g?7Gh}wi!5AQLCn4$>XZd^j=)luF~z# zR0`gG7~8Q})8k%Dp@%xoBgA4I!g}oqnoejlMf>t?(@yNK$y%jv0oGQBg7c`exi*NO zSx=wHsUPwH*DkB4Noc(ECak}zI&%CTeGa6m;k27;)^=n!6;T)7L49%6y~EXD#%1Sx zqrEPW{)a`x8R8-ig6$S-GuLSRvafH#ZloR?sj}fO1iOgB{imS)HHc@@h&!HphW?%3 zr{`sq1P9=5uUZu-I0O45I6KL6N?4Ee_3jkel8G_I{5!l9G(jDKa}$18LqD*hH@wHT zOIsbM0x>Tk8zzSKLT|(xoALjc5AhUXKe|C^s=mnSos$Q<!GUsq~|03{V&El4}pR-^m18_$1wK$%;ccc2q_4r49Ob53p~E8UD5L80TK4*VE%g2V&e(Zk;|U$%q+eP+ zfB%wg8(APeqO^hI+dU}L13T+-eizx%)0@Y4^jG_)yWbvO_4r*KGdBLLWYdc(JO3Uv zpz4o5OIdxRIu-LLYy5-iO+ClTFJ6$DlKWF|m+=p)E$7u^%pO(~@tcC*+@Jmt{Q65 zJhSjmi1S4Jz~3xTMmr^F;FD#J%As#R=W1__j4$^S50RH4uT9w|W3)cI9evM~eTAsz z#b_&j=11=3(s`aVd1TBuCPE{k=brS5)$?s1aRvGiTOM@nA=C%(_ZB4camDtp0`6UR zL`LK%aVmw;ws~tJCRRPCZf38+V-tfuUu=_SKKjsYVJt#bFLN^e4(*0Ssg>;4=2 zf|#G@EyWl;?7}@YPOk7a@C_?^z2<8dYreMVT*%+g6yMC1G)+P@3DSh%u@1B=m>{we23hn4G@3dChZ|MDAvIJ zjf?x9%O;)Q)5f#D-__zigTeKe@Xp6?vV#@ZJk&Hp4I!UpTfvH}vzz}@4S}Dk2WcmQ zm-3hE9p0e?{|F~D_f-DUPXPB3V?S_uzPki;JP7-oU%>b68{g#D>lWpmvY|SE^5fJa zxgUu0Hy)+bScJdb5&LzV{x^T@AMzEMwg&kfnJ=06P2oBoZHLP4>_g0$x_TQKIrii4 z>}w@G`0_T)<=X3@1NpO0_5$cLe^|yg#W_;)l5^x>J?8$tZ?Ru+@^Zg#^F)mAul+eE zhsV8t@_pDdrXLIUEIpiu_2f<4B=#)kU*D{j+2CzBcaJ>7OP~|_k!M^Z>>Dd9Ww-r<@wi7|G)v!M8G%I&SmHH}ad7Q@bdqJZQ_+bOU5`-qiPLC!zl>#y$=82cI5~ zJu1Yp_15AS3JAaj639Xvyp%B ztDn)rJm4FHaAqH8Z=FuUAA^gqL!V9w4%h|x?i4|;5sw4t^SpQ??ugDy$%P!-RrdnY z)78mH--_R%z{5KHrr_^79lzFDMtwly6H>Q#zLk5KQFdkHN6^_zu_iEmiS>hh09@sZ zU&Y=Z%3!a6U<$Y15F^04ii5_4dj;-ftoVW~54sT;#;AFVO6He57k)}5% z?bv!^@FehhSmq*e0Xa1Qclhl>eUPm#$ksPjp?W^?J92KDr@kr}Ff`^bvz$Ca`%Y$m zZ$+<_x*%UEtBzW$7JJlJ^lb#q4JZH8WlqUMnahx!eRz{+=uGOws1h7Qj` z=wsNAZuG%tCs-stnc3vEs8^rscfY<)&h*!))Ij=!(x$e#Z2#JWICZd#ML&9q69)-StR+cH}ilQ9qr5@T~U`R>ITL#!- zc~c}W_#}A3eCpe1FR1h*XUOQ2JbiM;;oAf7OU_iofP=cGOVvpx=CXo6QrZ;oTd|8^ z{EY2m0pi$X=Z*vn@Ph+AY8|hl1AAJhJP@I*>**c<6o|hoalEFhqtNO(bRE+~?}7Z) zHoQ|dAb$5DrUPd@pNX;EVH1B>!q$UiK5;+9nC}|x0Wny*&cd1RjVg2gdWZ8&eAiGO zYx`5)LEW;x)oZ5fM7P#$ycRhc?o(5^{?o93eT9uifp42WhKv)ux`gm|uVy;ouX~}R z5N%+Ma%v86+JIy}ahr;_6*^I4+`lg7OInAq6&n!Sfge_g{lr{`ufw$>FMqgpqp!n? z&B!Kmu=B^xH5j{9jOGn~TxMVAMS7-O5K> zC5|3g&bsxUOHuvqnT&*WN)a0dI$W0U?1@NP95h*A^-Iq zo^d^={(9W|9b4#k2AT@;&Iz=QJNG^p{zUrYe8qhEN9QWx*L_Z=x6yqgXk@Cl;?#$5 zHbit|-2FtoCA9_R@7;&HW-!m!ysXH7v~Cva4r*bH0C_N;_-66Rp|*VfKu_rj@kZ zsHQbdgq#kkF?hc=kpA1*EMxkz(C8HCXP3w)+5nH!&tqbtNI&m2{e&!Xaxiuh$2gGw zP%-Q8x?VN;K~bZ0E%|R zj!l0Iu9evy?C(nrzr#6>Q;$&BMSju$Ok41i=9wMzUg8gyCTB*<`<&4=vHi;y;_Xq(qG83QeUUTTNVb8XW|HLdO=m4dsU zmDd3+(cXi;)V#?16t0*f^&@7Pf1}9qg-!V=GZ1B7N14Y_=5v&R?}_gJ71IAM#yETY zV5KP&B-|QLr(q9+c)DT$>(%}$M{Xw{;J6g4RK_I~Tjdz3d!uFU_XgRZ|^KT`K@zY)h6Ka4|vgl*MM?$|+lTX71;C%5BG ztdFJwPxi4e==P@`+x|S(nP`93d**N0{d0zBIQo~Nuh7Y-jNmtne;93CjWtp5_-*{4 zkLjjP)5lp0__wKB>?LUH+M{GndP2Wqe`Dv7k*^`5_jGCxcA<_^;bq#!D|UmNH>}9t z1c$~Rqj4C{*@Zsf45TIU8gvs9JW9 zFPn0j@Nw<{FRduY{htJ^SZ`B*3AkUB`%efbZDhg!KCiMAbl}hF@F4$1+Bc=c6a3Gg zR32Q7x%jNp$oYUhb*&G3wLVNb(|$+8W@1l#ga1b88o&efxIp?n$^9SB`%kp|`i<}+ zjTcur=B{1y2-c4E0`t13Kf-6DM1U6Oj+5?E)NWWj?g~4MWkzOB-)dAU82zeFLK?har zSD{TGffwjB(Fw>;pc9m};8)sPtK0K|A3TP^?r+t!_U#CbW9=ubag}qph-;>b zr*YT84Z^EX?g^!zxO|~*iE)gr@oJ%?HCxWiJBSO8IrcHIr!#B`=L-5jofmWTa2fVG zm~SWZ8C>RSee=p!nZ|M~kFqan-VhM}&v_MaZ(E2geByHouzI}}9lK1Ko`8Gn2e8-e zrc9x%F!~(V2?{Vm*ZxF!9oJE?twbPa_fT(T{1woQm-7LIl6XBA{M?@ZRQ7S5zP9nuK&TF zFUNOT#r`bV1`(%leMh16Lv-cS2V2q0g})Kcdc09H8`^$Vho$_ma`4Qgfh6lZ2uwV1<&7%aUKp>xUaB~^NVXV z5e_1^po0-PC?#0;*_zGR%KOMRr;Qo0|#=ik4(+qw%?pZ~)`p~Xe-U=CX z_VMxd?R8?mf-M>MF>Pbrz}Xhi%T>_9iL={aH@cSX5m%3u;NI&}=Zi=C)wNF_K)h(@ zu!)7sy%VR?j7uT$VBQB+U>{@?%9%8Q3wE`c(=R zn{imuZ*)2#TR{u1-1-}xuA9148UKbJS45wnYrEXdcrRS@ui&Nd;ilj^hg% zvY!2YY?Qj|cyhVM4Lg_ro%4^@KcyJ0witis5v);9Xsw4DL*cI;lrOY%t6QLN|SN2{3^ ze9lYVj`Pqr8N3kpUvpj275EsjV%2FE%j zEBX2_!#Zdd_x8A^gnjpw82)Q{?rB-AC*b~Xxp%~J4!))9blOp;>-=J&nZiW98SROB z`@Y0EED!TzK2JoQhk%bK#yW5{mpil?peNLjhL%67~%_XhEPDNOv_IKja}fUBhdq}@{9;~ZhVXA4{xzGlt$2t2n)&W+x2g{d z2A<6R&k~tq)8=&MwHUup;_si_V?|4!1ej3N>F+RA<^pZ47>5w$0LKWj0CAqL622#8 z!#{b}=rTqo@*I7+=q84aMr*pdtdDi;GQ-n=53FxWiH{Ia-g%=(c)lwT{z> z`w{1;7Xs;bUBvv2O{!JuX;ZIw!^5YbJoZCML|)VWc5wg4T3-kcuhqQE!TE0b7^e<< zY`kj49i1Mo1MG_Va!%25u8y)^qdR-@PcKt>zxSxT)8Xs;0&FGim>)jawddg%y0*Ux z`ZRR!pigw8m7vX)xu<(@ra|_1GpjyR9>lm-E%5CmPXPX4AH#jod1)S`xrjp=UYP|OE!QY}I;`>Hh#q`7eH#J@-CXL1LcfofzVE)8x5L@6w zAnO2G8$s9AYoi_D=T13or_>iP=Ld++%`;%zPS38w`ErrzdR?)r>^Ja3uLZ7yTsPIW z#b7|c3cIkbs0seHlIsk#{YBY5u(`$aRtsO&a~U>j)1KbHIryNQIWz5HUT%?o3vG^o zPgn6MwgW$|2(= zqqa?G`s@p9JYG6GsZ0a(9UFR~(9AU4%}ZK=FVa-WlV}TRQ)Hf)wq7@Fkx!U0n-a$X z_7_d8$O%HLrVQ-qN4}GH>H8UDtZ32MY-foBKVjjuR}SXixQmu^apuu;amp}$({-IO zTep8?T~hmZ@VoBokyX0Bq6Bj|DvN*9rC_{b<765hw%O~ek<|&5`O37x7CXJg}&?BR4djq z8=&W31v!+&Q%9ET^;P+9wvD+%9kP{WedApo!1WUNqWCVZp5#0VUs)4F6R~t3HR}Y7 z4d>GQ^&(?nI~6-6WW(enoR4hKcU%5`qGpbk1%!z z&q3H7xK|~12gb!N7M>vdN9eRkz2ps#T&VkXmGBuEzea_)KGdt7n{f^~m3<4O>%03j z3`*N1`r|fHsB;7WnP(7|@pxt(;7L<1XG@4ap6Rqkh3?ftByYJWyM_{4_X0cGU7VsqtQ zW$8`uB`H%M`l~uT71uBC|AC#<&;8XX_aIWizjko^1L?u{2&Wkj-uY$H7mY|tFYvZQ zhP6Tu!gRr1$2&{#0z6v;}Pxo5Ov_mIDpIm;5^u^E>i|86)ts{#oYz zr+6>d^ypbhyj#&r5;PSOIfDFwB>s2iy_(jO@01VBNx&a5{I4X5|DD;2x*?x0S<8Hg z>n(HC8}`5_!_l!_!k)k5J)~h7H45(w&HGfmo3-tzC3x35M|c$1(FXQWpW%I?&VS}e zynC@mutjfNLcQ^cN;JzSuA}{zvrP)Aa*(=r`Y3(Tn%ls08znb{5>#!M(+^ zPLDvZw|d@rlHV|=uBM(}Jc04vxIg!<;1l}7_qCYMW(}GJKhT-Mb&Spa$voD@^$hAl z{{Img)%>DZcropboz(e{@|FMUHZ;d4zMrJ~f7Z_e?*E!j+b4Mn#tJ@#A7`>(vaaiS zN5;~?LYj`P$uTZpES(yz{Z(D|-7z*AOoDe7;1w7(yhZc)Jq~Y^(|I(^Uv9x? zK5^gLfctTM{{2kYk)RX7|L;>98A$7ZjrYj6BhLmaGW-Lk*ZS1WoSV=$y6Hn@`ut1E za?9yju4z+Z!9}SD(f>M9i}8uA9{n1|Z62NB8?E&}^eYQzUsBn=hWmdfu?@mY*wN2i#?WK? z^xpvt@?HEKpo1Dehi7zub_z`zKk6Lbb#352#lZch++{5`;)Sc3X7E0mF#&j{lJbuG zFf#s;Jt9ZZ-<9T@E9u*2Q)g_hC7tcvu#J0U{#M`!G)bFa@4k*^zr*p?zV^CrDeEW? zId^)yIwT$memz((!`|1?%5?m0!QXrIJJ!rPEy8juc6RLOD(+||T>wvYMRzjBQAacN zbRDzW##mi0dgXG!ey9J|kQsf+ysz0porXgQbO&c8L>u9&_BJ~0~Wo_`Ts zU_B&0lZ>h0T|fF)+86Ql>tf|1HENlxp+FV_uY1)x5K9w7zRqXrbf-dLz ztIoa<^ceA#@Jt?jqhb3L`2*gB`7@ODn*L^U+ym*qyd{=KU$)MNp}RP0DB8Y?^cP64 z6nRJZv|q*9#h^dPmF6 $fU#5B%d^s_ZdFSKJeSCo^nE8==Ng!N;^ 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Ν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