From: Ian Campbell <ian.campbell@citrix.com>
To: Tamas K Lengyel <tklengyel@sec.in.tum.de>
Cc: wei.liu2@citrix.com, stefano.stabellini@eu.citrix.com,
ian.jackson@eu.citrix.com, julien.grall@linaro.org, tim@xen.org,
xen-devel@lists.xen.org, stefano.stabellini@citrix.com,
jbeulich@suse.com, keir@xen.org
Subject: Re: [PATCH V13 4/7] xen/arm: Data abort exception (R/W) mem_events.
Date: Thu, 12 Mar 2015 13:35:05 +0000 [thread overview]
Message-ID: <1426167305.21353.423.camel@citrix.com> (raw)
In-Reply-To: <1425677073-13729-5-git-send-email-tklengyel@sec.in.tum.de>
On Fri, 2015-03-06 at 22:24 +0100, Tamas K Lengyel wrote:
> This patch enables to store, set, check and deliver LPAE R/W mem_events.
> As the LPAE PTE's lack enough available software programmable bits,
> we store the permissions in a Radix tree. The tree is only looked at if
> mem_access_enabled is turned on.
But it is maintained/updated regardless, is that deliberate?
> +static int p2m_mem_access_radix_set(struct p2m_domain *p2m, unsigned long pfn,
> + p2m_access_t a)
> +{
> + int rc;
> +
> + if ( p2m_access_rwx == a )
> + {
> + if ( p2m->mem_access_enabled )
In particular this is gated, but the rest of the function appears not to
be, which seems inconsistent...
> + radix_tree_delete(&p2m->mem_access_settings, pfn);
> +
> + return 0;
> + }
> +
> + rc = radix_tree_insert(&p2m->mem_access_settings, pfn,
> + radix_tree_int_to_ptr(a));
> + if ( rc == -EEXIST )
> + {
> + /* If a setting existed already, change it to the new one */
> + radix_tree_replace_slot(
> + radix_tree_lookup_slot(
> + &p2m->mem_access_settings, pfn),
> + radix_tree_int_to_ptr(a));
> + rc = 0;
> + }
> +
> + return rc;
> +}
> +
> enum p2m_operation {
> INSERT,
> ALLOCATE,
> REMOVE,
> RELINQUISH,
> CACHEFLUSH,
> + MEMACCESS,
> };
>
> /* Put any references on the single 4K page referenced by pte. TODO:
> @@ -560,13 +592,22 @@ static int apply_one_level(struct domain *d,
> if ( p2m_valid(orig_pte) )
> return P2M_ONE_DESCEND;
>
> - if ( is_mapping_aligned(*addr, end_gpaddr, 0, level_size) )
> + if ( is_mapping_aligned(*addr, end_gpaddr, 0, level_size) &&
> + /* We only create superpages when mem_access is not in use. */
> + (level == 3 || (level < 3 && !p2m->mem_access_enabled)) )
I don't think we can get away with adding this check to
is_mapping_aligned (it's used elsewhere), but perhaps you could wrap
this condition in a helper to use in both places.
mapping_allowed_at_level(p2m, level) or some such.
> - /* We do not handle replacing an existing table with a superpage */
> - (level == 3 || !p2m_table(orig_pte)) )
> + /* We do not handle replacing an existing table with a superpage
> + * or when mem_access is in use. */
> + (level == 3 || (!p2m_table(orig_pte) && !p2m->mem_access_enabled)) )
Actually, this is very subtly different isn't it. Can it be unified? If
not then ignore the helper idea.
> @@ -760,6 +807,47 @@ static int apply_one_level(struct domain *d,
> *addr += PAGE_SIZE;
> return P2M_ONE_PROGRESS_NOP;
> }
> +
> + case MEMACCESS:
> + if ( level < 3 )
> + {
> + if ( !p2m_valid(orig_pte) )
> + {
> + *addr += level_size;
> + return P2M_ONE_PROGRESS_NOP;
> + }
> +
> + /* Shatter large pages as we descend */
> + if ( p2m_mapping(orig_pte) )
> + {
> + rc = p2m_shatter_page(d, entry, level, flush_cache);
> + if ( rc < 0 )
> + return rc;
> + } /* else: an existing table mapping -> descend */
> +
> + return P2M_ONE_DESCEND;
> + }
> + else
> + {
> + pte = orig_pte;
> +
> + if ( !p2m_table(pte) )
> + pte.bits = 0;
What is this about? Just clobbering an invalid PTE?
> @@ -783,6 +871,8 @@ static int apply_p2m_changes(struct domain *d,
> unsigned int cur_root_table = ~0;
> unsigned int cur_offset[4] = { ~0, ~0, ~0, ~0 };
> unsigned int count = 0;
> + const unsigned long sgfn = paddr_to_pfn(start_gpaddr),
> + egfn = paddr_to_pfn(end_gpaddr);
> bool_t flush = false;
> bool_t flush_pt;
>
> @@ -912,6 +1006,12 @@ static int apply_p2m_changes(struct domain *d,
> rc = 0;
>
> out:
> + if ( flush )
> + {
> + flush_tlb_domain(d);
> + iommu_iotlb_flush(d, sgfn, egfn - sgfn);
> + }
Is moving the flush out of the loop an independent bug fix? If so please
do in a separate commit with a rationale in the commit log. If it is
somehow related to the changes here then please mention it in this
commit log, since it's a bit subtle.
> +
> if ( rc < 0 && ( op == INSERT || op == ALLOCATE ) &&
> addr != start_gpaddr )
> {
> @@ -1281,6 +1381,254 @@ void __init setup_virt_paging(void)
> smp_call_function(setup_virt_paging_one, (void *)val, 1);
> }
>
> +bool_t p2m_mem_access_check(paddr_t gpa, vaddr_t gla, const struct npfec npfec)
This is different to the current x86 prototype, is that due to your
other cleanup series?
> +{
> + int rc;
> + bool_t violation;
> + xenmem_access_t xma;
> + mem_event_request_t *req;
> + struct vcpu *v = current;
> + struct p2m_domain *p2m = p2m_get_hostp2m(v->domain);
> +
> + /* Mem_access is not in use. */
> + if ( !p2m->mem_access_enabled )
> + return true;
> +
> + rc = p2m_get_mem_access(v->domain, paddr_to_pfn(gpa), &xma);
> + if ( rc )
> + return true;
> +
> + /* Now check for mem_access violation. */
> + switch ( xma )
> + {
> + case XENMEM_access_rwx:
> + violation = false;
> + break;
> + case XENMEM_access_rw:
> + violation = npfec.insn_fetch;
> + break;
> + case XENMEM_access_wx:
> + violation = npfec.read_access;
> + break;
> + case XENMEM_access_rx:
> + case XENMEM_access_rx2rw:
> + violation = npfec.write_access;
> + break;
> + case XENMEM_access_x:
> + violation = npfec.read_access || npfec.write_access;
> + break;
> + case XENMEM_access_w:
> + violation = npfec.read_access || npfec.insn_fetch;
> + break;
> + case XENMEM_access_r:
> + violation = npfec.write_access || npfec.insn_fetch;
> + break;
> + default:
> + case XENMEM_access_n:
> + case XENMEM_access_n2rwx:
> + violation = true;
> + break;
> + }
> +
> + if ( !violation )
> + return true;
The preceding section looks pretty similar to the guits of x86's
p2m_mem_event_emulate_check, can they be combined?
> +
> + /* First, handle rx2rw and n2rwx conversion automatically. */
> + if ( npfec.write_access && xma == XENMEM_access_rx2rw )
> + {
> + rc = p2m_set_mem_access(v->domain, paddr_to_pfn(gpa), 1,
> + 0, ~0, XENMEM_access_rw);
> + return false;
> + }
> + else if ( xma == XENMEM_access_n2rwx )
> + {
> + rc = p2m_set_mem_access(v->domain, paddr_to_pfn(gpa), 1,
> + 0, ~0, XENMEM_access_rwx);
> + }
This looks like a bit of p2m_mem_access_check, can it be made common?
> +
> + /* Otherwise, check if there is a memory event listener, and send the message along */
> + if ( !mem_event_check_ring(&v->domain->mem_event->access) )
> + {
> + /* No listener */
> + if ( p2m->access_required )
> + {
> + gdprintk(XENLOG_INFO, "Memory access permissions failure, "
> + "no mem_event listener VCPU %d, dom %d\n",
> + v->vcpu_id, v->domain->domain_id);
> + domain_crash(v->domain);
> + }
> + else
> + {
> + /* n2rwx was already handled */
> + if ( xma != XENMEM_access_n2rwx )
> + {
> + /* A listener is not required, so clear the access
> + * restrictions. */
> + rc = p2m_set_mem_access(v->domain, paddr_to_pfn(gpa), 1,
> + 0, ~0, XENMEM_access_rwx);
> + }
> + }
> +
> + /* No need to reinject */
> + return false;
> + }
And this
> + req = xzalloc(mem_event_request_t);
> + if ( req )
> + {
> + req->reason = MEM_EVENT_REASON_VIOLATION;
> + if ( xma != XENMEM_access_n2rwx )
> + req->flags |= MEM_EVENT_FLAG_VCPU_PAUSED;
> + req->gfn = gpa >> PAGE_SHIFT;
> + req->offset = gpa & ((1 << PAGE_SHIFT) - 1);
> + req->gla = gla;
> + req->gla_valid = npfec.gla_valid;
> + req->access_r = npfec.read_access;
> + req->access_w = npfec.write_access;
> + req->access_x = npfec.insn_fetch;
> + if ( npfec_kind_in_gpt == npfec.kind )
> + req->fault_in_gpt = 1;
> + if ( npfec_kind_with_gla == npfec.kind )
> + req->fault_with_gla = 1;
> + req->vcpu_id = v->vcpu_id;
> +
> + mem_access_send_req(v->domain, req);
> + xfree(req);
> + }
> +
> + /* Pause the current VCPU */
> + if ( xma != XENMEM_access_n2rwx )
> + mem_event_vcpu_pause(v);
> +
> + return false;
> +}
> +
> +/* Set access type for a region of pfns.
> + * If start_pfn == -1ul, sets the default access type */
> +long p2m_set_mem_access(struct domain *d, unsigned long pfn, uint32_t nr,
> + uint32_t start, uint32_t mask, xenmem_access_t access)
and this function is nearly identical to the x86 one too.
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> + p2m_access_t a;
> + long rc = 0;
> +
> + static const p2m_access_t memaccess[] = {
> +#define ACCESS(ac) [XENMEM_access_##ac] = p2m_access_##ac
> + ACCESS(n),
> + ACCESS(r),
> + ACCESS(w),
> + ACCESS(rw),
> + ACCESS(x),
> + ACCESS(rx),
> + ACCESS(wx),
> + ACCESS(rwx),
> + ACCESS(rx2rw),
> + ACCESS(n2rwx),
> +#undef ACCESS
> + };
> +
> + switch ( access )
> + {
> + case 0 ... ARRAY_SIZE(memaccess) - 1:
> + a = memaccess[access];
> + break;
> + case XENMEM_access_default:
> + a = p2m->default_access;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /*
> + * Flip mem_access_enabled to true when a permission is set, as to prevent
> + * allocating or inserting super-pages.
> + */
> + p2m->mem_access_enabled = true;
> +
> + /* If request to set default access. */
> + if ( pfn == ~0ul )
> + {
> + p2m->default_access = a;
> + return 0;
> + }
> +
> + rc = apply_p2m_changes(d, MEMACCESS,
> + pfn_to_paddr(pfn+start), pfn_to_paddr(pfn+nr),
> + 0, MATTR_MEM, mask, 0, a);
> + if ( rc < 0 )
> + return rc;
> + else if ( rc > 0 )
> + return start + rc;
> +
> + return 0;
> +}
> +
> +int p2m_get_mem_access(struct domain *d, unsigned long gpfn,
> + xenmem_access_t *access)
> +{
> + struct p2m_domain *p2m = p2m_get_hostp2m(d);
> + void *i;
> + unsigned int index;
> +
> + static const xenmem_access_t memaccess[] = {
Would be nice not to have this static const thing twice in .rodata.
> +#define ACCESS(ac) [p2m_access_##ac] = XENMEM_access_##ac
> + ACCESS(n),
> + ACCESS(r),
> + ACCESS(w),
> + ACCESS(rw),
> + ACCESS(x),
> + ACCESS(rx),
> + ACCESS(wx),
> + ACCESS(rwx),
> + ACCESS(rx2rw),
> + ACCESS(n2rwx),
> +#undef ACCESS
> + };
> +
> + /* If no setting was ever set, just return rwx. */
> + if ( !p2m->mem_access_enabled )
> + {
> + *access = XENMEM_access_rwx;
> + return 0;
> + }
> +
> + /* If request to get default access */
> + if ( gpfn == ~0ull )
We should have a suitable constant for this, I think, INVALID_MFN looks
like the one.
> + {
> + *access = memaccess[p2m->default_access];
> + return 0;
> + }
> +
> + spin_lock(&p2m->lock);
> + i = radix_tree_lookup(&p2m->mem_access_settings, gpfn);
> + spin_unlock(&p2m->lock);
> +
> + if ( !i )
> + {
> + /*
> + * No setting was found in the Radix tree. Check if the
> + * entry exists in the page-tables.
> + */
> + paddr_t maddr = p2m_lookup(d, gpfn << PAGE_SHIFT, NULL);
> + if ( INVALID_PADDR == maddr )
> + return -ESRCH;
> +
> + /* If entry exists then its rwx. */
it's, please
> + *access = XENMEM_access_rwx;
> + }
> + else
> + {
> + /* Setting was found in the Radix tree. */
> + index = radix_tree_ptr_to_int(i);
> + if ( index >= ARRAY_SIZE(memaccess) )
> + return -ERANGE;
> +
> + *access = memaccess[index];
> + }
> +
> + return 0;
> +}
> +
> /*
> * Local variables:
> * mode: C
Ian.
next prev parent reply other threads:[~2015-03-12 13:35 UTC|newest]
Thread overview: 71+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-03-06 21:24 [PATCH V13 0/7] Mem_access for ARM Tamas K Lengyel
2015-03-06 21:24 ` [PATCH V13 1/7] xen/arm: p2m changes for mem_access support Tamas K Lengyel
2015-03-11 16:07 ` Stefano Stabellini
2015-03-11 17:05 ` Tamas K Lengyel
2015-03-12 11:27 ` Ian Campbell
2015-03-12 12:22 ` Tamas K Lengyel
2015-03-12 13:53 ` Ian Campbell
2015-03-12 12:57 ` Julien Grall
2015-03-12 13:18 ` Tamas K Lengyel
2015-03-12 13:25 ` Julien Grall
2015-03-12 13:55 ` Ian Campbell
2015-03-12 13:56 ` Ian Campbell
2015-03-12 14:10 ` Andrew Cooper
2015-03-12 16:56 ` Julien Grall
2015-03-12 17:11 ` Ian Campbell
2015-03-06 21:24 ` [PATCH V13 2/7] xen/arm: Implement domain_get_maximum_gpfn Tamas K Lengyel
2015-03-11 15:43 ` Stefano Stabellini
2015-03-06 21:24 ` [PATCH V13 3/7] xen/arm: Allow hypervisor access to mem_access protected pages Tamas K Lengyel
2015-03-12 12:08 ` Ian Campbell
2015-03-12 12:31 ` Tamas K Lengyel
2015-03-12 13:24 ` Julien Grall
2015-03-12 13:38 ` Tamas K Lengyel
2015-03-12 13:43 ` Julien Grall
2015-03-12 14:33 ` Tamas K Lengyel
2015-03-12 13:50 ` Julien Grall
2015-03-12 14:13 ` Tamas K Lengyel
2015-03-12 14:52 ` Julien Grall
2015-03-12 15:27 ` Ian Campbell
2015-03-12 15:40 ` Julien Grall
2015-03-12 15:44 ` Tamas K Lengyel
2015-03-12 15:56 ` Ian Campbell
2015-03-12 16:02 ` Tamas K Lengyel
2015-03-12 16:48 ` Ian Campbell
2015-03-12 16:55 ` Tamas K Lengyel
2015-03-12 15:54 ` Ian Campbell
2015-03-12 15:41 ` Tamas K Lengyel
2015-03-12 15:55 ` Ian Campbell
2015-03-12 16:10 ` Tamas K Lengyel
2015-03-06 21:24 ` [PATCH V13 4/7] xen/arm: Data abort exception (R/W) mem_events Tamas K Lengyel
2015-03-12 13:35 ` Ian Campbell [this message]
2015-03-12 15:13 ` Tamas K Lengyel
2015-03-12 15:19 ` Tamas K Lengyel
2015-03-12 15:24 ` Julien Grall
2015-03-12 15:35 ` Ian Campbell
2015-03-12 16:35 ` Julien Grall
2015-03-12 15:30 ` Ian Campbell
2015-03-12 15:13 ` Julien Grall
2015-03-12 15:26 ` Tamas K Lengyel
2015-03-12 15:37 ` Julien Grall
2015-03-12 15:46 ` Ian Campbell
2015-03-12 16:54 ` Julien Grall
2015-03-06 21:24 ` [PATCH V13 5/7] xen/arm: Instruction prefetch abort (X) mem_event handling Tamas K Lengyel
2015-03-23 14:32 ` Tamas K Lengyel
2015-03-23 15:15 ` Ian Campbell
2015-03-23 15:18 ` Ian Campbell
2015-03-23 15:47 ` Tamas K Lengyel
2015-03-23 16:22 ` Ian Campbell
2015-03-23 16:47 ` Tamas K Lengyel
2015-03-24 13:06 ` Tamas K Lengyel
2015-03-26 10:50 ` Ian Campbell
2015-03-26 11:24 ` Tamas K Lengyel
2015-03-26 11:53 ` Ian Campbell
2015-03-06 21:24 ` [PATCH V13 6/7] xen/arm: Enable mem_access on ARM Tamas K Lengyel
2015-03-12 13:36 ` Ian Campbell
2015-03-12 15:19 ` Julien Grall
2015-03-12 15:43 ` Tamas K Lengyel
2015-03-06 21:24 ` [PATCH V13 7/7] tools/libxc: Allocate magic page for mem access " Tamas K Lengyel
2015-03-12 13:36 ` Ian Campbell
2015-03-12 11:30 ` [PATCH V13 0/7] Mem_access for ARM Ian Campbell
2015-03-12 12:24 ` Tamas K Lengyel
2015-03-12 13:53 ` Ian Campbell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1426167305.21353.423.camel@citrix.com \
--to=ian.campbell@citrix.com \
--cc=ian.jackson@eu.citrix.com \
--cc=jbeulich@suse.com \
--cc=julien.grall@linaro.org \
--cc=keir@xen.org \
--cc=stefano.stabellini@citrix.com \
--cc=stefano.stabellini@eu.citrix.com \
--cc=tim@xen.org \
--cc=tklengyel@sec.in.tum.de \
--cc=wei.liu2@citrix.com \
--cc=xen-devel@lists.xen.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.