/* Returns: mfn for the given (hvm guest) vaddr */ static mfn_t dbg_hvm_va2mfn(dbgva_t vaddr, struct domain *dp, int toaddr, gfn_t *gfn) { mfn_t mfn; uint32_t pfec = PFEC_page_present; p2m_type_t gfntype; DBGP2("vaddr:%lx domid:%d\n", vaddr, dp->domain_id); *gfn = _gfn(paging_gva_to_gfn(dp->vcpu[0], vaddr, &pfec)); if ( gfn_eq(*gfn, INVALID_GFN) ) { DBGP2("kdb:bad gfn from gva_to_gfn\n"); return INVALID_MFN; } mfn = get_gfn(dp, gfn_x(*gfn), &gfntype); if ( p2m_is_readonly(gfntype) && toaddr ) { DBGP2("kdb:p2m_is_readonly: gfntype:%x\n", gfntype); mfn = INVALID_MFN; } else DBGP2("X: vaddr:%lx domid:%d mfn:%#"PRI_mfn"\n", vaddr, dp->domain_id, mfn_x(mfn)); if ( mfn_eq(mfn, INVALID_MFN) ) { put_gfn(dp, gfn_x(*gfn)); *gfn = INVALID_GFN; } return mfn; }
/* * Get access type for a gfn. * If gfn == INVALID_GFN, gets the default access type. */ static int _p2m_get_mem_access(struct p2m_domain *p2m, gfn_t gfn, xenmem_access_t *access) { p2m_type_t t; p2m_access_t a; mfn_t mfn; static const xenmem_access_t memaccess[] = { #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 request to get default access. */ if ( gfn_eq(gfn, INVALID_GFN) ) { *access = memaccess[p2m->default_access]; return 0; } gfn_lock(p2m, gfn, 0); mfn = p2m->get_entry(p2m, gfn_x(gfn), &t, &a, 0, NULL, NULL); gfn_unlock(p2m, gfn, 0); if ( mfn_eq(mfn, INVALID_MFN) ) return -ESRCH; if ( (unsigned int)a >= ARRAY_SIZE(memaccess) ) return -ERANGE; *access = memaccess[a]; return 0; }
/* * If mem_access is in use it might have been the reason why get_page_from_gva * failed to fetch the page, as it uses the MMU for the permission checking. * Only in these cases we do a software-based type check and fetch the page if * we indeed found a conflicting mem_access setting. */ struct page_info* p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag, const struct vcpu *v) { long rc; paddr_t ipa; gfn_t gfn; mfn_t mfn; xenmem_access_t xma; p2m_type_t t; struct page_info *page = NULL; struct p2m_domain *p2m = &v->domain->arch.p2m; rc = gva_to_ipa(gva, &ipa, flag); if ( rc < 0 ) goto err; gfn = _gfn(paddr_to_pfn(ipa)); /* * We do this first as this is faster in the default case when no * permission is set on the page. */ rc = __p2m_get_mem_access(v->domain, gfn, &xma); if ( rc < 0 ) goto err; /* Let's check if mem_access limited the access. */ switch ( xma ) { default: case XENMEM_access_rwx: case XENMEM_access_rw: /* * If mem_access contains no rw perm restrictions at all then the original * fault was correct. */ goto err; case XENMEM_access_n2rwx: case XENMEM_access_n: case XENMEM_access_x: /* * If no r/w is permitted by mem_access, this was a fault caused by mem_access. */ break; case XENMEM_access_wx: case XENMEM_access_w: /* * If this was a read then it was because of mem_access, but if it was * a write then the original get_page_from_gva fault was correct. */ if ( flag == GV2M_READ ) break; else goto err; case XENMEM_access_rx2rw: case XENMEM_access_rx: case XENMEM_access_r: /* * If this was a write then it was because of mem_access, but if it was * a read then the original get_page_from_gva fault was correct. */ if ( flag == GV2M_WRITE ) break; else goto err; } /* * We had a mem_access permission limiting the access, but the page type * could also be limiting, so we need to check that as well. */ mfn = p2m_get_entry(p2m, gfn, &t, NULL, NULL); if ( mfn_eq(mfn, INVALID_MFN) ) goto err; if ( !mfn_valid(mfn) ) goto err; /* * Base type doesn't allow r/w */ if ( t != p2m_ram_rw ) goto err; page = mfn_to_page(mfn_x(mfn)); if ( unlikely(!get_page(page, v->domain)) ) page = NULL; err: return page; }
/* * Set access type for a region of pfns. * If gfn == INVALID_GFN, sets the default access type. */ long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr, uint32_t start, uint32_t mask, xenmem_access_t access, unsigned int altp2m_idx) { struct p2m_domain *p2m = p2m_get_hostp2m(d); p2m_access_t a; unsigned int order; 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 ( gfn_eq(gfn, INVALID_GFN) ) { p2m->default_access = a; return 0; } p2m_write_lock(p2m); for ( gfn = gfn_add(gfn, start); nr > start; gfn = gfn_next_boundary(gfn, order) ) { p2m_type_t t; mfn_t mfn = p2m_get_entry(p2m, gfn, &t, NULL, &order); if ( !mfn_eq(mfn, INVALID_MFN) ) { order = 0; rc = p2m_set_entry(p2m, gfn, 1, mfn, t, a); if ( rc ) break; } start += gfn_x(gfn_next_boundary(gfn, order)) - gfn_x(gfn); /* Check for continuation if it is not the last iteration */ if ( nr > start && !(start & mask) && hypercall_preempt_check() ) { rc = start; break; } } p2m_write_unlock(p2m); return rc; }
static int __p2m_get_mem_access(struct domain *d, gfn_t gfn, xenmem_access_t *access) { struct p2m_domain *p2m = p2m_get_hostp2m(d); void *i; unsigned int index; static const xenmem_access_t memaccess[] = { #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 }; ASSERT(p2m_is_locked(p2m)); /* 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 ( gfn_eq(gfn, INVALID_GFN) ) { *access = memaccess[p2m->default_access]; return 0; } i = radix_tree_lookup(&p2m->mem_access_settings, gfn_x(gfn)); if ( !i ) { /* * No setting was found in the Radix tree. Check if the * entry exists in the page-tables. */ mfn_t mfn = p2m_get_entry(p2m, gfn, NULL, NULL, NULL); if ( mfn_eq(mfn, INVALID_MFN) ) return -ESRCH; /* If entry exists then its rwx. */ *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; }