aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-11-04 14:44:55 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2017-11-04 14:44:55 -0400
commitf0a32ee42f73a7e6229d6dd68d222a507447acd7 (patch)
treefdd2e2185ab8b37b60b0f469169e4c75abb1538f
parentb1878b857c732830ec38204196f99e4236b57b6e (diff)
parentb33c87328364e803d74e703d8e47821740dbfa87 (diff)
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
Pull KVM fixes from Paolo Bonzini: "Fixes for interrupt controller emulation in ARM/ARM64 and x86, plus a one-liner x86 KVM guest fix" * tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: KVM: x86: Update APICv on APIC reset KVM: VMX: Do not fully reset PI descriptor on vCPU reset kvm: Return -ENODEV from update_persistent_clock KVM: arm/arm64: vgic-its: Check GITS_BASER Valid bit before saving tables KVM: arm/arm64: vgic-its: Check CBASER/BASER validity before enabling the ITS KVM: arm/arm64: vgic-its: Fix vgic_its_restore_collection_table returned value KVM: arm/arm64: vgic-its: Fix return value for device table restore arm/arm64: kvm: Disable branch profiling in HYP code arm/arm64: kvm: Move initialization completion message arm/arm64: KVM: set right LR register value for 32 bit guest when inject abort KVM: arm64: its: Fix missing dynamic allocation check in scan_its_table
-rw-r--r--arch/arm/kvm/emulate.c6
-rw-r--r--arch/arm/kvm/hyp/Makefile2
-rw-r--r--arch/arm64/kvm/hyp/Makefile2
-rw-r--r--arch/arm64/kvm/inject_fault.c16
-rw-r--r--arch/x86/kernel/kvmclock.c2
-rw-r--r--arch/x86/kvm/lapic.c5
-rw-r--r--arch/x86/kvm/vmx.c3
-rw-r--r--virt/kvm/arm/arm.c31
-rw-r--r--virt/kvm/arm/vgic/vgic-its.c73
9 files changed, 87 insertions, 53 deletions
diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c
index 0064b86a2c87..30a13647c54c 100644
--- a/arch/arm/kvm/emulate.c
+++ b/arch/arm/kvm/emulate.c
@@ -227,7 +227,7 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu)
227 u32 return_offset = (is_thumb) ? 2 : 4; 227 u32 return_offset = (is_thumb) ? 2 : 4;
228 228
229 kvm_update_psr(vcpu, UND_MODE); 229 kvm_update_psr(vcpu, UND_MODE);
230 *vcpu_reg(vcpu, 14) = *vcpu_pc(vcpu) - return_offset; 230 *vcpu_reg(vcpu, 14) = *vcpu_pc(vcpu) + return_offset;
231 231
232 /* Branch to exception vector */ 232 /* Branch to exception vector */
233 *vcpu_pc(vcpu) = exc_vector_base(vcpu) + vect_offset; 233 *vcpu_pc(vcpu) = exc_vector_base(vcpu) + vect_offset;
@@ -239,10 +239,8 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu)
239 */ 239 */
240static void inject_abt(struct kvm_vcpu *vcpu, bool is_pabt, unsigned long addr) 240static void inject_abt(struct kvm_vcpu *vcpu, bool is_pabt, unsigned long addr)
241{ 241{
242 unsigned long cpsr = *vcpu_cpsr(vcpu);
243 bool is_thumb = (cpsr & PSR_T_BIT);
244 u32 vect_offset; 242 u32 vect_offset;
245 u32 return_offset = (is_thumb) ? 4 : 0; 243 u32 return_offset = (is_pabt) ? 4 : 8;
246 bool is_lpae; 244 bool is_lpae;
247 245
248 kvm_update_psr(vcpu, ABT_MODE); 246 kvm_update_psr(vcpu, ABT_MODE);
diff --git a/arch/arm/kvm/hyp/Makefile b/arch/arm/kvm/hyp/Makefile
index 5fca24d52fe6..5638ce0c9524 100644
--- a/arch/arm/kvm/hyp/Makefile
+++ b/arch/arm/kvm/hyp/Makefile
@@ -3,7 +3,7 @@
3# Makefile for Kernel-based Virtual Machine module, HYP part 3# Makefile for Kernel-based Virtual Machine module, HYP part
4# 4#
5 5
6ccflags-y += -fno-stack-protector 6ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
7 7
8KVM=../../../../virt/kvm 8KVM=../../../../virt/kvm
9 9
diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile
index 7c54d8fde855..f04400d494b7 100644
--- a/arch/arm64/kvm/hyp/Makefile
+++ b/arch/arm64/kvm/hyp/Makefile
@@ -3,7 +3,7 @@
3# Makefile for Kernel-based Virtual Machine module, HYP part 3# Makefile for Kernel-based Virtual Machine module, HYP part
4# 4#
5 5
6ccflags-y += -fno-stack-protector 6ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
7 7
8KVM=../../../../virt/kvm 8KVM=../../../../virt/kvm
9 9
diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
index da6a8cfa54a0..3556715a774e 100644
--- a/arch/arm64/kvm/inject_fault.c
+++ b/arch/arm64/kvm/inject_fault.c
@@ -33,12 +33,26 @@
33#define LOWER_EL_AArch64_VECTOR 0x400 33#define LOWER_EL_AArch64_VECTOR 0x400
34#define LOWER_EL_AArch32_VECTOR 0x600 34#define LOWER_EL_AArch32_VECTOR 0x600
35 35
36/*
37 * Table taken from ARMv8 ARM DDI0487B-B, table G1-10.
38 */
39static const u8 return_offsets[8][2] = {
40 [0] = { 0, 0 }, /* Reset, unused */
41 [1] = { 4, 2 }, /* Undefined */
42 [2] = { 0, 0 }, /* SVC, unused */
43 [3] = { 4, 4 }, /* Prefetch abort */
44 [4] = { 8, 8 }, /* Data abort */
45 [5] = { 0, 0 }, /* HVC, unused */
46 [6] = { 4, 4 }, /* IRQ, unused */
47 [7] = { 4, 4 }, /* FIQ, unused */
48};
49
36static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset) 50static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset)
37{ 51{
38 unsigned long cpsr; 52 unsigned long cpsr;
39 unsigned long new_spsr_value = *vcpu_cpsr(vcpu); 53 unsigned long new_spsr_value = *vcpu_cpsr(vcpu);
40 bool is_thumb = (new_spsr_value & COMPAT_PSR_T_BIT); 54 bool is_thumb = (new_spsr_value & COMPAT_PSR_T_BIT);
41 u32 return_offset = (is_thumb) ? 4 : 0; 55 u32 return_offset = return_offsets[vect_offset >> 2][is_thumb];
42 u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR); 56 u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR);
43 57
44 cpsr = mode | COMPAT_PSR_I_BIT; 58 cpsr = mode | COMPAT_PSR_I_BIT;
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index d88967659098..5b609e28ce3f 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -79,7 +79,7 @@ static void kvm_get_wallclock(struct timespec *now)
79 79
80static int kvm_set_wallclock(const struct timespec *now) 80static int kvm_set_wallclock(const struct timespec *now)
81{ 81{
82 return -1; 82 return -ENODEV;
83} 83}
84 84
85static u64 kvm_clock_read(void) 85static u64 kvm_clock_read(void)
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 69c5612be786..36c90d631096 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -1992,6 +1992,11 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event)
1992 vcpu->arch.apic_base | MSR_IA32_APICBASE_BSP); 1992 vcpu->arch.apic_base | MSR_IA32_APICBASE_BSP);
1993 vcpu->arch.pv_eoi.msr_val = 0; 1993 vcpu->arch.pv_eoi.msr_val = 0;
1994 apic_update_ppr(apic); 1994 apic_update_ppr(apic);
1995 if (vcpu->arch.apicv_active) {
1996 kvm_x86_ops->apicv_post_state_restore(vcpu);
1997 kvm_x86_ops->hwapic_irr_update(vcpu, -1);
1998 kvm_x86_ops->hwapic_isr_update(vcpu, -1);
1999 }
1995 2000
1996 vcpu->arch.apic_arb_prio = 0; 2001 vcpu->arch.apic_arb_prio = 0;
1997 vcpu->arch.apic_attention = 0; 2002 vcpu->arch.apic_attention = 0;
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 95a01609d7ee..a6f4f095f8f4 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -5619,9 +5619,6 @@ static void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
5619 5619
5620 kvm_make_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu); 5620 kvm_make_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu);
5621 5621
5622 if (kvm_vcpu_apicv_active(vcpu))
5623 memset(&vmx->pi_desc, 0, sizeof(struct pi_desc));
5624
5625 if (vmx->vpid != 0) 5622 if (vmx->vpid != 0)
5626 vmcs_write16(VIRTUAL_PROCESSOR_ID, vmx->vpid); 5623 vmcs_write16(VIRTUAL_PROCESSOR_ID, vmx->vpid);
5627 5624
diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
index b9f68e4add71..95cba0799828 100644
--- a/virt/kvm/arm/arm.c
+++ b/virt/kvm/arm/arm.c
@@ -1326,21 +1326,12 @@ static void teardown_hyp_mode(void)
1326{ 1326{
1327 int cpu; 1327 int cpu;
1328 1328
1329 if (is_kernel_in_hyp_mode())
1330 return;
1331
1332 free_hyp_pgds(); 1329 free_hyp_pgds();
1333 for_each_possible_cpu(cpu) 1330 for_each_possible_cpu(cpu)
1334 free_page(per_cpu(kvm_arm_hyp_stack_page, cpu)); 1331 free_page(per_cpu(kvm_arm_hyp_stack_page, cpu));
1335 hyp_cpu_pm_exit(); 1332 hyp_cpu_pm_exit();
1336} 1333}
1337 1334
1338static int init_vhe_mode(void)
1339{
1340 kvm_info("VHE mode initialized successfully\n");
1341 return 0;
1342}
1343
1344/** 1335/**
1345 * Inits Hyp-mode on all online CPUs 1336 * Inits Hyp-mode on all online CPUs
1346 */ 1337 */
@@ -1421,8 +1412,6 @@ static int init_hyp_mode(void)
1421 } 1412 }
1422 } 1413 }
1423 1414
1424 kvm_info("Hyp mode initialized successfully\n");
1425
1426 return 0; 1415 return 0;
1427 1416
1428out_err: 1417out_err:
@@ -1456,6 +1445,7 @@ int kvm_arch_init(void *opaque)
1456{ 1445{
1457 int err; 1446 int err;
1458 int ret, cpu; 1447 int ret, cpu;
1448 bool in_hyp_mode;
1459 1449
1460 if (!is_hyp_mode_available()) { 1450 if (!is_hyp_mode_available()) {
1461 kvm_err("HYP mode not available\n"); 1451 kvm_err("HYP mode not available\n");
@@ -1474,21 +1464,28 @@ int kvm_arch_init(void *opaque)
1474 if (err) 1464 if (err)
1475 return err; 1465 return err;
1476 1466
1477 if (is_kernel_in_hyp_mode()) 1467 in_hyp_mode = is_kernel_in_hyp_mode();
1478 err = init_vhe_mode(); 1468
1479 else 1469 if (!in_hyp_mode) {
1480 err = init_hyp_mode(); 1470 err = init_hyp_mode();
1481 if (err) 1471 if (err)
1482 goto out_err; 1472 goto out_err;
1473 }
1483 1474
1484 err = init_subsystems(); 1475 err = init_subsystems();
1485 if (err) 1476 if (err)
1486 goto out_hyp; 1477 goto out_hyp;
1487 1478
1479 if (in_hyp_mode)
1480 kvm_info("VHE mode initialized successfully\n");
1481 else
1482 kvm_info("Hyp mode initialized successfully\n");
1483
1488 return 0; 1484 return 0;
1489 1485
1490out_hyp: 1486out_hyp:
1491 teardown_hyp_mode(); 1487 if (!in_hyp_mode)
1488 teardown_hyp_mode();
1492out_err: 1489out_err:
1493 teardown_common_resources(); 1490 teardown_common_resources();
1494 return err; 1491 return err;
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index f51c1e1b3f70..547f12dc4d54 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -1466,6 +1466,16 @@ static void vgic_mmio_write_its_ctlr(struct kvm *kvm, struct vgic_its *its,
1466{ 1466{
1467 mutex_lock(&its->cmd_lock); 1467 mutex_lock(&its->cmd_lock);
1468 1468
1469 /*
1470 * It is UNPREDICTABLE to enable the ITS if any of the CBASER or
1471 * device/collection BASER are invalid
1472 */
1473 if (!its->enabled && (val & GITS_CTLR_ENABLE) &&
1474 (!(its->baser_device_table & GITS_BASER_VALID) ||
1475 !(its->baser_coll_table & GITS_BASER_VALID) ||
1476 !(its->cbaser & GITS_CBASER_VALID)))
1477 goto out;
1478
1469 its->enabled = !!(val & GITS_CTLR_ENABLE); 1479 its->enabled = !!(val & GITS_CTLR_ENABLE);
1470 1480
1471 /* 1481 /*
@@ -1474,6 +1484,7 @@ static void vgic_mmio_write_its_ctlr(struct kvm *kvm, struct vgic_its *its,
1474 */ 1484 */
1475 vgic_its_process_commands(kvm, its); 1485 vgic_its_process_commands(kvm, its);
1476 1486
1487out:
1477 mutex_unlock(&its->cmd_lock); 1488 mutex_unlock(&its->cmd_lock);
1478} 1489}
1479 1490
@@ -1801,37 +1812,33 @@ typedef int (*entry_fn_t)(struct vgic_its *its, u32 id, void *entry,
1801static int scan_its_table(struct vgic_its *its, gpa_t base, int size, int esz, 1812static int scan_its_table(struct vgic_its *its, gpa_t base, int size, int esz,
1802 int start_id, entry_fn_t fn, void *opaque) 1813 int start_id, entry_fn_t fn, void *opaque)
1803{ 1814{
1804 void *entry = kzalloc(esz, GFP_KERNEL);
1805 struct kvm *kvm = its->dev->kvm; 1815 struct kvm *kvm = its->dev->kvm;
1806 unsigned long len = size; 1816 unsigned long len = size;
1807 int id = start_id; 1817 int id = start_id;
1808 gpa_t gpa = base; 1818 gpa_t gpa = base;
1819 char entry[esz];
1809 int ret; 1820 int ret;
1810 1821
1822 memset(entry, 0, esz);
1823
1811 while (len > 0) { 1824 while (len > 0) {
1812 int next_offset; 1825 int next_offset;
1813 size_t byte_offset; 1826 size_t byte_offset;
1814 1827
1815 ret = kvm_read_guest(kvm, gpa, entry, esz); 1828 ret = kvm_read_guest(kvm, gpa, entry, esz);
1816 if (ret) 1829 if (ret)
1817 goto out; 1830 return ret;
1818 1831
1819 next_offset = fn(its, id, entry, opaque); 1832 next_offset = fn(its, id, entry, opaque);
1820 if (next_offset <= 0) { 1833 if (next_offset <= 0)
1821 ret = next_offset; 1834 return next_offset;
1822 goto out;
1823 }
1824 1835
1825 byte_offset = next_offset * esz; 1836 byte_offset = next_offset * esz;
1826 id += next_offset; 1837 id += next_offset;
1827 gpa += byte_offset; 1838 gpa += byte_offset;
1828 len -= byte_offset; 1839 len -= byte_offset;
1829 } 1840 }
1830 ret = 1; 1841 return 1;
1831
1832out:
1833 kfree(entry);
1834 return ret;
1835} 1842}
1836 1843
1837/** 1844/**
@@ -1940,6 +1947,14 @@ static int vgic_its_save_itt(struct vgic_its *its, struct its_device *device)
1940 return 0; 1947 return 0;
1941} 1948}
1942 1949
1950/**
1951 * vgic_its_restore_itt - restore the ITT of a device
1952 *
1953 * @its: its handle
1954 * @dev: device handle
1955 *
1956 * Return 0 on success, < 0 on error
1957 */
1943static int vgic_its_restore_itt(struct vgic_its *its, struct its_device *dev) 1958static int vgic_its_restore_itt(struct vgic_its *its, struct its_device *dev)
1944{ 1959{
1945 const struct vgic_its_abi *abi = vgic_its_get_abi(its); 1960 const struct vgic_its_abi *abi = vgic_its_get_abi(its);
@@ -1951,6 +1966,10 @@ static int vgic_its_restore_itt(struct vgic_its *its, struct its_device *dev)
1951 ret = scan_its_table(its, base, max_size, ite_esz, 0, 1966 ret = scan_its_table(its, base, max_size, ite_esz, 0,
1952 vgic_its_restore_ite, dev); 1967 vgic_its_restore_ite, dev);
1953 1968
1969 /* scan_its_table returns +1 if all ITEs are invalid */
1970 if (ret > 0)
1971 ret = 0;
1972
1954 return ret; 1973 return ret;
1955} 1974}
1956 1975
@@ -2048,11 +2067,12 @@ static int vgic_its_device_cmp(void *priv, struct list_head *a,
2048static int vgic_its_save_device_tables(struct vgic_its *its) 2067static int vgic_its_save_device_tables(struct vgic_its *its)
2049{ 2068{
2050 const struct vgic_its_abi *abi = vgic_its_get_abi(its); 2069 const struct vgic_its_abi *abi = vgic_its_get_abi(its);
2070 u64 baser = its->baser_device_table;
2051 struct its_device *dev; 2071 struct its_device *dev;
2052 int dte_esz = abi->dte_esz; 2072 int dte_esz = abi->dte_esz;
2053 u64 baser;
2054 2073
2055 baser = its->baser_device_table; 2074 if (!(baser & GITS_BASER_VALID))
2075 return 0;
2056 2076
2057 list_sort(NULL, &its->device_list, vgic_its_device_cmp); 2077 list_sort(NULL, &its->device_list, vgic_its_device_cmp);
2058 2078
@@ -2107,10 +2127,7 @@ static int handle_l1_dte(struct vgic_its *its, u32 id, void *addr,
2107 ret = scan_its_table(its, gpa, SZ_64K, dte_esz, 2127 ret = scan_its_table(its, gpa, SZ_64K, dte_esz,
2108 l2_start_id, vgic_its_restore_dte, NULL); 2128 l2_start_id, vgic_its_restore_dte, NULL);
2109 2129
2110 if (ret <= 0) 2130 return ret;
2111 return ret;
2112
2113 return 1;
2114} 2131}
2115 2132
2116/** 2133/**
@@ -2140,8 +2157,9 @@ static int vgic_its_restore_device_tables(struct vgic_its *its)
2140 vgic_its_restore_dte, NULL); 2157 vgic_its_restore_dte, NULL);
2141 } 2158 }
2142 2159
2160 /* scan_its_table returns +1 if all entries are invalid */
2143 if (ret > 0) 2161 if (ret > 0)
2144 ret = -EINVAL; 2162 ret = 0;
2145 2163
2146 return ret; 2164 return ret;
2147} 2165}
@@ -2198,17 +2216,17 @@ static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz)
2198static int vgic_its_save_collection_table(struct vgic_its *its) 2216static int vgic_its_save_collection_table(struct vgic_its *its)
2199{ 2217{
2200 const struct vgic_its_abi *abi = vgic_its_get_abi(its); 2218 const struct vgic_its_abi *abi = vgic_its_get_abi(its);
2219 u64 baser = its->baser_coll_table;
2220 gpa_t gpa = BASER_ADDRESS(baser);
2201 struct its_collection *collection; 2221 struct its_collection *collection;
2202 u64 val; 2222 u64 val;
2203 gpa_t gpa;
2204 size_t max_size, filled = 0; 2223 size_t max_size, filled = 0;
2205 int ret, cte_esz = abi->cte_esz; 2224 int ret, cte_esz = abi->cte_esz;
2206 2225
2207 gpa = BASER_ADDRESS(its->baser_coll_table); 2226 if (!(baser & GITS_BASER_VALID))
2208 if (!gpa)
2209 return 0; 2227 return 0;
2210 2228
2211 max_size = GITS_BASER_NR_PAGES(its->baser_coll_table) * SZ_64K; 2229 max_size = GITS_BASER_NR_PAGES(baser) * SZ_64K;
2212 2230
2213 list_for_each_entry(collection, &its->collection_list, coll_list) { 2231 list_for_each_entry(collection, &its->collection_list, coll_list) {
2214 ret = vgic_its_save_cte(its, collection, gpa, cte_esz); 2232 ret = vgic_its_save_cte(its, collection, gpa, cte_esz);
@@ -2239,17 +2257,18 @@ static int vgic_its_save_collection_table(struct vgic_its *its)
2239static int vgic_its_restore_collection_table(struct vgic_its *its) 2257static int vgic_its_restore_collection_table(struct vgic_its *its)
2240{ 2258{
2241 const struct vgic_its_abi *abi = vgic_its_get_abi(its); 2259 const struct vgic_its_abi *abi = vgic_its_get_abi(its);
2260 u64 baser = its->baser_coll_table;
2242 int cte_esz = abi->cte_esz; 2261 int cte_esz = abi->cte_esz;
2243 size_t max_size, read = 0; 2262 size_t max_size, read = 0;
2244 gpa_t gpa; 2263 gpa_t gpa;
2245 int ret; 2264 int ret;
2246 2265
2247 if (!(its->baser_coll_table & GITS_BASER_VALID)) 2266 if (!(baser & GITS_BASER_VALID))
2248 return 0; 2267 return 0;
2249 2268
2250 gpa = BASER_ADDRESS(its->baser_coll_table); 2269 gpa = BASER_ADDRESS(baser);
2251 2270
2252 max_size = GITS_BASER_NR_PAGES(its->baser_coll_table) * SZ_64K; 2271 max_size = GITS_BASER_NR_PAGES(baser) * SZ_64K;
2253 2272
2254 while (read < max_size) { 2273 while (read < max_size) {
2255 ret = vgic_its_restore_cte(its, gpa, cte_esz); 2274 ret = vgic_its_restore_cte(its, gpa, cte_esz);
@@ -2258,6 +2277,10 @@ static int vgic_its_restore_collection_table(struct vgic_its *its)
2258 gpa += cte_esz; 2277 gpa += cte_esz;
2259 read += cte_esz; 2278 read += cte_esz;
2260 } 2279 }
2280
2281 if (ret > 0)
2282 return 0;
2283
2261 return ret; 2284 return ret;
2262} 2285}
2263 2286