From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DA74E1DFE3; Wed, 1 May 2024 15:48:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714578509; cv=none; b=Oywo+qavERANqZN41VPDPkrpLPTj2zpHReL6YDtWvYEuFsqC9OPyopXFSDW6jVD8pAzWEM7YmosYjhsy39inX/cWnpzykr51XF6HSMiMLkUdfCfS9QsxLV0xPAUVldjjOiTk4e4Qw4zxP5TbzFACqy3HNwvSvL7SkZe+JaqbiXg= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714578509; c=relaxed/simple; bh=ZZatJdKUDmf2CVXPMFjBWSOb+XJ/XSlCnJ9vhdW5Ets=; h=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From: In-Reply-To:Content-Type; b=V38+SRYs7WzuB4eJF2Da7Vw0li3d4QFA1MQDG9xibVkFEb5A57g2CGLg9d72B+coUVsky9gcJSiZddmJh3+BcotSNqr0mCpY0qAZE2qiiSjX6PewBJU3wES/4tlM3msBv/Ok5hSSARBfABusyGU9Uc+njZ9ywkQ0KUK3Y4AZb2M= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 375502F4; Wed, 1 May 2024 08:48:52 -0700 (PDT) Received: from [10.57.34.70] (unknown [10.57.34.70]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id EA4EE3F793; Wed, 1 May 2024 08:48:19 -0700 (PDT) Message-ID: <98ef5e64-2d1e-4fa2-a3da-210ea180f20f@arm.com> Date: Wed, 1 May 2024 16:47:50 +0100 Precedence: bulk X-Mailing-List: kvmarm@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v2 17/43] arm64: RME: Allow VMM to set RIPAS To: Suzuki K Poulose , kvm@vger.kernel.org, kvmarm@lists.linux.dev Cc: Catalin Marinas , Marc Zyngier , Will Deacon , James Morse , Oliver Upton , Zenghui Yu , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Joey Gouly , Alexandru Elisei , Christoffer Dall , Fuad Tabba , linux-coco@lists.linux.dev, Ganapatrao Kulkarni References: <20240412084056.1733704-1-steven.price@arm.com> <20240412084309.1733783-1-steven.price@arm.com> <20240412084309.1733783-18-steven.price@arm.com> From: Steven Price Content-Language: en-GB In-Reply-To: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On 19/04/2024 10:34, Suzuki K Poulose wrote: > On 12/04/2024 09:42, Steven Price wrote: >> Each page within the protected region of the realm guest can be marked >> as either RAM or EMPTY. Allow the VMM to control this before the guest >> has started and provide the equivalent functions to change this (with >> the guest's approval) at runtime. >> >> When transitioning from RIPAS RAM (1) to RIPAS EMPTY (0) the memory is >> unmapped from the guest and undelegated allowing the memory to be reused >> by the host. When transitioning to RIPAS RAM the actual population of >> the leaf RTTs is done later on stage 2 fault, however it may be >> necessary to allocate additional RTTs to represent the range requested. > > minor nit: To give a bit more context: > > "however it may be necessary to allocate additional RTTs in order for > the RMM to track the RIPAS for the requested range". That is what I meant - but your wording is probably clearer ;) Technically there's also the case where a RIPAS change will cause a block mapping to be split which isn't just about tracking RIPAS but also about breaking up the block mapping for the pages that remain. >> >> When freeing a block mapping it is necessary to temporarily unfold the >> RTT which requires delegating an extra page to the RMM, this page can >> then be recovered once the contents of the block mapping have been >> freed. A spare, delegated page (spare_page) is used for this purpose. >> >> Signed-off-by: Steven Price >> --- >>   arch/arm64/include/asm/kvm_rme.h |  16 ++ >>   arch/arm64/kvm/mmu.c             |   8 +- >>   arch/arm64/kvm/rme.c             | 390 +++++++++++++++++++++++++++++++ >>   3 files changed, 411 insertions(+), 3 deletions(-) >> >> diff --git a/arch/arm64/include/asm/kvm_rme.h >> b/arch/arm64/include/asm/kvm_rme.h >> index 915e76068b00..cc8f81cfc3c0 100644 >> --- a/arch/arm64/include/asm/kvm_rme.h >> +++ b/arch/arm64/include/asm/kvm_rme.h >> @@ -96,6 +96,14 @@ void kvm_realm_destroy_rtts(struct kvm *kvm, u32 >> ia_bits); >>   int kvm_create_rec(struct kvm_vcpu *vcpu); >>   void kvm_destroy_rec(struct kvm_vcpu *vcpu); >>   +void kvm_realm_unmap_range(struct kvm *kvm, >> +               unsigned long ipa, >> +               u64 size, >> +               bool unmap_private); >> +int realm_set_ipa_state(struct kvm_vcpu *vcpu, >> +            unsigned long addr, unsigned long end, >> +            unsigned long ripas); >> + >>   #define RME_RTT_BLOCK_LEVEL    2 >>   #define RME_RTT_MAX_LEVEL    3 >>   @@ -114,4 +122,12 @@ static inline unsigned long >> rme_rtt_level_mapsize(int level) >>       return (1UL << RME_RTT_LEVEL_SHIFT(level)); >>   } >>   +static inline bool realm_is_addr_protected(struct realm *realm, >> +                       unsigned long addr) >> +{ >> +    unsigned int ia_bits = realm->ia_bits; >> + >> +    return !(addr & ~(BIT(ia_bits - 1) - 1)); >> +} >> + >>   #endif >> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c >> index 46f0c4e80ace..8a7b5449697f 100644 >> --- a/arch/arm64/kvm/mmu.c >> +++ b/arch/arm64/kvm/mmu.c >> @@ -310,6 +310,7 @@ static void invalidate_icache_guest_page(void *va, >> size_t size) >>    * @start: The intermediate physical base address of the range to unmap >>    * @size:  The size of the area to unmap >>    * @may_block: Whether or not we are permitted to block >> + * @only_shared: If true then protected mappings should not be unmapped >>    * >>    * Clear a range of stage-2 mappings, lowering the various >> ref-counts.  Must >>    * be called while holding mmu_lock (unless for freeing the stage2 >> pgd before >> @@ -317,7 +318,7 @@ static void invalidate_icache_guest_page(void *va, >> size_t size) >>    * with things behind our backs. >>    */ >>   static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t >> start, u64 size, >> -                 bool may_block) >> +                 bool may_block, bool only_shared) >>   { >>       struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu); >>       phys_addr_t end = start + size; >> @@ -330,7 +331,7 @@ static void __unmap_stage2_range(struct kvm_s2_mmu >> *mmu, phys_addr_t start, u64 >>     static void unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t >> start, u64 size) >>   { >> -    __unmap_stage2_range(mmu, start, size, true); >> +    __unmap_stage2_range(mmu, start, size, true, false); >>   } >>     static void stage2_flush_memslot(struct kvm *kvm, >> @@ -1771,7 +1772,8 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct >> kvm_gfn_range *range) >>         __unmap_stage2_range(&kvm->arch.mmu, range->start << PAGE_SHIFT, >>                    (range->end - range->start) << PAGE_SHIFT, >> -                 range->may_block); >> +                 range->may_block, >> +                 range->only_shared); >>         return false; >>   } >> diff --git a/arch/arm64/kvm/rme.c b/arch/arm64/kvm/rme.c >> index 629a095bea61..9e5983c51393 100644 >> --- a/arch/arm64/kvm/rme.c >> +++ b/arch/arm64/kvm/rme.c >> @@ -79,6 +79,12 @@ static phys_addr_t __alloc_delegated_page(struct >> realm *realm, >>       return phys; >>   } >>   +static phys_addr_t alloc_delegated_page(struct realm *realm, >> +                    struct kvm_mmu_memory_cache *mc) >> +{ >> +    return __alloc_delegated_page(realm, mc, GFP_KERNEL); >> +} >> + >>   static void free_delegated_page(struct realm *realm, phys_addr_t phys) >>   { >>       if (realm->spare_page == PHYS_ADDR_MAX) { >> @@ -94,6 +100,151 @@ static void free_delegated_page(struct realm >> *realm, phys_addr_t phys) >>       free_page((unsigned long)phys_to_virt(phys)); >>   } >>   +static int realm_rtt_create(struct realm *realm, >> +                unsigned long addr, >> +                int level, >> +                phys_addr_t phys) >> +{ >> +    addr = ALIGN_DOWN(addr, rme_rtt_level_mapsize(level - 1)); >> +    return rmi_rtt_create(virt_to_phys(realm->rd), phys, addr, level); >> +} >> + >> +static int realm_rtt_fold(struct realm *realm, >> +              unsigned long addr, >> +              int level, >> +              phys_addr_t *rtt_granule) >> +{ >> +    unsigned long out_rtt; >> +    int ret; >> + >> +    ret = rmi_rtt_fold(virt_to_phys(realm->rd), addr, level, &out_rtt); >> + >> +    if (RMI_RETURN_STATUS(ret) == RMI_SUCCESS && rtt_granule) >> +        *rtt_granule = out_rtt; >> + >> +    return ret; >> +} >> + >> +static int realm_destroy_protected(struct realm *realm, >> +                   unsigned long ipa, >> +                   unsigned long *next_addr) >> +{ >> +    unsigned long rd = virt_to_phys(realm->rd); >> +    unsigned long addr; >> +    phys_addr_t rtt; >> +    int ret; >> + >> +loop: >> +    ret = rmi_data_destroy(rd, ipa, &addr, next_addr); >> +    if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) { >> +        if (*next_addr > ipa) >> +            return 0; /* UNASSIGNED */ >> +        rtt = alloc_delegated_page(realm, NULL); >> +        if (WARN_ON(rtt == PHYS_ADDR_MAX)) >> +            return -1; >> +        /* ASSIGNED - ipa is mapped as a block, so split */ >> +        ret = realm_rtt_create(realm, ipa, >> +                       RMI_RETURN_INDEX(ret) + 1, rtt); > > Could we not go all the way to L3 (rather than 1 level deeper) and try > again ? That way, we are covered for block mappings at L1 (1G). I don't think this situation can happen. The spec states that for RMI_ERROR_RTT either top>ipa (UNASSIGNED, in which case we've bailed out above) or top==ipa (ASSIGNED) in which case RMI_RETURN_INDEX(ret) must equal 2. So in this case realm_rtt_create() will always be called with level=3. I can simplify this by explicitly passing 3 and adding a WARN_ON() for RMI_RETURN_INDEX(ret) != 2. >> +        if (WARN_ON(ret)) { >> +            free_delegated_page(realm, rtt); >> +            return -1; >> +        } >> +        /* retry */ >> +        goto loop; >> +    } else if (WARN_ON(ret)) { >> +        return -1; >> +    } >> +    ret = rmi_granule_undelegate(addr); >> + >> +    /* >> +     * If the undelegate fails then something has gone seriously >> +     * wrong: take an extra reference to just leak the page >> +     */ >> +    if (WARN_ON(ret)) >> +        get_page(phys_to_page(addr)); >> + >> +    return 0; >> +} >> + >> +static void realm_unmap_range_shared(struct kvm *kvm, >> +                     int level, >> +                     unsigned long start, >> +                     unsigned long end) >> +{ >> +    struct realm *realm = &kvm->arch.realm; >> +    unsigned long rd = virt_to_phys(realm->rd); >> +    ssize_t map_size = rme_rtt_level_mapsize(level); >> +    unsigned long next_addr, addr; >> +    unsigned long shared_bit = BIT(realm->ia_bits - 1); >> + >> +    if (WARN_ON(level > RME_RTT_MAX_LEVEL)) >> +        return; >> + >> +    start |= shared_bit; >> +    end |= shared_bit; >> + >> +    for (addr = start; addr < end; addr = next_addr) { >> +        unsigned long align_addr = ALIGN(addr, map_size); >> +        int ret; >> + >> +        next_addr = ALIGN(addr + 1, map_size); >> + >> +        if (align_addr != addr || next_addr > end) { >> +            /* Need to recurse deeper */ >> +            if (addr < align_addr) >> +                next_addr = align_addr; >> +            realm_unmap_range_shared(kvm, level + 1, addr, >> +                         min(next_addr, end)); >> +            continue; >> +        } >> + >> +        ret = rmi_rtt_unmap_unprotected(rd, addr, level, &next_addr); > > minor nit: We could potentially use rmi_rtt_destroy() to tear down > shared mappings without unmapping them individually, if the range > is big enough. All such optimisations could come later though. Apparently so, I do feel the "liveness" concept in the RMM spec is very confusing. But it looks like an RTT in the unprotected IPA range isn't considered 'live' if it only contains ASSIGNED_NS entries. The wording here isn't great though: An RTT is live if, for any of its entries, either of the following is true: • The RTTE state is ASSIGNED • The RTTE state is TABLE. Note that an RTT can be non-live, even if one of its entries is live. This would be the case for example if the RTT corresponds to an Unprotected IPA range and the state of one of its entries is ASSIGNED_NS. Like you say this is an optimisation, so I think we should leave it until later for now. >> +        switch (RMI_RETURN_STATUS(ret)) { >> +        case RMI_SUCCESS: >> +            break; >> +        case RMI_ERROR_RTT: >> +            if (next_addr == addr) { > > At this point we have a block aligned address, but the mapping is > further deep. Given, start from top to down, we implicitly handle > the case of block mappings. Not sure if that needs to be in a comment > here. > >> +                next_addr = ALIGN(addr + 1, map_size); > > Reset to the "actual next" as it was overwritten by the RMI call. I'll add a comment explaining what's happening here. >> +                realm_unmap_range_shared(kvm, level + 1, addr, >> +                             next_addr); >> +            } >> +            break; >> +        default: >> +            WARN_ON(1); >> +        } >> +    } >> +} >> + >> +static void realm_unmap_range_private(struct kvm *kvm, >> +                      unsigned long start, >> +                      unsigned long end) >> +{ >> +    struct realm *realm = &kvm->arch.realm; >> +    ssize_t map_size = RME_PAGE_SIZE; >> +    unsigned long next_addr, addr; >> + >> +    for (addr = start; addr < end; addr = next_addr) { >> +        int ret; >> + >> +        next_addr = ALIGN(addr + 1, map_size); >> + >> +        ret = realm_destroy_protected(realm, addr, &next_addr); >> + >> +        if (WARN_ON(ret)) >> +            break; >> +    } >> +} >> + >> +static void realm_unmap_range(struct kvm *kvm, >> +                  unsigned long start, >> +                  unsigned long end, >> +                  bool unmap_private) >> +{ >> +    realm_unmap_range_shared(kvm, RME_RTT_MAX_LEVEL - 1, start, end); > > minor nit: We already have a helper to find a suitable start level > (defined below), may be we could use that ? And even do the rtt_destroy > optimisation for unprotected range. Good point, that should work. >> +    if (unmap_private) >> +        realm_unmap_range_private(kvm, start, end); >> +} >> + >>   u32 kvm_realm_ipa_limit(void) >>   { >>       return u64_get_bits(rmm_feat_reg0, RMI_FEATURE_REGISTER_0_S2SZ); >> @@ -190,6 +341,30 @@ static int realm_rtt_destroy(struct realm *realm, >> unsigned long addr, >>       return ret; >>   } >>   +static int realm_create_rtt_levels(struct realm *realm, >> +                   unsigned long ipa, >> +                   int level, >> +                   int max_level, >> +                   struct kvm_mmu_memory_cache *mc) >> +{ >> +    if (WARN_ON(level == max_level)) >> +        return 0; >> + >> +    while (level++ < max_level) { >> +        phys_addr_t rtt = alloc_delegated_page(realm, mc); >> + >> +        if (rtt == PHYS_ADDR_MAX) >> +            return -ENOMEM; >> + >> +        if (realm_rtt_create(realm, ipa, level, rtt)) { >> +            free_delegated_page(realm, rtt); >> +            return -ENXIO; >> +        } >> +    } >> + >> +    return 0; >> +} >> + >>   static int realm_tear_down_rtt_level(struct realm *realm, int level, >>                        unsigned long start, unsigned long end) >>   { >> @@ -265,6 +440,68 @@ static int realm_tear_down_rtt_range(struct realm >> *realm, >>                        start, end); >>   } >>   +/* >> + * Returns 0 on successful fold, a negative value on error, a >> positive value if >> + * we were not able to fold all tables at this level. >> + */ >> +static int realm_fold_rtt_level(struct realm *realm, int level, >> +                unsigned long start, unsigned long end) >> +{ >> +    int not_folded = 0; >> +    ssize_t map_size; >> +    unsigned long addr, next_addr; >> + >> +    if (WARN_ON(level > RME_RTT_MAX_LEVEL)) >> +        return -EINVAL; >> + >> +    map_size = rme_rtt_level_mapsize(level - 1); >> + >> +    for (addr = start; addr < end; addr = next_addr) { >> +        phys_addr_t rtt_granule; >> +        int ret; >> +        unsigned long align_addr = ALIGN(addr, map_size); >> + >> +        next_addr = ALIGN(addr + 1, map_size); >> + >> +        ret = realm_rtt_fold(realm, align_addr, level, &rtt_granule); >> + >> +        switch (RMI_RETURN_STATUS(ret)) { >> +        case RMI_SUCCESS: >> +            if (!WARN_ON(rmi_granule_undelegate(rtt_granule))) >> +                free_page((unsigned long)phys_to_virt(rtt_granule)); > > minor nit: Do we need a wrapper function for things like this, abd > leaking the page if undelegate fails, something like > rme_reclaim_delegated_page()  ? I'll take a look at that - mostly sites don't have the same argument repeated (unlike this one), so I'll have to have a think about the best form. >> +            break; >> +        case RMI_ERROR_RTT: >> +            if (level == RME_RTT_MAX_LEVEL || >> +                RMI_RETURN_INDEX(ret) < level) { >> +                not_folded++; >> +                break; >> +            } >> +            /* Recurse a level deeper */ >> +            ret = realm_fold_rtt_level(realm, >> +                           level + 1, >> +                           addr, >> +                           next_addr); >> +            if (ret < 0) >> +                return ret; >> +            else if (ret == 0) >> +                /* Try again at this level */ >> +                next_addr = addr; >> +            break; >> +        default: >> +            return -ENXIO; >> +        } >> +    } >> + >> +    return not_folded; >> +} >> + >> +static int realm_fold_rtt_range(struct realm *realm, >> +                unsigned long start, unsigned long end) >> +{ >> +    return realm_fold_rtt_level(realm, get_start_level(realm) + 1, >> +                    start, end); >> +} >> + >>   static void ensure_spare_page(struct realm *realm) >>   { >>       phys_addr_t tmp_rtt; >> @@ -295,6 +532,147 @@ void kvm_realm_destroy_rtts(struct kvm *kvm, u32 >> ia_bits) >>       WARN_ON(realm_tear_down_rtt_range(realm, 0, (1UL << ia_bits))); >>   } >>   +void kvm_realm_unmap_range(struct kvm *kvm, unsigned long ipa, u64 >> size, >> +               bool unmap_private) >> +{ >> +    unsigned long end = ipa + size; >> +    struct realm *realm = &kvm->arch.realm; >> + >> +    end = min(BIT(realm->ia_bits - 1), end); >> + >> +    ensure_spare_page(realm); >> + >> +    realm_unmap_range(kvm, ipa, end, unmap_private); >> + >> +    realm_fold_rtt_range(realm, ipa, end); > > Shouldn't this be : > >     if (unmap_private) >         realm_fold_rtt_range(realm, ipa, end); Indeed it's a little pointless folding if we haven't touched the private mappings. > Also it is fine to reclaim RTTs from the protected space, not the > unprotected half, as long as we use RTT_DESTROY in unmap_shared routine. > >> +} >> + >> +static int find_map_level(struct realm *realm, >> +              unsigned long start, >> +              unsigned long end) >> +{ >> +    int level = RME_RTT_MAX_LEVEL; >> + >> +    while (level > get_start_level(realm)) { >> +        unsigned long map_size = rme_rtt_level_mapsize(level - 1); >> + >> +        if (!IS_ALIGNED(start, map_size) || >> +            (start + map_size) > end) >> +            break; >> + >> +        level--; >> +    } >> + >> +    return level; >> +} >> + >> +int realm_set_ipa_state(struct kvm_vcpu *vcpu, >> +            unsigned long start, >> +            unsigned long end, >> +            unsigned long ripas) >> +{ >> +    struct kvm *kvm = vcpu->kvm; >> +    struct realm *realm = &kvm->arch.realm; >> +    struct realm_rec *rec = &vcpu->arch.rec; >> +    phys_addr_t rd_phys = virt_to_phys(realm->rd); >> +    phys_addr_t rec_phys = virt_to_phys(rec->rec_page); >> +    struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache; >> +    unsigned long ipa = start; >> +    int ret = 0; >> + >> +    while (ipa < end) { >> +        unsigned long next; >> + >> +        ret = rmi_rtt_set_ripas(rd_phys, rec_phys, ipa, end, &next); >> + >> +        if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) { >> +            int walk_level = RMI_RETURN_INDEX(ret); >> +            int level = find_map_level(realm, ipa, end); > > Might be worth adding a comment here. Check if the RMM needs tables to > create deeper level tables. Ok >> + >> +            if (walk_level < level) { >> +                ret = realm_create_rtt_levels(realm, ipa, >> +                                  walk_level, >> +                                  level, >> +                                  memcache); >                 /* Retry with RTTs created */ Ok >> +                if (!ret) >> +                    continue; >> +            } else { >> +                ret = -EINVAL; >> +            } >> + >> +            break; >> +        } else if (RMI_RETURN_STATUS(ret) != RMI_SUCCESS) { >> +            WARN(1, "Unexpected error in %s: %#x\n", __func__, >> +                 ret); >> +            ret = -EINVAL; >> +            break; >> +        } >> +        ipa = next; >> +    } >> + >> +    if (ripas == RMI_EMPTY && ipa != start) >> +        kvm_realm_unmap_range(kvm, start, ipa - start, true); > > This triggers unmapping the "shared" aliases too, which is not necessary. Yeah, the kvm_realm_unmap_range() function is a bit problematic - the various "unmap_private" and "only_shared" flags need a bit of a rethink. >> + >> +    return ret; >> +} >> + >> +static int realm_init_ipa_state(struct realm *realm, >> +                unsigned long ipa, >> +                unsigned long end) >> +{ >> +    phys_addr_t rd_phys = virt_to_phys(realm->rd); >> +    int ret; >> + >> +    while (ipa < end) { >> +        unsigned long next; >> + >> +        ret = rmi_rtt_init_ripas(rd_phys, ipa, end, &next); >> + >> +        if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) { >> +            int err_level = RMI_RETURN_INDEX(ret); >> +            int level = find_map_level(realm, ipa, end); >> + >> +            if (WARN_ON(err_level >= level)) >> +                return -ENXIO; >> + >> +            ret = realm_create_rtt_levels(realm, ipa, >> +                              err_level, >> +                              level, NULL); >> +            if (ret) >> +                return ret; >> +            /* Retry with the RTT levels in place */ >> +            continue; >> +        } else if (WARN_ON(ret)) { >> +            return -ENXIO; >> +        } >> + >> +        ipa = next; >> +    } >> + >> +    return 0; >> +} >> + >> +static int kvm_init_ipa_range_realm(struct kvm *kvm, >> +                    struct kvm_cap_arm_rme_init_ipa_args *args) >> +{ >> +    int ret = 0; >> +    gpa_t addr, end; >> +    struct realm *realm = &kvm->arch.realm; >> + >> +    addr = args->init_ipa_base; >> +    end = addr + args->init_ipa_size; >> + >> +    if (end < addr) >> +        return -EINVAL; >> + >> +    if (kvm_realm_state(kvm) != REALM_STATE_NEW) >> +        return -EINVAL; >> + >> +    ret = realm_init_ipa_state(realm, addr, end); >> + >> +    return ret; > > super minor nit: > >     return realm_init_ipa_state(realm, addr, end); That was probably left over from some printk() debugging ;) Thanks, Steve >> +} >> + >>   /* Protects access to rme_vmid_bitmap */ >>   static DEFINE_SPINLOCK(rme_vmid_lock); >>   static unsigned long *rme_vmid_bitmap; >> @@ -418,6 +796,18 @@ int kvm_realm_enable_cap(struct kvm *kvm, struct >> kvm_enable_cap *cap) >>       case KVM_CAP_ARM_RME_CREATE_RD: >>           r = kvm_create_realm(kvm); >>           break; >> +    case KVM_CAP_ARM_RME_INIT_IPA_REALM: { >> +        struct kvm_cap_arm_rme_init_ipa_args args; >> +        void __user *argp = u64_to_user_ptr(cap->args[1]); >> + >> +        if (copy_from_user(&args, argp, sizeof(args))) { >> +            r = -EFAULT; >> +            break; >> +        } >> + >> +        r = kvm_init_ipa_range_realm(kvm, &args); >> +        break; >> +    } > > > Suzuki From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 4B10DC4345F for ; Wed, 1 May 2024 15:48:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:In-Reply-To:From:References:Cc:To: Subject:MIME-Version:Date:Message-ID:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=6HHsnMAHxR1IBf/zpNJtOwRly/u2s6GzYDc7y4/+tfw=; b=F2V0U2zXDviH0G 6pe1gCqZmQUZCoDJN/7FCzzpewyn4knA8tuFaddYQiuB6cKI3kvGbL6IAw+DMg93OY9dgoSAipvUY CBwnslG7z4RPh15+KKoHc5VeC2nKBlmDVYgI27TCBi4nv+sWcT2k0bwuEW11Xgh5WDtb2mfxn4535 Ng4TXmxbaaWNQkByF/ud6KW8vv3TOnNbn3XsZ2jBLOKbdqpKitJIMoK/5aswh02pI/Ij/wQ3HDEUg GCugjWjyvnVzGAnqRUdK6ezXt1oObO91OOBEy1rCJM/NiT8AZu7xv5PO6+i63r2ouWCS6lO7HOQ+y S1sS/Wk9t0GTne/HfxdQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1s2CC9-0000000A2ix-3aFm; Wed, 01 May 2024 15:48:34 +0000 Received: from foss.arm.com ([217.140.110.172]) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1s2CC5-0000000A2iO-3ZV9 for linux-arm-kernel@lists.infradead.org; Wed, 01 May 2024 15:48:32 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 375502F4; Wed, 1 May 2024 08:48:52 -0700 (PDT) Received: from [10.57.34.70] (unknown [10.57.34.70]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id EA4EE3F793; Wed, 1 May 2024 08:48:19 -0700 (PDT) Message-ID: <98ef5e64-2d1e-4fa2-a3da-210ea180f20f@arm.com> Date: Wed, 1 May 2024 16:47:50 +0100 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v2 17/43] arm64: RME: Allow VMM to set RIPAS To: Suzuki K Poulose , kvm@vger.kernel.org, kvmarm@lists.linux.dev Cc: Catalin Marinas , Marc Zyngier , Will Deacon , James Morse , Oliver Upton , Zenghui Yu , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Joey Gouly , Alexandru Elisei , Christoffer Dall , Fuad Tabba , linux-coco@lists.linux.dev, Ganapatrao Kulkarni References: <20240412084056.1733704-1-steven.price@arm.com> <20240412084309.1733783-1-steven.price@arm.com> <20240412084309.1733783-18-steven.price@arm.com> From: Steven Price Content-Language: en-GB In-Reply-To: X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240501_084830_025127_C728AE44 X-CRM114-Status: GOOD ( 39.07 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org T24gMTkvMDQvMjAyNCAxMDozNCwgU3V6dWtpIEsgUG91bG9zZSB3cm90ZToKPiBPbiAxMi8wNC8y MDI0IDA5OjQyLCBTdGV2ZW4gUHJpY2Ugd3JvdGU6Cj4+IEVhY2ggcGFnZSB3aXRoaW4gdGhlIHBy b3RlY3RlZCByZWdpb24gb2YgdGhlIHJlYWxtIGd1ZXN0IGNhbiBiZSBtYXJrZWQKPj4gYXMgZWl0 aGVyIFJBTSBvciBFTVBUWS4gQWxsb3cgdGhlIFZNTSB0byBjb250cm9sIHRoaXMgYmVmb3JlIHRo ZSBndWVzdAo+PiBoYXMgc3RhcnRlZCBhbmQgcHJvdmlkZSB0aGUgZXF1aXZhbGVudCBmdW5jdGlv bnMgdG8gY2hhbmdlIHRoaXMgKHdpdGgKPj4gdGhlIGd1ZXN0J3MgYXBwcm92YWwpIGF0IHJ1bnRp bWUuCj4+Cj4+IFdoZW4gdHJhbnNpdGlvbmluZyBmcm9tIFJJUEFTIFJBTSAoMSkgdG8gUklQQVMg RU1QVFkgKDApIHRoZSBtZW1vcnkgaXMKPj4gdW5tYXBwZWQgZnJvbSB0aGUgZ3Vlc3QgYW5kIHVu ZGVsZWdhdGVkIGFsbG93aW5nIHRoZSBtZW1vcnkgdG8gYmUgcmV1c2VkCj4+IGJ5IHRoZSBob3N0 LiBXaGVuIHRyYW5zaXRpb25pbmcgdG8gUklQQVMgUkFNIHRoZSBhY3R1YWwgcG9wdWxhdGlvbiBv Zgo+PiB0aGUgbGVhZiBSVFRzIGlzIGRvbmUgbGF0ZXIgb24gc3RhZ2UgMiBmYXVsdCwgaG93ZXZl ciBpdCBtYXkgYmUKPj4gbmVjZXNzYXJ5IHRvIGFsbG9jYXRlIGFkZGl0aW9uYWwgUlRUcyB0byBy ZXByZXNlbnQgdGhlIHJhbmdlIHJlcXVlc3RlZC4KPiAKPiBtaW5vciBuaXQ6IFRvIGdpdmUgYSBi aXQgbW9yZSBjb250ZXh0Ogo+IAo+ICJob3dldmVyIGl0IG1heSBiZSBuZWNlc3NhcnkgdG8gYWxs b2NhdGUgYWRkaXRpb25hbCBSVFRzIGluIG9yZGVyIGZvcgo+IHRoZSBSTU0gdG8gdHJhY2sgdGhl IFJJUEFTIGZvciB0aGUgcmVxdWVzdGVkIHJhbmdlIi4KClRoYXQgaXMgd2hhdCBJIG1lYW50IC0g YnV0IHlvdXIgd29yZGluZyBpcyBwcm9iYWJseSBjbGVhcmVyIDspClRlY2huaWNhbGx5IHRoZXJl J3MgYWxzbyB0aGUgY2FzZSB3aGVyZSBhIFJJUEFTIGNoYW5nZSB3aWxsIGNhdXNlIGEKYmxvY2sg bWFwcGluZyB0byBiZSBzcGxpdCB3aGljaCBpc24ndCBqdXN0IGFib3V0IHRyYWNraW5nIFJJUEFT IGJ1dCBhbHNvCmFib3V0IGJyZWFraW5nIHVwIHRoZSBibG9jayBtYXBwaW5nIGZvciB0aGUgcGFn ZXMgdGhhdCByZW1haW4uCgo+Pgo+PiBXaGVuIGZyZWVpbmcgYSBibG9jayBtYXBwaW5nIGl0IGlz IG5lY2Vzc2FyeSB0byB0ZW1wb3JhcmlseSB1bmZvbGQgdGhlCj4+IFJUVCB3aGljaCByZXF1aXJl cyBkZWxlZ2F0aW5nIGFuIGV4dHJhIHBhZ2UgdG8gdGhlIFJNTSwgdGhpcyBwYWdlIGNhbgo+PiB0 aGVuIGJlIHJlY292ZXJlZCBvbmNlIHRoZSBjb250ZW50cyBvZiB0aGUgYmxvY2sgbWFwcGluZyBo YXZlIGJlZW4KPj4gZnJlZWQuIEEgc3BhcmUsIGRlbGVnYXRlZCBwYWdlIChzcGFyZV9wYWdlKSBp cyB1c2VkIGZvciB0aGlzIHB1cnBvc2UuCj4+Cj4+IFNpZ25lZC1vZmYtYnk6IFN0ZXZlbiBQcmlj ZSA8c3RldmVuLnByaWNlQGFybS5jb20+Cj4+IC0tLQo+PiDCoCBhcmNoL2FybTY0L2luY2x1ZGUv YXNtL2t2bV9ybWUuaCB8wqAgMTYgKysKPj4gwqAgYXJjaC9hcm02NC9rdm0vbW11LmPCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqAgfMKgwqAgOCArLQo+PiDCoCBhcmNoL2FybTY0L2t2bS9ybWUuY8Kg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCB8IDM5MCArKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrCj4+IMKgIDMgZmlsZXMgY2hhbmdlZCwgNDExIGluc2VydGlvbnMoKyksIDMgZGVsZXRpb25z KC0pCj4+Cj4+IGRpZmYgLS1naXQgYS9hcmNoL2FybTY0L2luY2x1ZGUvYXNtL2t2bV9ybWUuaAo+ PiBiL2FyY2gvYXJtNjQvaW5jbHVkZS9hc20va3ZtX3JtZS5oCj4+IGluZGV4IDkxNWU3NjA2OGIw MC4uY2M4ZjgxY2ZjM2MwIDEwMDY0NAo+PiAtLS0gYS9hcmNoL2FybTY0L2luY2x1ZGUvYXNtL2t2 bV9ybWUuaAo+PiArKysgYi9hcmNoL2FybTY0L2luY2x1ZGUvYXNtL2t2bV9ybWUuaAo+PiBAQCAt OTYsNiArOTYsMTQgQEAgdm9pZCBrdm1fcmVhbG1fZGVzdHJveV9ydHRzKHN0cnVjdCBrdm0gKmt2 bSwgdTMyCj4+IGlhX2JpdHMpOwo+PiDCoCBpbnQga3ZtX2NyZWF0ZV9yZWMoc3RydWN0IGt2bV92 Y3B1ICp2Y3B1KTsKPj4gwqAgdm9pZCBrdm1fZGVzdHJveV9yZWMoc3RydWN0IGt2bV92Y3B1ICp2 Y3B1KTsKPj4gwqAgK3ZvaWQga3ZtX3JlYWxtX3VubWFwX3JhbmdlKHN0cnVjdCBrdm0gKmt2bSwK Pj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgdW5zaWduZWQgbG9uZyBpcGEsCj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHU2NCBzaXplLAo+PiArwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoCBib29sIHVubWFwX3ByaXZhdGUpOwo+PiAraW50IHJlYWxtX3NldF9pcGFf c3RhdGUoc3RydWN0IGt2bV92Y3B1ICp2Y3B1LAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCB1 bnNpZ25lZCBsb25nIGFkZHIsIHVuc2lnbmVkIGxvbmcgZW5kLAo+PiArwqDCoMKgwqDCoMKgwqDC oMKgwqDCoCB1bnNpZ25lZCBsb25nIHJpcGFzKTsKPj4gKwo+PiDCoCAjZGVmaW5lIFJNRV9SVFRf QkxPQ0tfTEVWRUzCoMKgwqAgMgo+PiDCoCAjZGVmaW5lIFJNRV9SVFRfTUFYX0xFVkVMwqDCoMKg IDMKPj4gwqAgQEAgLTExNCw0ICsxMjIsMTIgQEAgc3RhdGljIGlubGluZSB1bnNpZ25lZCBsb25n Cj4+IHJtZV9ydHRfbGV2ZWxfbWFwc2l6ZShpbnQgbGV2ZWwpCj4+IMKgwqDCoMKgwqAgcmV0dXJu ICgxVUwgPDwgUk1FX1JUVF9MRVZFTF9TSElGVChsZXZlbCkpOwo+PiDCoCB9Cj4+IMKgICtzdGF0 aWMgaW5saW5lIGJvb2wgcmVhbG1faXNfYWRkcl9wcm90ZWN0ZWQoc3RydWN0IHJlYWxtICpyZWFs bSwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHVuc2ln bmVkIGxvbmcgYWRkcikKPj4gK3sKPj4gK8KgwqDCoCB1bnNpZ25lZCBpbnQgaWFfYml0cyA9IHJl YWxtLT5pYV9iaXRzOwo+PiArCj4+ICvCoMKgwqAgcmV0dXJuICEoYWRkciAmIH4oQklUKGlhX2Jp dHMgLSAxKSAtIDEpKTsKPj4gK30KPj4gKwo+PiDCoCAjZW5kaWYKPj4gZGlmZiAtLWdpdCBhL2Fy Y2gvYXJtNjQva3ZtL21tdS5jIGIvYXJjaC9hcm02NC9rdm0vbW11LmMKPj4gaW5kZXggNDZmMGM0 ZTgwYWNlLi44YTdiNTQ0OTY5N2YgMTAwNjQ0Cj4+IC0tLSBhL2FyY2gvYXJtNjQva3ZtL21tdS5j Cj4+ICsrKyBiL2FyY2gvYXJtNjQva3ZtL21tdS5jCj4+IEBAIC0zMTAsNiArMzEwLDcgQEAgc3Rh dGljIHZvaWQgaW52YWxpZGF0ZV9pY2FjaGVfZ3Vlc3RfcGFnZSh2b2lkICp2YSwKPj4gc2l6ZV90 IHNpemUpCj4+IMKgwqAgKiBAc3RhcnQ6IFRoZSBpbnRlcm1lZGlhdGUgcGh5c2ljYWwgYmFzZSBh ZGRyZXNzIG9mIHRoZSByYW5nZSB0byB1bm1hcAo+PiDCoMKgICogQHNpemU6wqAgVGhlIHNpemUg b2YgdGhlIGFyZWEgdG8gdW5tYXAKPj4gwqDCoCAqIEBtYXlfYmxvY2s6IFdoZXRoZXIgb3Igbm90 IHdlIGFyZSBwZXJtaXR0ZWQgdG8gYmxvY2sKPj4gKyAqIEBvbmx5X3NoYXJlZDogSWYgdHJ1ZSB0 aGVuIHByb3RlY3RlZCBtYXBwaW5ncyBzaG91bGQgbm90IGJlIHVubWFwcGVkCj4+IMKgwqAgKgo+ PiDCoMKgICogQ2xlYXIgYSByYW5nZSBvZiBzdGFnZS0yIG1hcHBpbmdzLCBsb3dlcmluZyB0aGUg dmFyaW91cwo+PiByZWYtY291bnRzLsKgIE11c3QKPj4gwqDCoCAqIGJlIGNhbGxlZCB3aGlsZSBo b2xkaW5nIG1tdV9sb2NrICh1bmxlc3MgZm9yIGZyZWVpbmcgdGhlIHN0YWdlMgo+PiBwZ2QgYmVm b3JlCj4+IEBAIC0zMTcsNyArMzE4LDcgQEAgc3RhdGljIHZvaWQgaW52YWxpZGF0ZV9pY2FjaGVf Z3Vlc3RfcGFnZSh2b2lkICp2YSwKPj4gc2l6ZV90IHNpemUpCj4+IMKgwqAgKiB3aXRoIHRoaW5n cyBiZWhpbmQgb3VyIGJhY2tzLgo+PiDCoMKgICovCj4+IMKgIHN0YXRpYyB2b2lkIF9fdW5tYXBf c3RhZ2UyX3JhbmdlKHN0cnVjdCBrdm1fczJfbW11ICptbXUsIHBoeXNfYWRkcl90Cj4+IHN0YXJ0 LCB1NjQgc2l6ZSwKPj4gLcKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGJvb2wgbWF5 X2Jsb2NrKQo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgYm9vbCBtYXlfYmxv Y2ssIGJvb2wgb25seV9zaGFyZWQpCj4+IMKgIHsKPj4gwqDCoMKgwqDCoCBzdHJ1Y3Qga3ZtICpr dm0gPSBrdm1fczJfbW11X3RvX2t2bShtbXUpOwo+PiDCoMKgwqDCoMKgIHBoeXNfYWRkcl90IGVu ZCA9IHN0YXJ0ICsgc2l6ZTsKPj4gQEAgLTMzMCw3ICszMzEsNyBAQCBzdGF0aWMgdm9pZCBfX3Vu bWFwX3N0YWdlMl9yYW5nZShzdHJ1Y3Qga3ZtX3MyX21tdQo+PiAqbW11LCBwaHlzX2FkZHJfdCBz dGFydCwgdTY0Cj4+IMKgIMKgIHN0YXRpYyB2b2lkIHVubWFwX3N0YWdlMl9yYW5nZShzdHJ1Y3Qg a3ZtX3MyX21tdSAqbW11LCBwaHlzX2FkZHJfdAo+PiBzdGFydCwgdTY0IHNpemUpCj4+IMKgIHsK Pj4gLcKgwqDCoCBfX3VubWFwX3N0YWdlMl9yYW5nZShtbXUsIHN0YXJ0LCBzaXplLCB0cnVlKTsK Pj4gK8KgwqDCoCBfX3VubWFwX3N0YWdlMl9yYW5nZShtbXUsIHN0YXJ0LCBzaXplLCB0cnVlLCBm YWxzZSk7Cj4+IMKgIH0KPj4gwqAgwqAgc3RhdGljIHZvaWQgc3RhZ2UyX2ZsdXNoX21lbXNsb3Qo c3RydWN0IGt2bSAqa3ZtLAo+PiBAQCAtMTc3MSw3ICsxNzcyLDggQEAgYm9vbCBrdm1fdW5tYXBf Z2ZuX3JhbmdlKHN0cnVjdCBrdm0gKmt2bSwgc3RydWN0Cj4+IGt2bV9nZm5fcmFuZ2UgKnJhbmdl KQo+PiDCoCDCoMKgwqDCoMKgIF9fdW5tYXBfc3RhZ2UyX3JhbmdlKCZrdm0tPmFyY2gubW11LCBy YW5nZS0+c3RhcnQgPDwgUEFHRV9TSElGVCwKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgIChyYW5nZS0+ZW5kIC0gcmFuZ2UtPnN0YXJ0KSA8PCBQQUdFX1NISUZULAo+PiAt wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgcmFuZ2UtPm1heV9ibG9jayk7Cj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByYW5nZS0+bWF5X2Jsb2NrLAo+PiArwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgcmFuZ2UtPm9ubHlfc2hhcmVkKTsKPj4gwqAg wqDCoMKgwqDCoCByZXR1cm4gZmFsc2U7Cj4+IMKgIH0KPj4gZGlmZiAtLWdpdCBhL2FyY2gvYXJt NjQva3ZtL3JtZS5jIGIvYXJjaC9hcm02NC9rdm0vcm1lLmMKPj4gaW5kZXggNjI5YTA5NWJlYTYx Li45ZTU5ODNjNTEzOTMgMTAwNjQ0Cj4+IC0tLSBhL2FyY2gvYXJtNjQva3ZtL3JtZS5jCj4+ICsr KyBiL2FyY2gvYXJtNjQva3ZtL3JtZS5jCj4+IEBAIC03OSw2ICs3OSwxMiBAQCBzdGF0aWMgcGh5 c19hZGRyX3QgX19hbGxvY19kZWxlZ2F0ZWRfcGFnZShzdHJ1Y3QKPj4gcmVhbG0gKnJlYWxtLAo+ PiDCoMKgwqDCoMKgIHJldHVybiBwaHlzOwo+PiDCoCB9Cj4+IMKgICtzdGF0aWMgcGh5c19hZGRy X3QgYWxsb2NfZGVsZWdhdGVkX3BhZ2Uoc3RydWN0IHJlYWxtICpyZWFsbSwKPj4gK8KgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHN0cnVjdCBrdm1fbW11X21lbW9yeV9jYWNo ZSAqbWMpCj4+ICt7Cj4+ICvCoMKgwqAgcmV0dXJuIF9fYWxsb2NfZGVsZWdhdGVkX3BhZ2UocmVh bG0sIG1jLCBHRlBfS0VSTkVMKTsKPj4gK30KPj4gKwo+PiDCoCBzdGF0aWMgdm9pZCBmcmVlX2Rl bGVnYXRlZF9wYWdlKHN0cnVjdCByZWFsbSAqcmVhbG0sIHBoeXNfYWRkcl90IHBoeXMpCj4+IMKg IHsKPj4gwqDCoMKgwqDCoCBpZiAocmVhbG0tPnNwYXJlX3BhZ2UgPT0gUEhZU19BRERSX01BWCkg ewo+PiBAQCAtOTQsNiArMTAwLDE1MSBAQCBzdGF0aWMgdm9pZCBmcmVlX2RlbGVnYXRlZF9wYWdl KHN0cnVjdCByZWFsbQo+PiAqcmVhbG0sIHBoeXNfYWRkcl90IHBoeXMpCj4+IMKgwqDCoMKgwqAg ZnJlZV9wYWdlKCh1bnNpZ25lZCBsb25nKXBoeXNfdG9fdmlydChwaHlzKSk7Cj4+IMKgIH0KPj4g wqAgK3N0YXRpYyBpbnQgcmVhbG1fcnR0X2NyZWF0ZShzdHJ1Y3QgcmVhbG0gKnJlYWxtLAo+PiAr wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHVuc2lnbmVkIGxvbmcgYWRkciwKPj4gK8Kg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBpbnQgbGV2ZWwsCj4+ICvCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqAgcGh5c19hZGRyX3QgcGh5cykKPj4gK3sKPj4gK8KgwqDCoCBhZGRy ID0gQUxJR05fRE9XTihhZGRyLCBybWVfcnR0X2xldmVsX21hcHNpemUobGV2ZWwgLSAxKSk7Cj4+ ICvCoMKgwqAgcmV0dXJuIHJtaV9ydHRfY3JlYXRlKHZpcnRfdG9fcGh5cyhyZWFsbS0+cmQpLCBw aHlzLCBhZGRyLCBsZXZlbCk7Cj4+ICt9Cj4+ICsKPj4gK3N0YXRpYyBpbnQgcmVhbG1fcnR0X2Zv bGQoc3RydWN0IHJlYWxtICpyZWFsbSwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHVu c2lnbmVkIGxvbmcgYWRkciwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGludCBsZXZl bCwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHBoeXNfYWRkcl90ICpydHRfZ3JhbnVs ZSkKPj4gK3sKPj4gK8KgwqDCoCB1bnNpZ25lZCBsb25nIG91dF9ydHQ7Cj4+ICvCoMKgwqAgaW50 IHJldDsKPj4gKwo+PiArwqDCoMKgIHJldCA9IHJtaV9ydHRfZm9sZCh2aXJ0X3RvX3BoeXMocmVh bG0tPnJkKSwgYWRkciwgbGV2ZWwsICZvdXRfcnR0KTsKPj4gKwo+PiArwqDCoMKgIGlmIChSTUlf UkVUVVJOX1NUQVRVUyhyZXQpID09IFJNSV9TVUNDRVNTICYmIHJ0dF9ncmFudWxlKQo+PiArwqDC oMKgwqDCoMKgwqAgKnJ0dF9ncmFudWxlID0gb3V0X3J0dDsKPj4gKwo+PiArwqDCoMKgIHJldHVy biByZXQ7Cj4+ICt9Cj4+ICsKPj4gK3N0YXRpYyBpbnQgcmVhbG1fZGVzdHJveV9wcm90ZWN0ZWQo c3RydWN0IHJlYWxtICpyZWFsbSwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoCB1bnNpZ25lZCBsb25nIGlwYSwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoCB1bnNpZ25lZCBsb25nICpuZXh0X2FkZHIpCj4+ICt7Cj4+ICvCoMKgwqAgdW5zaWdu ZWQgbG9uZyByZCA9IHZpcnRfdG9fcGh5cyhyZWFsbS0+cmQpOwo+PiArwqDCoMKgIHVuc2lnbmVk IGxvbmcgYWRkcjsKPj4gK8KgwqDCoCBwaHlzX2FkZHJfdCBydHQ7Cj4+ICvCoMKgwqAgaW50IHJl dDsKPj4gKwo+PiArbG9vcDoKPj4gK8KgwqDCoCByZXQgPSBybWlfZGF0YV9kZXN0cm95KHJkLCBp cGEsICZhZGRyLCBuZXh0X2FkZHIpOwo+PiArwqDCoMKgIGlmIChSTUlfUkVUVVJOX1NUQVRVUyhy ZXQpID09IFJNSV9FUlJPUl9SVFQpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIGlmICgqbmV4dF9hZGRy ID4gaXBhKQo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXR1cm4gMDsgLyogVU5BU1NJR05F RCAqLwo+PiArwqDCoMKgwqDCoMKgwqAgcnR0ID0gYWxsb2NfZGVsZWdhdGVkX3BhZ2UocmVhbG0s IE5VTEwpOwo+PiArwqDCoMKgwqDCoMKgwqAgaWYgKFdBUk5fT04ocnR0ID09IFBIWVNfQUREUl9N QVgpKQo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXR1cm4gLTE7Cj4+ICvCoMKgwqDCoMKg wqDCoCAvKiBBU1NJR05FRCAtIGlwYSBpcyBtYXBwZWQgYXMgYSBibG9jaywgc28gc3BsaXQgKi8K Pj4gK8KgwqDCoMKgwqDCoMKgIHJldCA9IHJlYWxtX3J0dF9jcmVhdGUocmVhbG0sIGlwYSwKPj4g K8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIFJNSV9SRVRVUk5f SU5ERVgocmV0KSArIDEsIHJ0dCk7Cj4gCj4gQ291bGQgd2Ugbm90IGdvIGFsbCB0aGUgd2F5IHRv IEwzIChyYXRoZXIgdGhhbiAxIGxldmVsIGRlZXBlcikgYW5kIHRyeQo+IGFnYWluID8gVGhhdCB3 YXksIHdlIGFyZSBjb3ZlcmVkIGZvciBibG9jayBtYXBwaW5ncyBhdCBMMSAoMUcpLgoKSSBkb24n dCB0aGluayB0aGlzIHNpdHVhdGlvbiBjYW4gaGFwcGVuLiBUaGUgc3BlYyBzdGF0ZXMgdGhhdCBm b3IKUk1JX0VSUk9SX1JUVCBlaXRoZXIgdG9wPmlwYSAoVU5BU1NJR05FRCwgaW4gd2hpY2ggY2Fz ZSB3ZSd2ZSBiYWlsZWQgb3V0CmFib3ZlKSBvciB0b3A9PWlwYSAoQVNTSUdORUQpIGluIHdoaWNo IGNhc2UgUk1JX1JFVFVSTl9JTkRFWChyZXQpIG11c3QKZXF1YWwgMi4KClNvIGluIHRoaXMgY2Fz ZSByZWFsbV9ydHRfY3JlYXRlKCkgd2lsbCBhbHdheXMgYmUgY2FsbGVkIHdpdGggbGV2ZWw9My4K CkkgY2FuIHNpbXBsaWZ5IHRoaXMgYnkgZXhwbGljaXRseSBwYXNzaW5nIDMgYW5kIGFkZGluZyBh IFdBUk5fT04oKSBmb3IKUk1JX1JFVFVSTl9JTkRFWChyZXQpICE9IDIuCgo+PiArwqDCoMKgwqDC oMKgwqAgaWYgKFdBUk5fT04ocmV0KSkgewo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBmcmVl X2RlbGVnYXRlZF9wYWdlKHJlYWxtLCBydHQpOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBy ZXR1cm4gLTE7Cj4+ICvCoMKgwqDCoMKgwqDCoCB9Cj4+ICvCoMKgwqDCoMKgwqDCoCAvKiByZXRy eSAqLwo+PiArwqDCoMKgwqDCoMKgwqAgZ290byBsb29wOwo+PiArwqDCoMKgIH0gZWxzZSBpZiAo V0FSTl9PTihyZXQpKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXR1cm4gLTE7Cj4+ICvCoMKgwqAg fQo+PiArwqDCoMKgIHJldCA9IHJtaV9ncmFudWxlX3VuZGVsZWdhdGUoYWRkcik7Cj4+ICsKPj4g K8KgwqDCoCAvKgo+PiArwqDCoMKgwqAgKiBJZiB0aGUgdW5kZWxlZ2F0ZSBmYWlscyB0aGVuIHNv bWV0aGluZyBoYXMgZ29uZSBzZXJpb3VzbHkKPj4gK8KgwqDCoMKgICogd3Jvbmc6IHRha2UgYW4g ZXh0cmEgcmVmZXJlbmNlIHRvIGp1c3QgbGVhayB0aGUgcGFnZQo+PiArwqDCoMKgwqAgKi8KPj4g K8KgwqDCoCBpZiAoV0FSTl9PTihyZXQpKQo+PiArwqDCoMKgwqDCoMKgwqAgZ2V0X3BhZ2UocGh5 c190b19wYWdlKGFkZHIpKTsKPj4gKwo+PiArwqDCoMKgIHJldHVybiAwOwo+PiArfQo+PiArCj4+ ICtzdGF0aWMgdm9pZCByZWFsbV91bm1hcF9yYW5nZV9zaGFyZWQoc3RydWN0IGt2bSAqa3ZtLAo+ PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBpbnQgbGV2ZWwsCj4+ ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHVuc2lnbmVkIGxvbmcg c3RhcnQsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHVuc2ln bmVkIGxvbmcgZW5kKQo+PiArewo+PiArwqDCoMKgIHN0cnVjdCByZWFsbSAqcmVhbG0gPSAma3Zt LT5hcmNoLnJlYWxtOwo+PiArwqDCoMKgIHVuc2lnbmVkIGxvbmcgcmQgPSB2aXJ0X3RvX3BoeXMo cmVhbG0tPnJkKTsKPj4gK8KgwqDCoCBzc2l6ZV90IG1hcF9zaXplID0gcm1lX3J0dF9sZXZlbF9t YXBzaXplKGxldmVsKTsKPj4gK8KgwqDCoCB1bnNpZ25lZCBsb25nIG5leHRfYWRkciwgYWRkcjsK Pj4gK8KgwqDCoCB1bnNpZ25lZCBsb25nIHNoYXJlZF9iaXQgPSBCSVQocmVhbG0tPmlhX2JpdHMg LSAxKTsKPj4gKwo+PiArwqDCoMKgIGlmIChXQVJOX09OKGxldmVsID4gUk1FX1JUVF9NQVhfTEVW RUwpKQo+PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuOwo+PiArCj4+ICvCoMKgwqAgc3RhcnQgfD0g c2hhcmVkX2JpdDsKPj4gK8KgwqDCoCBlbmQgfD0gc2hhcmVkX2JpdDsKPj4gKwo+PiArwqDCoMKg IGZvciAoYWRkciA9IHN0YXJ0OyBhZGRyIDwgZW5kOyBhZGRyID0gbmV4dF9hZGRyKSB7Cj4+ICvC oMKgwqDCoMKgwqDCoCB1bnNpZ25lZCBsb25nIGFsaWduX2FkZHIgPSBBTElHTihhZGRyLCBtYXBf c2l6ZSk7Cj4+ICvCoMKgwqDCoMKgwqDCoCBpbnQgcmV0Owo+PiArCj4+ICvCoMKgwqDCoMKgwqDC oCBuZXh0X2FkZHIgPSBBTElHTihhZGRyICsgMSwgbWFwX3NpemUpOwo+PiArCj4+ICvCoMKgwqDC oMKgwqDCoCBpZiAoYWxpZ25fYWRkciAhPSBhZGRyIHx8IG5leHRfYWRkciA+IGVuZCkgewo+PiAr wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAvKiBOZWVkIHRvIHJlY3Vyc2UgZGVlcGVyICovCj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgIGlmIChhZGRyIDwgYWxpZ25fYWRkcikKPj4gK8KgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBuZXh0X2FkZHIgPSBhbGlnbl9hZGRyOwo+PiArwqDCoMKg wqDCoMKgwqDCoMKgwqDCoCByZWFsbV91bm1hcF9yYW5nZV9zaGFyZWQoa3ZtLCBsZXZlbCArIDEs IGFkZHIsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqAgbWluKG5leHRfYWRkciwgZW5kKSk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGNvbnRp bnVlOwo+PiArwqDCoMKgwqDCoMKgwqAgfQo+PiArCj4+ICvCoMKgwqDCoMKgwqDCoCByZXQgPSBy bWlfcnR0X3VubWFwX3VucHJvdGVjdGVkKHJkLCBhZGRyLCBsZXZlbCwgJm5leHRfYWRkcik7Cj4g Cj4gbWlub3Igbml0OiBXZSBjb3VsZCBwb3RlbnRpYWxseSB1c2Ugcm1pX3J0dF9kZXN0cm95KCkg dG8gdGVhciBkb3duCj4gc2hhcmVkIG1hcHBpbmdzIHdpdGhvdXQgdW5tYXBwaW5nIHRoZW0gaW5k aXZpZHVhbGx5LCBpZiB0aGUgcmFuZ2UKPiBpcyBiaWcgZW5vdWdoLiBBbGwgc3VjaCBvcHRpbWlz YXRpb25zIGNvdWxkIGNvbWUgbGF0ZXIgdGhvdWdoLgoKQXBwYXJlbnRseSBzbywgSSBkbyBmZWVs IHRoZSAibGl2ZW5lc3MiIGNvbmNlcHQgaW4gdGhlIFJNTSBzcGVjIGlzIHZlcnkKY29uZnVzaW5n LiBCdXQgaXQgbG9va3MgbGlrZSBhbiBSVFQgaW4gdGhlIHVucHJvdGVjdGVkIElQQSByYW5nZSBp c24ndApjb25zaWRlcmVkICdsaXZlJyBpZiBpdCBvbmx5IGNvbnRhaW5zIEFTU0lHTkVEX05TIGVu dHJpZXMuIFRoZSB3b3JkaW5nCmhlcmUgaXNuJ3QgZ3JlYXQgdGhvdWdoOgoKICBBbiBSVFQgaXMg bGl2ZSBpZiwgZm9yIGFueSBvZiBpdHMgZW50cmllcywgZWl0aGVyIG9mIHRoZSBmb2xsb3dpbmcg aXMKICB0cnVlOgogICAg4oCiIFRoZSBSVFRFIHN0YXRlIGlzIEFTU0lHTkVECiAgICDigKIgVGhl IFJUVEUgc3RhdGUgaXMgVEFCTEUuCgogICBOb3RlIHRoYXQgYW4gUlRUIGNhbiBiZSBub24tbGl2 ZSwgZXZlbiBpZiBvbmUgb2YgaXRzIGVudHJpZXMgaXMgbGl2ZS4KICAgVGhpcyB3b3VsZCBiZSB0 aGUgY2FzZSBmb3IgZXhhbXBsZSBpZiB0aGUgUlRUIGNvcnJlc3BvbmRzIHRvIGFuCiAgIFVucHJv dGVjdGVkIElQQSByYW5nZSBhbmQgdGhlIHN0YXRlIG9mIG9uZSBvZiBpdHMgZW50cmllcyBpcwog ICBBU1NJR05FRF9OUy4KCkxpa2UgeW91IHNheSB0aGlzIGlzIGFuIG9wdGltaXNhdGlvbiwgc28g SSB0aGluayB3ZSBzaG91bGQgbGVhdmUgaXQKdW50aWwgbGF0ZXIgZm9yIG5vdy4KCj4+ICvCoMKg wqDCoMKgwqDCoCBzd2l0Y2ggKFJNSV9SRVRVUk5fU1RBVFVTKHJldCkpIHsKPj4gK8KgwqDCoMKg wqDCoMKgIGNhc2UgUk1JX1NVQ0NFU1M6Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGJyZWFr Owo+PiArwqDCoMKgwqDCoMKgwqAgY2FzZSBSTUlfRVJST1JfUlRUOgo+PiArwqDCoMKgwqDCoMKg wqDCoMKgwqDCoCBpZiAobmV4dF9hZGRyID09IGFkZHIpIHsKPiAKPiBBdCB0aGlzIHBvaW50IHdl IGhhdmUgYSBibG9jayBhbGlnbmVkIGFkZHJlc3MsIGJ1dCB0aGUgbWFwcGluZyBpcwo+IGZ1cnRo ZXIgZGVlcC4gR2l2ZW4sIHN0YXJ0IGZyb20gdG9wIHRvIGRvd24sIHdlIGltcGxpY2l0bHkgaGFu ZGxlCj4gdGhlIGNhc2Ugb2YgYmxvY2sgbWFwcGluZ3MuIE5vdCBzdXJlIGlmIHRoYXQgbmVlZHMg dG8gYmUgaW4gYSBjb21tZW50Cj4gaGVyZS4KPiAKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoCBuZXh0X2FkZHIgPSBBTElHTihhZGRyICsgMSwgbWFwX3NpemUpOwo+IAo+IFJlc2V0 IHRvIHRoZSAiYWN0dWFsIG5leHQiIGFzIGl0IHdhcyBvdmVyd3JpdHRlbiBieSB0aGUgUk1JIGNh bGwuCgpJJ2xsIGFkZCBhIGNvbW1lbnQgZXhwbGFpbmluZyB3aGF0J3MgaGFwcGVuaW5nIGhlcmUu Cgo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJlYWxtX3VubWFwX3JhbmdlX3No YXJlZChrdm0sIGxldmVsICsgMSwgYWRkciwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIG5leHRfYWRkcik7Cj4+ICvCoMKgwqDCoMKg wqDCoMKgwqDCoMKgIH0KPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgYnJlYWs7Cj4+ICvCoMKg wqDCoMKgwqDCoCBkZWZhdWx0Ogo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBXQVJOX09OKDEp Owo+PiArwqDCoMKgwqDCoMKgwqAgfQo+PiArwqDCoMKgIH0KPj4gK30KPj4gKwo+PiArc3RhdGlj IHZvaWQgcmVhbG1fdW5tYXBfcmFuZ2VfcHJpdmF0ZShzdHJ1Y3Qga3ZtICprdm0sCj4+ICvCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgdW5zaWduZWQgbG9uZyBzdGFy dCwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCB1bnNpZ25l ZCBsb25nIGVuZCkKPj4gK3sKPj4gK8KgwqDCoCBzdHJ1Y3QgcmVhbG0gKnJlYWxtID0gJmt2bS0+ YXJjaC5yZWFsbTsKPj4gK8KgwqDCoCBzc2l6ZV90IG1hcF9zaXplID0gUk1FX1BBR0VfU0laRTsK Pj4gK8KgwqDCoCB1bnNpZ25lZCBsb25nIG5leHRfYWRkciwgYWRkcjsKPj4gKwo+PiArwqDCoMKg IGZvciAoYWRkciA9IHN0YXJ0OyBhZGRyIDwgZW5kOyBhZGRyID0gbmV4dF9hZGRyKSB7Cj4+ICvC oMKgwqDCoMKgwqDCoCBpbnQgcmV0Owo+PiArCj4+ICvCoMKgwqDCoMKgwqDCoCBuZXh0X2FkZHIg PSBBTElHTihhZGRyICsgMSwgbWFwX3NpemUpOwo+PiArCj4+ICvCoMKgwqDCoMKgwqDCoCByZXQg PSByZWFsbV9kZXN0cm95X3Byb3RlY3RlZChyZWFsbSwgYWRkciwgJm5leHRfYWRkcik7Cj4+ICsK Pj4gK8KgwqDCoMKgwqDCoMKgIGlmIChXQVJOX09OKHJldCkpCj4+ICvCoMKgwqDCoMKgwqDCoMKg wqDCoMKgIGJyZWFrOwo+PiArwqDCoMKgIH0KPj4gK30KPj4gKwo+PiArc3RhdGljIHZvaWQgcmVh bG1fdW5tYXBfcmFuZ2Uoc3RydWN0IGt2bSAqa3ZtLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoCB1bnNpZ25lZCBsb25nIHN0YXJ0LAo+PiArwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoCB1bnNpZ25lZCBsb25nIGVuZCwKPj4gK8KgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqAgYm9vbCB1bm1hcF9wcml2YXRlKQo+PiArewo+PiArwqDCoMKgIHJl YWxtX3VubWFwX3JhbmdlX3NoYXJlZChrdm0sIFJNRV9SVFRfTUFYX0xFVkVMIC0gMSwgc3RhcnQs IGVuZCk7Cj4gCj4gbWlub3Igbml0OiBXZSBhbHJlYWR5IGhhdmUgYSBoZWxwZXIgdG8gZmluZCBh IHN1aXRhYmxlIHN0YXJ0IGxldmVsCj4gKGRlZmluZWQgYmVsb3cpLCBtYXkgYmUgd2UgY291bGQg dXNlIHRoYXQgPyBBbmQgZXZlbiBkbyB0aGUgcnR0X2Rlc3Ryb3kKPiBvcHRpbWlzYXRpb24gZm9y IHVucHJvdGVjdGVkIHJhbmdlLgoKR29vZCBwb2ludCwgdGhhdCBzaG91bGQgd29yay4KCj4+ICvC oMKgwqAgaWYgKHVubWFwX3ByaXZhdGUpCj4+ICvCoMKgwqDCoMKgwqDCoCByZWFsbV91bm1hcF9y YW5nZV9wcml2YXRlKGt2bSwgc3RhcnQsIGVuZCk7Cj4+ICt9Cj4+ICsKPj4gwqAgdTMyIGt2bV9y ZWFsbV9pcGFfbGltaXQodm9pZCkKPj4gwqAgewo+PiDCoMKgwqDCoMKgIHJldHVybiB1NjRfZ2V0 X2JpdHMocm1tX2ZlYXRfcmVnMCwgUk1JX0ZFQVRVUkVfUkVHSVNURVJfMF9TMlNaKTsKPj4gQEAg LTE5MCw2ICszNDEsMzAgQEAgc3RhdGljIGludCByZWFsbV9ydHRfZGVzdHJveShzdHJ1Y3QgcmVh bG0gKnJlYWxtLAo+PiB1bnNpZ25lZCBsb25nIGFkZHIsCj4+IMKgwqDCoMKgwqAgcmV0dXJuIHJl dDsKPj4gwqAgfQo+PiDCoCArc3RhdGljIGludCByZWFsbV9jcmVhdGVfcnR0X2xldmVscyhzdHJ1 Y3QgcmVhbG0gKnJlYWxtLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg IHVuc2lnbmVkIGxvbmcgaXBhLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgIGludCBsZXZlbCwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBp bnQgbWF4X2xldmVsLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHN0 cnVjdCBrdm1fbW11X21lbW9yeV9jYWNoZSAqbWMpCj4+ICt7Cj4+ICvCoMKgwqAgaWYgKFdBUk5f T04obGV2ZWwgPT0gbWF4X2xldmVsKSkKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiAwOwo+PiAr Cj4+ICvCoMKgwqAgd2hpbGUgKGxldmVsKysgPCBtYXhfbGV2ZWwpIHsKPj4gK8KgwqDCoMKgwqDC oMKgIHBoeXNfYWRkcl90IHJ0dCA9IGFsbG9jX2RlbGVnYXRlZF9wYWdlKHJlYWxtLCBtYyk7Cj4+ ICsKPj4gK8KgwqDCoMKgwqDCoMKgIGlmIChydHQgPT0gUEhZU19BRERSX01BWCkKPj4gK8KgwqDC oMKgwqDCoMKgwqDCoMKgwqAgcmV0dXJuIC1FTk9NRU07Cj4+ICsKPj4gK8KgwqDCoMKgwqDCoMKg IGlmIChyZWFsbV9ydHRfY3JlYXRlKHJlYWxtLCBpcGEsIGxldmVsLCBydHQpKSB7Cj4+ICvCoMKg wqDCoMKgwqDCoMKgwqDCoMKgIGZyZWVfZGVsZWdhdGVkX3BhZ2UocmVhbG0sIHJ0dCk7Cj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJldHVybiAtRU5YSU87Cj4+ICvCoMKgwqDCoMKgwqDCoCB9 Cj4+ICvCoMKgwqAgfQo+PiArCj4+ICvCoMKgwqAgcmV0dXJuIDA7Cj4+ICt9Cj4+ICsKPj4gwqAg c3RhdGljIGludCByZWFsbV90ZWFyX2Rvd25fcnR0X2xldmVsKHN0cnVjdCByZWFsbSAqcmVhbG0s IGludCBsZXZlbCwKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqAgdW5zaWduZWQgbG9uZyBzdGFydCwgdW5zaWduZWQgbG9uZyBlbmQpCj4+IMKgIHsKPj4gQEAg LTI2NSw2ICs0NDAsNjggQEAgc3RhdGljIGludCByZWFsbV90ZWFyX2Rvd25fcnR0X3JhbmdlKHN0 cnVjdCByZWFsbQo+PiAqcmVhbG0sCj4+IMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgIHN0YXJ0LCBlbmQpOwo+PiDCoCB9Cj4+IMKgICsvKgo+PiArICogUmV0dXJu cyAwIG9uIHN1Y2Nlc3NmdWwgZm9sZCwgYSBuZWdhdGl2ZSB2YWx1ZSBvbiBlcnJvciwgYQo+PiBw b3NpdGl2ZSB2YWx1ZSBpZgo+PiArICogd2Ugd2VyZSBub3QgYWJsZSB0byBmb2xkIGFsbCB0YWJs ZXMgYXQgdGhpcyBsZXZlbC4KPj4gKyAqLwo+PiArc3RhdGljIGludCByZWFsbV9mb2xkX3J0dF9s ZXZlbChzdHJ1Y3QgcmVhbG0gKnJlYWxtLCBpbnQgbGV2ZWwsCj4+ICvCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqAgdW5zaWduZWQgbG9uZyBzdGFydCwgdW5zaWduZWQgbG9uZyBlbmQpCj4+ ICt7Cj4+ICvCoMKgwqAgaW50IG5vdF9mb2xkZWQgPSAwOwo+PiArwqDCoMKgIHNzaXplX3QgbWFw X3NpemU7Cj4+ICvCoMKgwqAgdW5zaWduZWQgbG9uZyBhZGRyLCBuZXh0X2FkZHI7Cj4+ICsKPj4g K8KgwqDCoCBpZiAoV0FSTl9PTihsZXZlbCA+IFJNRV9SVFRfTUFYX0xFVkVMKSkKPj4gK8KgwqDC oMKgwqDCoMKgIHJldHVybiAtRUlOVkFMOwo+PiArCj4+ICvCoMKgwqAgbWFwX3NpemUgPSBybWVf cnR0X2xldmVsX21hcHNpemUobGV2ZWwgLSAxKTsKPj4gKwo+PiArwqDCoMKgIGZvciAoYWRkciA9 IHN0YXJ0OyBhZGRyIDwgZW5kOyBhZGRyID0gbmV4dF9hZGRyKSB7Cj4+ICvCoMKgwqDCoMKgwqDC oCBwaHlzX2FkZHJfdCBydHRfZ3JhbnVsZTsKPj4gK8KgwqDCoMKgwqDCoMKgIGludCByZXQ7Cj4+ ICvCoMKgwqDCoMKgwqDCoCB1bnNpZ25lZCBsb25nIGFsaWduX2FkZHIgPSBBTElHTihhZGRyLCBt YXBfc2l6ZSk7Cj4+ICsKPj4gK8KgwqDCoMKgwqDCoMKgIG5leHRfYWRkciA9IEFMSUdOKGFkZHIg KyAxLCBtYXBfc2l6ZSk7Cj4+ICsKPj4gK8KgwqDCoMKgwqDCoMKgIHJldCA9IHJlYWxtX3J0dF9m b2xkKHJlYWxtLCBhbGlnbl9hZGRyLCBsZXZlbCwgJnJ0dF9ncmFudWxlKTsKPj4gKwo+PiArwqDC oMKgwqDCoMKgwqAgc3dpdGNoIChSTUlfUkVUVVJOX1NUQVRVUyhyZXQpKSB7Cj4+ICvCoMKgwqDC oMKgwqDCoCBjYXNlIFJNSV9TVUNDRVNTOgo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBpZiAo IVdBUk5fT04ocm1pX2dyYW51bGVfdW5kZWxlZ2F0ZShydHRfZ3JhbnVsZSkpKQo+PiArwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGZyZWVfcGFnZSgodW5zaWduZWQgbG9uZylwaHlzX3Rv X3ZpcnQocnR0X2dyYW51bGUpKTsKPiAKPiBtaW5vciBuaXQ6IERvIHdlIG5lZWQgYSB3cmFwcGVy IGZ1bmN0aW9uIGZvciB0aGluZ3MgbGlrZSB0aGlzLCBhYmQKPiBsZWFraW5nIHRoZSBwYWdlIGlm IHVuZGVsZWdhdGUgZmFpbHMsIHNvbWV0aGluZyBsaWtlCj4gcm1lX3JlY2xhaW1fZGVsZWdhdGVk X3BhZ2UoKcKgID8KCkknbGwgdGFrZSBhIGxvb2sgYXQgdGhhdCAtIG1vc3RseSBzaXRlcyBkb24n dCBoYXZlIHRoZSBzYW1lIGFyZ3VtZW50CnJlcGVhdGVkICh1bmxpa2UgdGhpcyBvbmUpLCBzbyBJ J2xsIGhhdmUgdG8gaGF2ZSBhIHRoaW5rIGFib3V0IHRoZSBiZXN0CmZvcm0uCgo+PiArwqDCoMKg wqDCoMKgwqDCoMKgwqDCoCBicmVhazsKPj4gK8KgwqDCoMKgwqDCoMKgIGNhc2UgUk1JX0VSUk9S X1JUVDoKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgaWYgKGxldmVsID09IFJNRV9SVFRfTUFY X0xFVkVMIHx8Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgUk1JX1JFVFVSTl9J TkRFWChyZXQpIDwgbGV2ZWwpIHsKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBu b3RfZm9sZGVkKys7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgYnJlYWs7Cj4+ ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIH0KPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgLyog UmVjdXJzZSBhIGxldmVsIGRlZXBlciAqLwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXQg PSByZWFsbV9mb2xkX3J0dF9sZXZlbChyZWFsbSwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgbGV2ZWwgKyAxLAo+PiArwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBhZGRyLAo+PiArwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBuZXh0X2FkZHIp Owo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBpZiAocmV0IDwgMCkKPj4gK8KgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoCByZXR1cm4gcmV0Owo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDC oCBlbHNlIGlmIChyZXQgPT0gMCkKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAv KiBUcnkgYWdhaW4gYXQgdGhpcyBsZXZlbCAqLwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgIG5leHRfYWRkciA9IGFkZHI7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGJyZWFr Owo+PiArwqDCoMKgwqDCoMKgwqAgZGVmYXVsdDoKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAg cmV0dXJuIC1FTlhJTzsKPj4gK8KgwqDCoMKgwqDCoMKgIH0KPj4gK8KgwqDCoCB9Cj4+ICsKPj4g K8KgwqDCoCByZXR1cm4gbm90X2ZvbGRlZDsKPj4gK30KPj4gKwo+PiArc3RhdGljIGludCByZWFs bV9mb2xkX3J0dF9yYW5nZShzdHJ1Y3QgcmVhbG0gKnJlYWxtLAo+PiArwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgIHVuc2lnbmVkIGxvbmcgc3RhcnQsIHVuc2lnbmVkIGxvbmcgZW5kKQo+ PiArewo+PiArwqDCoMKgIHJldHVybiByZWFsbV9mb2xkX3J0dF9sZXZlbChyZWFsbSwgZ2V0X3N0 YXJ0X2xldmVsKHJlYWxtKSArIDEsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoCBzdGFydCwgZW5kKTsKPj4gK30KPj4gKwo+PiDCoCBzdGF0aWMgdm9pZCBlbnN1cmVf c3BhcmVfcGFnZShzdHJ1Y3QgcmVhbG0gKnJlYWxtKQo+PiDCoCB7Cj4+IMKgwqDCoMKgwqAgcGh5 c19hZGRyX3QgdG1wX3J0dDsKPj4gQEAgLTI5NSw2ICs1MzIsMTQ3IEBAIHZvaWQga3ZtX3JlYWxt X2Rlc3Ryb3lfcnR0cyhzdHJ1Y3Qga3ZtICprdm0sIHUzMgo+PiBpYV9iaXRzKQo+PiDCoMKgwqDC oMKgIFdBUk5fT04ocmVhbG1fdGVhcl9kb3duX3J0dF9yYW5nZShyZWFsbSwgMCwgKDFVTCA8PCBp YV9iaXRzKSkpOwo+PiDCoCB9Cj4+IMKgICt2b2lkIGt2bV9yZWFsbV91bm1hcF9yYW5nZShzdHJ1 Y3Qga3ZtICprdm0sIHVuc2lnbmVkIGxvbmcgaXBhLCB1NjQKPj4gc2l6ZSwKPj4gK8KgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqAgYm9vbCB1bm1hcF9wcml2YXRlKQo+PiArewo+PiArwqDCoMKg IHVuc2lnbmVkIGxvbmcgZW5kID0gaXBhICsgc2l6ZTsKPj4gK8KgwqDCoCBzdHJ1Y3QgcmVhbG0g KnJlYWxtID0gJmt2bS0+YXJjaC5yZWFsbTsKPj4gKwo+PiArwqDCoMKgIGVuZCA9IG1pbihCSVQo cmVhbG0tPmlhX2JpdHMgLSAxKSwgZW5kKTsKPj4gKwo+PiArwqDCoMKgIGVuc3VyZV9zcGFyZV9w YWdlKHJlYWxtKTsKPj4gKwo+PiArwqDCoMKgIHJlYWxtX3VubWFwX3JhbmdlKGt2bSwgaXBhLCBl bmQsIHVubWFwX3ByaXZhdGUpOwo+PiArCj4+ICvCoMKgwqAgcmVhbG1fZm9sZF9ydHRfcmFuZ2Uo cmVhbG0sIGlwYSwgZW5kKTsKPiAKPiBTaG91bGRuJ3QgdGhpcyBiZSA6Cj4gCj4gwqDCoMKgwqBp ZiAodW5tYXBfcHJpdmF0ZSkKPiDCoMKgwqDCoMKgwqDCoCByZWFsbV9mb2xkX3J0dF9yYW5nZShy ZWFsbSwgaXBhLCBlbmQpOwoKSW5kZWVkIGl0J3MgYSBsaXR0bGUgcG9pbnRsZXNzIGZvbGRpbmcg aWYgd2UgaGF2ZW4ndCB0b3VjaGVkIHRoZSBwcml2YXRlCm1hcHBpbmdzLgoKPiBBbHNvIGl0IGlz IGZpbmUgdG8gcmVjbGFpbSBSVFRzIGZyb20gdGhlIHByb3RlY3RlZCBzcGFjZSwgbm90IHRoZQo+ IHVucHJvdGVjdGVkIGhhbGYsIGFzIGxvbmcgYXMgd2UgdXNlIFJUVF9ERVNUUk9ZIGluIHVubWFw X3NoYXJlZCByb3V0aW5lLgo+IAo+PiArfQo+PiArCj4+ICtzdGF0aWMgaW50IGZpbmRfbWFwX2xl dmVsKHN0cnVjdCByZWFsbSAqcmVhbG0sCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCB1 bnNpZ25lZCBsb25nIHN0YXJ0LAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgdW5zaWdu ZWQgbG9uZyBlbmQpCj4+ICt7Cj4+ICvCoMKgwqAgaW50IGxldmVsID0gUk1FX1JUVF9NQVhfTEVW RUw7Cj4+ICsKPj4gK8KgwqDCoCB3aGlsZSAobGV2ZWwgPiBnZXRfc3RhcnRfbGV2ZWwocmVhbG0p KSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCB1bnNpZ25lZCBsb25nIG1hcF9zaXplID0gcm1lX3J0dF9s ZXZlbF9tYXBzaXplKGxldmVsIC0gMSk7Cj4+ICsKPj4gK8KgwqDCoMKgwqDCoMKgIGlmICghSVNf QUxJR05FRChzdGFydCwgbWFwX3NpemUpIHx8Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIChz dGFydCArIG1hcF9zaXplKSA+IGVuZCkKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgYnJlYWs7 Cj4+ICsKPj4gK8KgwqDCoMKgwqDCoMKgIGxldmVsLS07Cj4+ICvCoMKgwqAgfQo+PiArCj4+ICvC oMKgwqAgcmV0dXJuIGxldmVsOwo+PiArfQo+PiArCj4+ICtpbnQgcmVhbG1fc2V0X2lwYV9zdGF0 ZShzdHJ1Y3Qga3ZtX3ZjcHUgKnZjcHUsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHVuc2ln bmVkIGxvbmcgc3RhcnQsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHVuc2lnbmVkIGxvbmcg ZW5kLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCB1bnNpZ25lZCBsb25nIHJpcGFzKQo+PiAr ewo+PiArwqDCoMKgIHN0cnVjdCBrdm0gKmt2bSA9IHZjcHUtPmt2bTsKPj4gK8KgwqDCoCBzdHJ1 Y3QgcmVhbG0gKnJlYWxtID0gJmt2bS0+YXJjaC5yZWFsbTsKPj4gK8KgwqDCoCBzdHJ1Y3QgcmVh bG1fcmVjICpyZWMgPSAmdmNwdS0+YXJjaC5yZWM7Cj4+ICvCoMKgwqAgcGh5c19hZGRyX3QgcmRf cGh5cyA9IHZpcnRfdG9fcGh5cyhyZWFsbS0+cmQpOwo+PiArwqDCoMKgIHBoeXNfYWRkcl90IHJl Y19waHlzID0gdmlydF90b19waHlzKHJlYy0+cmVjX3BhZ2UpOwo+PiArwqDCoMKgIHN0cnVjdCBr dm1fbW11X21lbW9yeV9jYWNoZSAqbWVtY2FjaGUgPSAmdmNwdS0+YXJjaC5tbXVfcGFnZV9jYWNo ZTsKPj4gK8KgwqDCoCB1bnNpZ25lZCBsb25nIGlwYSA9IHN0YXJ0Owo+PiArwqDCoMKgIGludCBy ZXQgPSAwOwo+PiArCj4+ICvCoMKgwqAgd2hpbGUgKGlwYSA8IGVuZCkgewo+PiArwqDCoMKgwqDC oMKgwqAgdW5zaWduZWQgbG9uZyBuZXh0Owo+PiArCj4+ICvCoMKgwqDCoMKgwqDCoCByZXQgPSBy bWlfcnR0X3NldF9yaXBhcyhyZF9waHlzLCByZWNfcGh5cywgaXBhLCBlbmQsICZuZXh0KTsKPj4g Kwo+PiArwqDCoMKgwqDCoMKgwqAgaWYgKFJNSV9SRVRVUk5fU1RBVFVTKHJldCkgPT0gUk1JX0VS Uk9SX1JUVCkgewo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBpbnQgd2Fsa19sZXZlbCA9IFJN SV9SRVRVUk5fSU5ERVgocmV0KTsKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgaW50IGxldmVs ID0gZmluZF9tYXBfbGV2ZWwocmVhbG0sIGlwYSwgZW5kKTsKPiAKPiBNaWdodCBiZSB3b3J0aCBh ZGRpbmcgYSBjb21tZW50IGhlcmUuIENoZWNrIGlmIHRoZSBSTU0gbmVlZHMgdGFibGVzIHRvCj4g Y3JlYXRlIGRlZXBlciBsZXZlbCB0YWJsZXMuCgpPawoKPj4gKwo+PiArwqDCoMKgwqDCoMKgwqDC oMKgwqDCoCBpZiAod2Fsa19sZXZlbCA8IGxldmVsKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqAgcmV0ID0gcmVhbG1fY3JlYXRlX3J0dF9sZXZlbHMocmVhbG0sIGlwYSwKPj4g K8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoCB3YWxrX2xldmVsLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGxldmVsLAo+PiArwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg IG1lbWNhY2hlKTsKPiDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgLyogUmV0cnkgd2l0 aCBSVFRzIGNyZWF0ZWQgKi8KCk9rCgo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg IGlmICghcmV0KQo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgY29u dGludWU7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIH0gZWxzZSB7Cj4+ICvCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqAgcmV0ID0gLUVJTlZBTDsKPj4gK8KgwqDCoMKgwqDCoMKgwqDC oMKgwqAgfQo+PiArCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGJyZWFrOwo+PiArwqDCoMKg wqDCoMKgwqAgfSBlbHNlIGlmIChSTUlfUkVUVVJOX1NUQVRVUyhyZXQpICE9IFJNSV9TVUNDRVNT KSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIFdBUk4oMSwgIlVuZXhwZWN0ZWQgZXJyb3Ig aW4gJXM6ICUjeFxuIiwgX19mdW5jX18sCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoCByZXQpOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXQgPSAtRUlOVkFMOwo+PiAr wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBicmVhazsKPj4gK8KgwqDCoMKgwqDCoMKgIH0KPj4gK8Kg wqDCoMKgwqDCoMKgIGlwYSA9IG5leHQ7Cj4+ICvCoMKgwqAgfQo+PiArCj4+ICvCoMKgwqAgaWYg KHJpcGFzID09IFJNSV9FTVBUWSAmJiBpcGEgIT0gc3RhcnQpCj4+ICvCoMKgwqDCoMKgwqDCoCBr dm1fcmVhbG1fdW5tYXBfcmFuZ2Uoa3ZtLCBzdGFydCwgaXBhIC0gc3RhcnQsIHRydWUpOwo+IAo+ IFRoaXMgdHJpZ2dlcnMgdW5tYXBwaW5nIHRoZSAic2hhcmVkIiBhbGlhc2VzIHRvbywgd2hpY2gg aXMgbm90IG5lY2Vzc2FyeS4KClllYWgsIHRoZSBrdm1fcmVhbG1fdW5tYXBfcmFuZ2UoKSBmdW5j dGlvbiBpcyBhIGJpdCBwcm9ibGVtYXRpYyAtIHRoZQp2YXJpb3VzICJ1bm1hcF9wcml2YXRlIiBh bmQgIm9ubHlfc2hhcmVkIiBmbGFncyBuZWVkIGEgYml0IG9mIGEgcmV0aGluay4KCj4+ICsKPj4g K8KgwqDCoCByZXR1cm4gcmV0Owo+PiArfQo+PiArCj4+ICtzdGF0aWMgaW50IHJlYWxtX2luaXRf aXBhX3N0YXRlKHN0cnVjdCByZWFsbSAqcmVhbG0sCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqAgdW5zaWduZWQgbG9uZyBpcGEsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqAgdW5zaWduZWQgbG9uZyBlbmQpCj4+ICt7Cj4+ICvCoMKgwqAgcGh5c19hZGRyX3QgcmRf cGh5cyA9IHZpcnRfdG9fcGh5cyhyZWFsbS0+cmQpOwo+PiArwqDCoMKgIGludCByZXQ7Cj4+ICsK Pj4gK8KgwqDCoCB3aGlsZSAoaXBhIDwgZW5kKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCB1bnNpZ25l ZCBsb25nIG5leHQ7Cj4+ICsKPj4gK8KgwqDCoMKgwqDCoMKgIHJldCA9IHJtaV9ydHRfaW5pdF9y aXBhcyhyZF9waHlzLCBpcGEsIGVuZCwgJm5leHQpOwo+PiArCj4+ICvCoMKgwqDCoMKgwqDCoCBp ZiAoUk1JX1JFVFVSTl9TVEFUVVMocmV0KSA9PSBSTUlfRVJST1JfUlRUKSB7Cj4+ICvCoMKgwqDC oMKgwqDCoMKgwqDCoMKgIGludCBlcnJfbGV2ZWwgPSBSTUlfUkVUVVJOX0lOREVYKHJldCk7Cj4+ ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGludCBsZXZlbCA9IGZpbmRfbWFwX2xldmVsKHJlYWxt LCBpcGEsIGVuZCk7Cj4+ICsKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgaWYgKFdBUk5fT04o ZXJyX2xldmVsID49IGxldmVsKSkKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBy ZXR1cm4gLUVOWElPOwo+PiArCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJldCA9IHJlYWxt X2NyZWF0ZV9ydHRfbGV2ZWxzKHJlYWxtLCBpcGEsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGVycl9sZXZlbCwKPj4gK8KgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgbGV2 ZWwsIE5VTEwpOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBpZiAocmV0KQo+PiArwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJldHVybiByZXQ7Cj4+ICvCoMKgwqDCoMKgwqDCoMKg wqDCoMKgIC8qIFJldHJ5IHdpdGggdGhlIFJUVCBsZXZlbHMgaW4gcGxhY2UgKi8KPj4gK8KgwqDC oMKgwqDCoMKgwqDCoMKgwqAgY29udGludWU7Cj4+ICvCoMKgwqDCoMKgwqDCoCB9IGVsc2UgaWYg KFdBUk5fT04ocmV0KSkgewo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXR1cm4gLUVOWElP Owo+PiArwqDCoMKgwqDCoMKgwqAgfQo+PiArCj4+ICvCoMKgwqDCoMKgwqDCoCBpcGEgPSBuZXh0 Owo+PiArwqDCoMKgIH0KPj4gKwo+PiArwqDCoMKgIHJldHVybiAwOwo+PiArfQo+PiArCj4+ICtz dGF0aWMgaW50IGt2bV9pbml0X2lwYV9yYW5nZV9yZWFsbShzdHJ1Y3Qga3ZtICprdm0sCj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBzdHJ1Y3Qga3ZtX2NhcF9hcm1f cm1lX2luaXRfaXBhX2FyZ3MgKmFyZ3MpCj4+ICt7Cj4+ICvCoMKgwqAgaW50IHJldCA9IDA7Cj4+ ICvCoMKgwqAgZ3BhX3QgYWRkciwgZW5kOwo+PiArwqDCoMKgIHN0cnVjdCByZWFsbSAqcmVhbG0g PSAma3ZtLT5hcmNoLnJlYWxtOwo+PiArCj4+ICvCoMKgwqAgYWRkciA9IGFyZ3MtPmluaXRfaXBh X2Jhc2U7Cj4+ICvCoMKgwqAgZW5kID0gYWRkciArIGFyZ3MtPmluaXRfaXBhX3NpemU7Cj4+ICsK Pj4gK8KgwqDCoCBpZiAoZW5kIDwgYWRkcikKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiAtRUlO VkFMOwo+PiArCj4+ICvCoMKgwqAgaWYgKGt2bV9yZWFsbV9zdGF0ZShrdm0pICE9IFJFQUxNX1NU QVRFX05FVykKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiAtRUlOVkFMOwo+PiArCj4+ICvCoMKg wqAgcmV0ID0gcmVhbG1faW5pdF9pcGFfc3RhdGUocmVhbG0sIGFkZHIsIGVuZCk7Cj4+ICsKPj4g K8KgwqDCoCByZXR1cm4gcmV0Owo+IAo+IHN1cGVyIG1pbm9yIG5pdDoKPiAKPiDCoMKgwqDCoHJl dHVybiByZWFsbV9pbml0X2lwYV9zdGF0ZShyZWFsbSwgYWRkciwgZW5kKTsKClRoYXQgd2FzIHBy b2JhYmx5IGxlZnQgb3ZlciBmcm9tIHNvbWUgcHJpbnRrKCkgZGVidWdnaW5nIDspCgpUaGFua3Ms CgpTdGV2ZQoKPj4gK30KPj4gKwo+PiDCoCAvKiBQcm90ZWN0cyBhY2Nlc3MgdG8gcm1lX3ZtaWRf Yml0bWFwICovCj4+IMKgIHN0YXRpYyBERUZJTkVfU1BJTkxPQ0socm1lX3ZtaWRfbG9jayk7Cj4+ IMKgIHN0YXRpYyB1bnNpZ25lZCBsb25nICpybWVfdm1pZF9iaXRtYXA7Cj4+IEBAIC00MTgsNiAr Nzk2LDE4IEBAIGludCBrdm1fcmVhbG1fZW5hYmxlX2NhcChzdHJ1Y3Qga3ZtICprdm0sIHN0cnVj dAo+PiBrdm1fZW5hYmxlX2NhcCAqY2FwKQo+PiDCoMKgwqDCoMKgIGNhc2UgS1ZNX0NBUF9BUk1f Uk1FX0NSRUFURV9SRDoKPj4gwqDCoMKgwqDCoMKgwqDCoMKgIHIgPSBrdm1fY3JlYXRlX3JlYWxt KGt2bSk7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoCBicmVhazsKPj4gK8KgwqDCoCBjYXNlIEtWTV9D QVBfQVJNX1JNRV9JTklUX0lQQV9SRUFMTTogewo+PiArwqDCoMKgwqDCoMKgwqAgc3RydWN0IGt2 bV9jYXBfYXJtX3JtZV9pbml0X2lwYV9hcmdzIGFyZ3M7Cj4+ICvCoMKgwqDCoMKgwqDCoCB2b2lk IF9fdXNlciAqYXJncCA9IHU2NF90b191c2VyX3B0cihjYXAtPmFyZ3NbMV0pOwo+PiArCj4+ICvC oMKgwqDCoMKgwqDCoCBpZiAoY29weV9mcm9tX3VzZXIoJmFyZ3MsIGFyZ3AsIHNpemVvZihhcmdz KSkpIHsKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgciA9IC1FRkFVTFQ7Cj4+ICvCoMKgwqDC oMKgwqDCoMKgwqDCoMKgIGJyZWFrOwo+PiArwqDCoMKgwqDCoMKgwqAgfQo+PiArCj4+ICvCoMKg wqDCoMKgwqDCoCByID0ga3ZtX2luaXRfaXBhX3JhbmdlX3JlYWxtKGt2bSwgJmFyZ3MpOwo+PiAr wqDCoMKgwqDCoMKgwqAgYnJlYWs7Cj4+ICvCoMKgwqAgfQo+IAo+IAo+IFN1enVraQoKCl9fX19f X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fCmxpbnV4LWFybS1rZXJu ZWwgbWFpbGluZyBsaXN0CmxpbnV4LWFybS1rZXJuZWxAbGlzdHMuaW5mcmFkZWFkLm9yZwpodHRw Oi8vbGlzdHMuaW5mcmFkZWFkLm9yZy9tYWlsbWFuL2xpc3RpbmZvL2xpbnV4LWFybS1rZXJuZWwK