diff mbox series

[V3,17/30] x86/sgx: Support modifying SGX page type

Message ID bac6e77fe04100d6bfebd4e13aa9b916f82bfea6.1648847675.git.reinette.chatre@intel.com (mailing list archive)
State New, archived
Headers show
Series x86/sgx and selftests/sgx: Support SGX2 | expand

Commit Message

Reinette Chatre April 4, 2022, 4:49 p.m. UTC
Every enclave contains one or more Thread Control Structures (TCS). The
TCS contains meta-data used by the hardware to save and restore thread
specific information when entering/exiting the enclave. With SGX1 an
enclave needs to be created with enough TCSs to support the largest
number of threads expecting to use the enclave and enough enclave pages
to meet all its anticipated memory demands. In SGX1 all pages remain in
the enclave until the enclave is unloaded.

SGX2 introduces a new function, ENCLS[EMODT], that is used to change
the type of an enclave page from a regular (SGX_PAGE_TYPE_REG) enclave
page to a TCS (SGX_PAGE_TYPE_TCS) page or change the type from a
regular (SGX_PAGE_TYPE_REG) or TCS (SGX_PAGE_TYPE_TCS)
page to a trimmed (SGX_PAGE_TYPE_TRIM) page (setting it up for later
removal).

With the existing support of dynamically adding regular enclave pages
to an initialized enclave and changing the page type to TCS it is
possible to dynamically increase the number of threads supported by an
enclave.

Changing the enclave page type to SGX_PAGE_TYPE_TRIM is the first step
of dynamically removing pages from an initialized enclave. The complete
page removal flow is:
1) Change the type of the pages to be removed to SGX_PAGE_TYPE_TRIM
   using the SGX_IOC_ENCLAVE_MODIFY_TYPE ioctl() introduced here.
2) Approve the page removal by running ENCLU[EACCEPT] from within
   the enclave.
3) Initiate actual page removal using the ioctl() introduced in the
   following patch.

Add ioctl() SGX_IOC_ENCLAVE_MODIFY_TYPE to support changing SGX
enclave page types within an initialized enclave. With
SGX_IOC_ENCLAVE_MODIFY_TYPE the user specifies a page range and the
enclave page type to be applied to all pages in the provided range.
The ioctl() itself can return an error code based on failures
encountered by the kernel. It is also possible for SGX specific
failures to be encountered.  Add a result output parameter to
communicate the SGX return code. It is possible for the enclave page
type change request to fail on any page within the provided range.
Support partial success by returning the number of pages that were
successfully changed.

After the page type is changed the page continues to be accessible
from the kernel perspective with page table entries and internal
state. The page may be moved to swap. Any access until ENCLU[EACCEPT]
will encounter a page fault with SGX flag set in error code.

Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
---
Changes since V2:
- Adjust ioctl number after removal of SGX_IOC_ENCLAVE_RELAX_PERMISSIONS.
- Remove attempt at runtime tracking of EPCM permissions
  (sgx_encl_page->vm_run_prot_bits). (Jarkko)
- Change names to follow guidance of using detailed names (Jarkko):
  struct sgx_enclave_modt -> struct sgx_enclave_modify_type
  sgx_enclave_modt() -> sgx_enclave_modify_type()
  sgx_ioc_enclave_modt() -> sgx_ioc_enclave_modify_type()

Changes since V1:
- Remove the "Earlier changes ..." paragraph (Jarkko).
- Change "new ioctl" text to "Add SGX_IOC_ENCLAVE_MOD_TYPE" (Jarkko).
- Discussion about EPCM interaction and the EPCM MODIFIED bit is moved
  to new patch that introduces the ENCLS[EMODT] wrapper while keeping
  the higher level discussion on page accessibility in
  this commit log (Jarkko).
- Rename SGX_IOC_PAGE_MODT ioctl() to SGX_IOC_ENCLAVE_MODIFY_TYPE
  (Jarkko).
- Rename struct sgx_page_modt to struct sgx_enclave_modt in support
  of ioctl() rename.
- Rename sgx_page_modt() to sgx_enclave_modt() and sgx_ioc_page_modt()
  to sgx_ioc_enclave_modt() in support of ioctl() rename.
- Provide secinfo as parameter to ioctl() instead of just
  page type (Jarkko).
- Update comments to refer to new ioctl() names.
- Use new SGX2 checking helper().
- Use ETRACK flow utility.
- Move kernel-doc to function that provides documentation for
  Documentation/x86/sgx.rst.
- Remove redundant comment.
- Use offset/length validation utility.
- Make explicit which members of struct sgx_enclave_modt are for
  output (Dave).

 arch/x86/include/uapi/asm/sgx.h |  20 +++
 arch/x86/kernel/cpu/sgx/ioctl.c | 209 ++++++++++++++++++++++++++++++++
 2 files changed, 229 insertions(+)

Comments

Jarkko Sakkinen April 5, 2022, 7:06 a.m. UTC | #1
On Mon, Apr 04, 2022 at 09:49:25AM -0700, Reinette Chatre wrote:
> Every enclave contains one or more Thread Control Structures (TCS). The
> TCS contains meta-data used by the hardware to save and restore thread
> specific information when entering/exiting the enclave. With SGX1 an
> enclave needs to be created with enough TCSs to support the largest
> number of threads expecting to use the enclave and enough enclave pages
> to meet all its anticipated memory demands. In SGX1 all pages remain in
> the enclave until the enclave is unloaded.
> 
> SGX2 introduces a new function, ENCLS[EMODT], that is used to change
> the type of an enclave page from a regular (SGX_PAGE_TYPE_REG) enclave
> page to a TCS (SGX_PAGE_TYPE_TCS) page or change the type from a
> regular (SGX_PAGE_TYPE_REG) or TCS (SGX_PAGE_TYPE_TCS)
> page to a trimmed (SGX_PAGE_TYPE_TRIM) page (setting it up for later
> removal).
> 
> With the existing support of dynamically adding regular enclave pages
> to an initialized enclave and changing the page type to TCS it is
> possible to dynamically increase the number of threads supported by an
> enclave.
> 
> Changing the enclave page type to SGX_PAGE_TYPE_TRIM is the first step
> of dynamically removing pages from an initialized enclave. The complete
> page removal flow is:
> 1) Change the type of the pages to be removed to SGX_PAGE_TYPE_TRIM
>    using the SGX_IOC_ENCLAVE_MODIFY_TYPE ioctl() introduced here.
> 2) Approve the page removal by running ENCLU[EACCEPT] from within
>    the enclave.
> 3) Initiate actual page removal using the ioctl() introduced in the
>    following patch.
> 
> Add ioctl() SGX_IOC_ENCLAVE_MODIFY_TYPE to support changing SGX
> enclave page types within an initialized enclave. With
> SGX_IOC_ENCLAVE_MODIFY_TYPE the user specifies a page range and the
> enclave page type to be applied to all pages in the provided range.
> The ioctl() itself can return an error code based on failures
> encountered by the kernel. It is also possible for SGX specific
> failures to be encountered.  Add a result output parameter to
> communicate the SGX return code. It is possible for the enclave page
> type change request to fail on any page within the provided range.
> Support partial success by returning the number of pages that were
> successfully changed.
> 
> After the page type is changed the page continues to be accessible
> from the kernel perspective with page table entries and internal
> state. The page may be moved to swap. Any access until ENCLU[EACCEPT]
> will encounter a page fault with SGX flag set in error code.
> 
> Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
> ---
> Changes since V2:
> - Adjust ioctl number after removal of SGX_IOC_ENCLAVE_RELAX_PERMISSIONS.
> - Remove attempt at runtime tracking of EPCM permissions
>   (sgx_encl_page->vm_run_prot_bits). (Jarkko)
> - Change names to follow guidance of using detailed names (Jarkko):
>   struct sgx_enclave_modt -> struct sgx_enclave_modify_type
>   sgx_enclave_modt() -> sgx_enclave_modify_type()
>   sgx_ioc_enclave_modt() -> sgx_ioc_enclave_modify_type()
> 
> Changes since V1:
> - Remove the "Earlier changes ..." paragraph (Jarkko).
> - Change "new ioctl" text to "Add SGX_IOC_ENCLAVE_MOD_TYPE" (Jarkko).
> - Discussion about EPCM interaction and the EPCM MODIFIED bit is moved
>   to new patch that introduces the ENCLS[EMODT] wrapper while keeping
>   the higher level discussion on page accessibility in
>   this commit log (Jarkko).
> - Rename SGX_IOC_PAGE_MODT ioctl() to SGX_IOC_ENCLAVE_MODIFY_TYPE
>   (Jarkko).
> - Rename struct sgx_page_modt to struct sgx_enclave_modt in support
>   of ioctl() rename.
> - Rename sgx_page_modt() to sgx_enclave_modt() and sgx_ioc_page_modt()
>   to sgx_ioc_enclave_modt() in support of ioctl() rename.
> - Provide secinfo as parameter to ioctl() instead of just
>   page type (Jarkko).
> - Update comments to refer to new ioctl() names.
> - Use new SGX2 checking helper().
> - Use ETRACK flow utility.
> - Move kernel-doc to function that provides documentation for
>   Documentation/x86/sgx.rst.
> - Remove redundant comment.
> - Use offset/length validation utility.
> - Make explicit which members of struct sgx_enclave_modt are for
>   output (Dave).
> 
>  arch/x86/include/uapi/asm/sgx.h |  20 +++
>  arch/x86/kernel/cpu/sgx/ioctl.c | 209 ++++++++++++++++++++++++++++++++
>  2 files changed, 229 insertions(+)
> 
> diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
> index a0a24e94fb27..529f4ab28410 100644
> --- a/arch/x86/include/uapi/asm/sgx.h
> +++ b/arch/x86/include/uapi/asm/sgx.h
> @@ -31,6 +31,8 @@ enum sgx_page_flags {
>  	_IO(SGX_MAGIC, 0x04)
>  #define SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS \
>  	_IOWR(SGX_MAGIC, 0x05, struct sgx_enclave_restrict_permissions)
> +#define SGX_IOC_ENCLAVE_MODIFY_TYPE \
> +	_IOWR(SGX_MAGIC, 0x06, struct sgx_enclave_modify_type)
>  
>  /**
>   * struct sgx_enclave_create - parameter structure for the
> @@ -97,6 +99,24 @@ struct sgx_enclave_restrict_permissions {
>  	__u64 count;
>  };
>  
> +/**
> + * struct sgx_enclave_modify_type - parameters for %SGX_IOC_ENCLAVE_MODIFY_TYPE
> + * @offset:	starting page offset (page aligned relative to enclave base
> + *		address defined in SECS)
> + * @length:	length of memory (multiple of the page size)
> + * @secinfo:	address for the SECINFO data containing the new type
> + *		for pages in range described by @offset and @length
> + * @result:	(output) SGX result code of ENCLS[EMODT] function
> + * @count:	(output) bytes successfully changed (multiple of page size)
> + */
> +struct sgx_enclave_modify_type {
> +	__u64 offset;
> +	__u64 length;
> +	__u64 secinfo;
> +	__u64 result;
> +	__u64 count;
> +};
> +
>  struct sgx_enclave_run;
>  
>  /**
> diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c
> index 4d88bfd163e7..6f769e67ec2d 100644
> --- a/arch/x86/kernel/cpu/sgx/ioctl.c
> +++ b/arch/x86/kernel/cpu/sgx/ioctl.c
> @@ -898,6 +898,212 @@ static long sgx_ioc_enclave_restrict_permissions(struct sgx_encl *encl,
>  	return ret;
>  }
>  
> +/**
> + * sgx_enclave_modify_type() - Modify type of SGX enclave pages
> + * @encl:	Enclave to which the pages belong.
> + * @modt:	Checked parameters from user about which pages need modifying.
> + * @page_type:	New page type.
> + *
> + * Return:
> + * - 0:		Success
> + * - -errno:	Otherwise
> + */
> +static long sgx_enclave_modify_type(struct sgx_encl *encl,
> +				    struct sgx_enclave_modify_type *modt,
> +				    enum sgx_page_type page_type)
> +{
> +	unsigned long max_prot_restore;
> +	struct sgx_encl_page *entry;
> +	struct sgx_secinfo secinfo;
> +	unsigned long prot;
> +	unsigned long addr;
> +	unsigned long c;
> +	void *epc_virt;
> +	int ret;
> +
> +	/*
> +	 * The only new page types allowed by hardware are PT_TCS and PT_TRIM.
> +	 */
> +	if (page_type != SGX_PAGE_TYPE_TCS && page_type != SGX_PAGE_TYPE_TRIM)
> +		return -EINVAL;
> +
> +	memset(&secinfo, 0, sizeof(secinfo));
> +
> +	secinfo.flags = page_type << 8;
> +
> +	for (c = 0 ; c < modt->length; c += PAGE_SIZE) {
> +		addr = encl->base + modt->offset + c;
> +
> +		mutex_lock(&encl->lock);
> +
> +		entry = sgx_encl_load_page(encl, addr);
> +		if (IS_ERR(entry)) {
> +			ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT;
> +			goto out_unlock;
> +		}
> +
> +		/*
> +		 * Borrow the logic from the Intel SDM. Regular pages
> +		 * (SGX_PAGE_TYPE_REG) can change type to SGX_PAGE_TYPE_TCS
> +		 * or SGX_PAGE_TYPE_TRIM but TCS pages can only be trimmed.
> +		 * CET pages not supported yet.
> +		 */
> +		if (!(entry->type == SGX_PAGE_TYPE_REG ||
> +		      (entry->type == SGX_PAGE_TYPE_TCS &&
> +		       page_type == SGX_PAGE_TYPE_TRIM))) {
> +			ret = -EINVAL;
> +			goto out_unlock;
> +		}
> +
> +		max_prot_restore = entry->vm_max_prot_bits;
> +
> +		/*
> +		 * Once a regular page becomes a TCS page it cannot be
> +		 * changed back. So the maximum allowed protection reflects
> +		 * the TCS page that is always RW from kernel perspective but
> +		 * will be inaccessible from within enclave. Before doing
> +		 * so, do make sure that the new page type continues to
> +		 * respect the originally vetted page permissions.
> +		 */
> +		if (entry->type == SGX_PAGE_TYPE_REG &&
> +		    page_type == SGX_PAGE_TYPE_TCS) {
> +			if (~entry->vm_max_prot_bits & (VM_READ | VM_WRITE)) {
> +				ret = -EPERM;
> +				goto out_unlock;
> +			}
> +			prot = PROT_READ | PROT_WRITE;
> +			entry->vm_max_prot_bits = calc_vm_prot_bits(prot, 0);
> +
> +			/*
> +			 * Prevent page from being reclaimed while mutex
> +			 * is released.
> +			 */
> +			if (sgx_unmark_page_reclaimable(entry->epc_page)) {
> +				ret = -EAGAIN;
> +				goto out_entry_changed;
> +			}
> +
> +			/*
> +			 * Do not keep encl->lock because of dependency on
> +			 * mmap_lock acquired in sgx_zap_enclave_ptes().
> +			 */
> +			mutex_unlock(&encl->lock);
> +
> +			sgx_zap_enclave_ptes(encl, addr);
> +
> +			mutex_lock(&encl->lock);
> +
> +			sgx_mark_page_reclaimable(entry->epc_page);
> +		}
> +
> +		/* Change EPC type */
> +		epc_virt = sgx_get_epc_virt_addr(entry->epc_page);
> +		ret = __emodt(&secinfo, epc_virt);
> +		if (encls_faulted(ret)) {
> +			/*
> +			 * All possible faults should be avoidable:
> +			 * parameters have been checked, will only change
> +			 * valid page types, and no concurrent
> +			 * SGX1/SGX2 ENCLS instructions since these are
> +			 * protected with mutex.
> +			 */
> +			pr_err_once("EMODT encountered exception %d\n",
> +				    ENCLS_TRAPNR(ret));
> +			ret = -EFAULT;
> +			goto out_entry_changed;
> +		}
> +		if (encls_failed(ret)) {
> +			modt->result = ret;
> +			ret = -EFAULT;
> +			goto out_entry_changed;
> +		}
> +
> +		ret = sgx_enclave_etrack(encl);
> +		if (ret) {
> +			ret = -EFAULT;
> +			goto out_unlock;
> +		}
> +
> +		entry->type = page_type;
> +
> +		mutex_unlock(&encl->lock);
> +	}
> +
> +	ret = 0;
> +	goto out;
> +
> +out_entry_changed:
> +	entry->vm_max_prot_bits = max_prot_restore;
> +out_unlock:
> +	mutex_unlock(&encl->lock);
> +out:
> +	modt->count = c;
> +
> +	return ret;
> +}
> +
> +/**
> + * sgx_ioc_enclave_modify_type() - handler for %SGX_IOC_ENCLAVE_MODIFY_TYPE
> + * @encl:	an enclave pointer
> + * @arg:	userspace pointer to a &struct sgx_enclave_modify_type instance
> + *
> + * Ability to change the enclave page type supports the following use cases:
> + *
> + * * It is possible to add TCS pages to an enclave by changing the type of
> + *   regular pages (%SGX_PAGE_TYPE_REG) to TCS (%SGX_PAGE_TYPE_TCS) pages.
> + *   With this support the number of threads supported by an initialized
> + *   enclave can be increased dynamically.
> + *
> + * * Regular or TCS pages can dynamically be removed from an initialized
> + *   enclave by changing the page type to %SGX_PAGE_TYPE_TRIM. Changing the
> + *   page type to %SGX_PAGE_TYPE_TRIM marks the page for removal with actual
> + *   removal done by handler of %SGX_IOC_ENCLAVE_REMOVE_PAGES ioctl() called
> + *   after ENCLU[EACCEPT] is run on %SGX_PAGE_TYPE_TRIM page from within the
> + *   enclave.
> + *
> + * Return:
> + * - 0:		Success
> + * - -errno:	Otherwise
> + */
> +static long sgx_ioc_enclave_modify_type(struct sgx_encl *encl, void __user *arg)
> +{
> +	struct sgx_enclave_modify_type params;
> +	enum sgx_page_type page_type;
> +	struct sgx_secinfo secinfo;
> +	long ret;
> +
> +	ret = sgx_ioc_sgx2_ready(encl);
> +	if (ret)
> +		return ret;
> +
> +	if (copy_from_user(&params, arg, sizeof(params)))
> +		return -EFAULT;
> +
> +	if (sgx_validate_offset_length(encl, params.offset, params.length))
> +		return -EINVAL;
> +
> +	if (copy_from_user(&secinfo, (void __user *)params.secinfo,
> +			   sizeof(secinfo)))
> +		return -EFAULT;
> +
> +	if (secinfo.flags & ~SGX_SECINFO_PAGE_TYPE_MASK)
> +		return -EINVAL;
> +
> +	if (memchr_inv(secinfo.reserved, 0, sizeof(secinfo.reserved)))
> +		return -EINVAL;
> +
> +	if (params.result || params.count)
> +		return -EINVAL;
> +
> +	page_type = (secinfo.flags & SGX_SECINFO_PAGE_TYPE_MASK) >> 8;
> +	ret = sgx_enclave_modify_type(encl, &params, page_type);
> +
> +	if (copy_to_user(arg, &params, sizeof(params)))
> +		return -EFAULT;
> +
> +	return ret;
> +}
> +
>  long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
>  {
>  	struct sgx_encl *encl = filep->private_data;
> @@ -923,6 +1129,9 @@ long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
>  		ret = sgx_ioc_enclave_restrict_permissions(encl,
>  							   (void __user *)arg);
>  		break;
> +	case SGX_IOC_ENCLAVE_MODIFY_TYPE:
> +		ret = sgx_ioc_enclave_modify_type(encl, (void __user *)arg);
> +		break;
>  	default:
>  		ret = -ENOIOCTLCMD;
>  		break;
> -- 
> 2.25.1
> 

To be coherent with other names, this should be
SGX_IOC_ENCLAVE_MODIFY_TYPES.

BR, Jarkko
Jarkko Sakkinen April 5, 2022, 3:34 p.m. UTC | #2
On Tue, 2022-04-05 at 10:06 +0300, Jarkko Sakkinen wrote:
> On Mon, Apr 04, 2022 at 09:49:25AM -0700, Reinette Chatre wrote:
> > Every enclave contains one or more Thread Control Structures (TCS). The
> > TCS contains meta-data used by the hardware to save and restore thread
> > specific information when entering/exiting the enclave. With SGX1 an
> > enclave needs to be created with enough TCSs to support the largest
> > number of threads expecting to use the enclave and enough enclave pages
> > to meet all its anticipated memory demands. In SGX1 all pages remain in
> > the enclave until the enclave is unloaded.
> > 
> > SGX2 introduces a new function, ENCLS[EMODT], that is used to change
> > the type of an enclave page from a regular (SGX_PAGE_TYPE_REG) enclave
> > page to a TCS (SGX_PAGE_TYPE_TCS) page or change the type from a
> > regular (SGX_PAGE_TYPE_REG) or TCS (SGX_PAGE_TYPE_TCS)
> > page to a trimmed (SGX_PAGE_TYPE_TRIM) page (setting it up for later
> > removal).
> > 
> > With the existing support of dynamically adding regular enclave pages
> > to an initialized enclave and changing the page type to TCS it is
> > possible to dynamically increase the number of threads supported by an
> > enclave.
> > 
> > Changing the enclave page type to SGX_PAGE_TYPE_TRIM is the first step
> > of dynamically removing pages from an initialized enclave. The complete
> > page removal flow is:
> > 1) Change the type of the pages to be removed to SGX_PAGE_TYPE_TRIM
> >    using the SGX_IOC_ENCLAVE_MODIFY_TYPE ioctl() introduced here.
> > 2) Approve the page removal by running ENCLU[EACCEPT] from within
> >    the enclave.
> > 3) Initiate actual page removal using the ioctl() introduced in the
> >    following patch.
> > 
> > Add ioctl() SGX_IOC_ENCLAVE_MODIFY_TYPE to support changing SGX
> > enclave page types within an initialized enclave. With
> > SGX_IOC_ENCLAVE_MODIFY_TYPE the user specifies a page range and the
> > enclave page type to be applied to all pages in the provided range.
> > The ioctl() itself can return an error code based on failures
> > encountered by the kernel. It is also possible for SGX specific
> > failures to be encountered.  Add a result output parameter to
> > communicate the SGX return code. It is possible for the enclave page
> > type change request to fail on any page within the provided range.
> > Support partial success by returning the number of pages that were
> > successfully changed.
> > 
> > After the page type is changed the page continues to be accessible
> > from the kernel perspective with page table entries and internal
> > state. The page may be moved to swap. Any access until ENCLU[EACCEPT]
> > will encounter a page fault with SGX flag set in error code.
> > 
> > Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
> > ---
> > Changes since V2:
> > - Adjust ioctl number after removal of SGX_IOC_ENCLAVE_RELAX_PERMISSIONS.
> > - Remove attempt at runtime tracking of EPCM permissions
> >   (sgx_encl_page->vm_run_prot_bits). (Jarkko)
> > - Change names to follow guidance of using detailed names (Jarkko):
> >   struct sgx_enclave_modt -> struct sgx_enclave_modify_type
> >   sgx_enclave_modt() -> sgx_enclave_modify_type()
> >   sgx_ioc_enclave_modt() -> sgx_ioc_enclave_modify_type()
> > 
> > Changes since V1:
> > - Remove the "Earlier changes ..." paragraph (Jarkko).
> > - Change "new ioctl" text to "Add SGX_IOC_ENCLAVE_MOD_TYPE" (Jarkko).
> > - Discussion about EPCM interaction and the EPCM MODIFIED bit is moved
> >   to new patch that introduces the ENCLS[EMODT] wrapper while keeping
> >   the higher level discussion on page accessibility in
> >   this commit log (Jarkko).
> > - Rename SGX_IOC_PAGE_MODT ioctl() to SGX_IOC_ENCLAVE_MODIFY_TYPE
> >   (Jarkko).
> > - Rename struct sgx_page_modt to struct sgx_enclave_modt in support
> >   of ioctl() rename.
> > - Rename sgx_page_modt() to sgx_enclave_modt() and sgx_ioc_page_modt()
> >   to sgx_ioc_enclave_modt() in support of ioctl() rename.
> > - Provide secinfo as parameter to ioctl() instead of just
> >   page type (Jarkko).
> > - Update comments to refer to new ioctl() names.
> > - Use new SGX2 checking helper().
> > - Use ETRACK flow utility.
> > - Move kernel-doc to function that provides documentation for
> >   Documentation/x86/sgx.rst.
> > - Remove redundant comment.
> > - Use offset/length validation utility.
> > - Make explicit which members of struct sgx_enclave_modt are for
> >   output (Dave).
> > 
> >  arch/x86/include/uapi/asm/sgx.h |  20 +++
> >  arch/x86/kernel/cpu/sgx/ioctl.c | 209 ++++++++++++++++++++++++++++++++
> >  2 files changed, 229 insertions(+)
> > 
> > diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
> > index a0a24e94fb27..529f4ab28410 100644
> > --- a/arch/x86/include/uapi/asm/sgx.h
> > +++ b/arch/x86/include/uapi/asm/sgx.h
> > @@ -31,6 +31,8 @@ enum sgx_page_flags {
> >         _IO(SGX_MAGIC, 0x04)
> >  #define SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS \
> >         _IOWR(SGX_MAGIC, 0x05, struct sgx_enclave_restrict_permissions)
> > +#define SGX_IOC_ENCLAVE_MODIFY_TYPE \
> > +       _IOWR(SGX_MAGIC, 0x06, struct sgx_enclave_modify_type)
> >  
> >  /**
> >   * struct sgx_enclave_create - parameter structure for the
> > @@ -97,6 +99,24 @@ struct sgx_enclave_restrict_permissions {
> >         __u64 count;
> >  };
> >  
> > +/**
> > + * struct sgx_enclave_modify_type - parameters for %SGX_IOC_ENCLAVE_MODIFY_TYPE
> > + * @offset:    starting page offset (page aligned relative to enclave base
> > + *             address defined in SECS)
> > + * @length:    length of memory (multiple of the page size)
> > + * @secinfo:   address for the SECINFO data containing the new type
> > + *             for pages in range described by @offset and @length
> > + * @result:    (output) SGX result code of ENCLS[EMODT] function
> > + * @count:     (output) bytes successfully changed (multiple of page size)
> > + */
> > +struct sgx_enclave_modify_type {
> > +       __u64 offset;
> > +       __u64 length;
> > +       __u64 secinfo;
> > +       __u64 result;
> > +       __u64 count;
> > +};
> > +
> >  struct sgx_enclave_run;
> >  
> >  /**
> > diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c
> > index 4d88bfd163e7..6f769e67ec2d 100644
> > --- a/arch/x86/kernel/cpu/sgx/ioctl.c
> > +++ b/arch/x86/kernel/cpu/sgx/ioctl.c
> > @@ -898,6 +898,212 @@ static long sgx_ioc_enclave_restrict_permissions(struct sgx_encl *encl,
> >         return ret;
> >  }
> >  
> > +/**
> > + * sgx_enclave_modify_type() - Modify type of SGX enclave pages
> > + * @encl:      Enclave to which the pages belong.
> > + * @modt:      Checked parameters from user about which pages need modifying.
> > + * @page_type: New page type.
> > + *
> > + * Return:
> > + * - 0:                Success
> > + * - -errno:   Otherwise
> > + */
> > +static long sgx_enclave_modify_type(struct sgx_encl *encl,
> > +                                   struct sgx_enclave_modify_type *modt,
> > +                                   enum sgx_page_type page_type)
> > +{
> > +       unsigned long max_prot_restore;
> > +       struct sgx_encl_page *entry;
> > +       struct sgx_secinfo secinfo;
> > +       unsigned long prot;
> > +       unsigned long addr;
> > +       unsigned long c;
> > +       void *epc_virt;
> > +       int ret;
> > +
> > +       /*
> > +        * The only new page types allowed by hardware are PT_TCS and PT_TRIM.
> > +        */
> > +       if (page_type != SGX_PAGE_TYPE_TCS && page_type != SGX_PAGE_TYPE_TRIM)
> > +               return -EINVAL;
> > +
> > +       memset(&secinfo, 0, sizeof(secinfo));
> > +
> > +       secinfo.flags = page_type << 8;
> > +
> > +       for (c = 0 ; c < modt->length; c += PAGE_SIZE) {
> > +               addr = encl->base + modt->offset + c;
> > +
> > +               mutex_lock(&encl->lock);
> > +
> > +               entry = sgx_encl_load_page(encl, addr);
> > +               if (IS_ERR(entry)) {
> > +                       ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT;
> > +                       goto out_unlock;
> > +               }
> > +
> > +               /*
> > +                * Borrow the logic from the Intel SDM. Regular pages
> > +                * (SGX_PAGE_TYPE_REG) can change type to SGX_PAGE_TYPE_TCS
> > +                * or SGX_PAGE_TYPE_TRIM but TCS pages can only be trimmed.
> > +                * CET pages not supported yet.
> > +                */
> > +               if (!(entry->type == SGX_PAGE_TYPE_REG ||
> > +                     (entry->type == SGX_PAGE_TYPE_TCS &&
> > +                      page_type == SGX_PAGE_TYPE_TRIM))) {
> > +                       ret = -EINVAL;
> > +                       goto out_unlock;
> > +               }
> > +
> > +               max_prot_restore = entry->vm_max_prot_bits;
> > +
> > +               /*
> > +                * Once a regular page becomes a TCS page it cannot be
> > +                * changed back. So the maximum allowed protection reflects
> > +                * the TCS page that is always RW from kernel perspective but
> > +                * will be inaccessible from within enclave. Before doing
> > +                * so, do make sure that the new page type continues to
> > +                * respect the originally vetted page permissions.
> > +                */
> > +               if (entry->type == SGX_PAGE_TYPE_REG &&
> > +                   page_type == SGX_PAGE_TYPE_TCS) {
> > +                       if (~entry->vm_max_prot_bits & (VM_READ | VM_WRITE)) {
> > +                               ret = -EPERM;
> > +                               goto out_unlock;
> > +                       }
> > +                       prot = PROT_READ | PROT_WRITE;
> > +                       entry->vm_max_prot_bits = calc_vm_prot_bits(prot, 0);
> > +
> > +                       /*
> > +                        * Prevent page from being reclaimed while mutex
> > +                        * is released.
> > +                        */
> > +                       if (sgx_unmark_page_reclaimable(entry->epc_page)) {
> > +                               ret = -EAGAIN;
> > +                               goto out_entry_changed;
> > +                       }
> > +
> > +                       /*
> > +                        * Do not keep encl->lock because of dependency on
> > +                        * mmap_lock acquired in sgx_zap_enclave_ptes().
> > +                        */
> > +                       mutex_unlock(&encl->lock);
> > +
> > +                       sgx_zap_enclave_ptes(encl, addr);
> > +
> > +                       mutex_lock(&encl->lock);
> > +
> > +                       sgx_mark_page_reclaimable(entry->epc_page);
> > +               }
> > +
> > +               /* Change EPC type */
> > +               epc_virt = sgx_get_epc_virt_addr(entry->epc_page);
> > +               ret = __emodt(&secinfo, epc_virt);
> > +               if (encls_faulted(ret)) {
> > +                       /*
> > +                        * All possible faults should be avoidable:
> > +                        * parameters have been checked, will only change
> > +                        * valid page types, and no concurrent
> > +                        * SGX1/SGX2 ENCLS instructions since these are
> > +                        * protected with mutex.
> > +                        */
> > +                       pr_err_once("EMODT encountered exception %d\n",
> > +                                   ENCLS_TRAPNR(ret));
> > +                       ret = -EFAULT;
> > +                       goto out_entry_changed;
> > +               }
> > +               if (encls_failed(ret)) {
> > +                       modt->result = ret;
> > +                       ret = -EFAULT;
> > +                       goto out_entry_changed;
> > +               }
> > +
> > +               ret = sgx_enclave_etrack(encl);
> > +               if (ret) {
> > +                       ret = -EFAULT;
> > +                       goto out_unlock;
> > +               }
> > +
> > +               entry->type = page_type;
> > +
> > +               mutex_unlock(&encl->lock);
> > +       }
> > +
> > +       ret = 0;
> > +       goto out;
> > +
> > +out_entry_changed:
> > +       entry->vm_max_prot_bits = max_prot_restore;
> > +out_unlock:
> > +       mutex_unlock(&encl->lock);
> > +out:
> > +       modt->count = c;
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * sgx_ioc_enclave_modify_type() - handler for %SGX_IOC_ENCLAVE_MODIFY_TYPE
> > + * @encl:      an enclave pointer
> > + * @arg:       userspace pointer to a &struct sgx_enclave_modify_type instance
> > + *
> > + * Ability to change the enclave page type supports the following use cases:
> > + *
> > + * * It is possible to add TCS pages to an enclave by changing the type of
> > + *   regular pages (%SGX_PAGE_TYPE_REG) to TCS (%SGX_PAGE_TYPE_TCS) pages.
> > + *   With this support the number of threads supported by an initialized
> > + *   enclave can be increased dynamically.
> > + *
> > + * * Regular or TCS pages can dynamically be removed from an initialized
> > + *   enclave by changing the page type to %SGX_PAGE_TYPE_TRIM. Changing the
> > + *   page type to %SGX_PAGE_TYPE_TRIM marks the page for removal with actual
> > + *   removal done by handler of %SGX_IOC_ENCLAVE_REMOVE_PAGES ioctl() called
> > + *   after ENCLU[EACCEPT] is run on %SGX_PAGE_TYPE_TRIM page from within the
> > + *   enclave.
> > + *
> > + * Return:
> > + * - 0:                Success
> > + * - -errno:   Otherwise
> > + */
> > +static long sgx_ioc_enclave_modify_type(struct sgx_encl *encl, void __user *arg)
> > +{
> > +       struct sgx_enclave_modify_type params;
> > +       enum sgx_page_type page_type;
> > +       struct sgx_secinfo secinfo;
> > +       long ret;
> > +
> > +       ret = sgx_ioc_sgx2_ready(encl);
> > +       if (ret)
> > +               return ret;
> > +
> > +       if (copy_from_user(&params, arg, sizeof(params)))
> > +               return -EFAULT;
> > +
> > +       if (sgx_validate_offset_length(encl, params.offset, params.length))
> > +               return -EINVAL;
> > +
> > +       if (copy_from_user(&secinfo, (void __user *)params.secinfo,
> > +                          sizeof(secinfo)))
> > +               return -EFAULT;
> > +
> > +       if (secinfo.flags & ~SGX_SECINFO_PAGE_TYPE_MASK)
> > +               return -EINVAL;
> > +
> > +       if (memchr_inv(secinfo.reserved, 0, sizeof(secinfo.reserved)))
> > +               return -EINVAL;
> > +
> > +       if (params.result || params.count)
> > +               return -EINVAL;
> > +
> > +       page_type = (secinfo.flags & SGX_SECINFO_PAGE_TYPE_MASK) >> 8;
> > +       ret = sgx_enclave_modify_type(encl, &params, page_type);
> > +
> > +       if (copy_to_user(arg, &params, sizeof(params)))
> > +               return -EFAULT;
> > +
> > +       return ret;
> > +}
> > +
> >  long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
> >  {
> >         struct sgx_encl *encl = filep->private_data;
> > @@ -923,6 +1129,9 @@ long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
> >                 ret = sgx_ioc_enclave_restrict_permissions(encl,
> >                                                            (void __user *)arg);
> >                 break;
> > +       case SGX_IOC_ENCLAVE_MODIFY_TYPE:
> > +               ret = sgx_ioc_enclave_modify_type(encl, (void __user *)arg);
> > +               break;
> >         default:
> >                 ret = -ENOIOCTLCMD;
> >                 break;
> > -- 
> > 2.25.1
> > 
> 
> To be coherent with other names, this should be
> SGX_IOC_ENCLAVE_MODIFY_TYPES.

This should take only page type given that flags are zeroed:

EPCM(DS:RCX).R := 0;
EPCM(DS:RCX).W := 0;
EPCM(DS:RCX).X := 0; 

BR, Jarkko
Reinette Chatre April 5, 2022, 5:05 p.m. UTC | #3
Hi Jarkko,

On 4/5/2022 8:34 AM, Jarkko Sakkinen wrote:
> On Tue, 2022-04-05 at 10:06 +0300, Jarkko Sakkinen wrote:

>>>
>>
>> To be coherent with other names, this should be
>> SGX_IOC_ENCLAVE_MODIFY_TYPES.

This is not such a clear change request to me:

SGX_IOC_ENCLAVE_ADD_PAGES - add multiple pages
SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS - restrict multiple permissions
SGX_IOC_ENCLAVE_REMOVE_PAGES - remove multiple pages
SGX_IOC_ENCLAVE_MODIFY_TYPE - set a single type

Perhaps it should rather be SGX_IOC_ENCLAVE_SET_TYPE to indicate that
there is a single target type as opposed to the possibility
of multiple source types (TCS and regular pages can be trimmed).

> 
> This should take only page type given that flags are zeroed:
> 
> EPCM(DS:RCX).R := 0;
> EPCM(DS:RCX).W := 0;
> EPCM(DS:RCX).X := 0; 
> 

ok, this was how it was done in V1 [1] and I can go back to that.


Reinette

[1] https://lore.kernel.org/linux-sgx/c0f04a8f7e1afd9e9319bb9f283db9a3187f7abc.1638381245.git.reinette.chatre@intel.com/
Jarkko Sakkinen April 5, 2022, 6:41 p.m. UTC | #4
On Tue, 2022-04-05 at 10:05 -0700, Reinette Chatre wrote:
> Hi Jarkko,
> 
> On 4/5/2022 8:34 AM, Jarkko Sakkinen wrote:
> > On Tue, 2022-04-05 at 10:06 +0300, Jarkko Sakkinen wrote:
> 
> > > > 
> > > 
> > > To be coherent with other names, this should be
> > > SGX_IOC_ENCLAVE_MODIFY_TYPES.
> 
> This is not such a clear change request to me:
> 
> SGX_IOC_ENCLAVE_ADD_PAGES - add multiple pages
> SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS - restrict multiple permissions
> SGX_IOC_ENCLAVE_REMOVE_PAGES - remove multiple pages
> SGX_IOC_ENCLAVE_MODIFY_TYPE - set a single type
> 
> Perhaps it should rather be SGX_IOC_ENCLAVE_SET_TYPE to indicate that
> there is a single target type as opposed to the possibility
> of multiple source types (TCS and regular pages can be trimmed).
> 
> > 
> > This should take only page type given that flags are zeroed:
> > 
> > EPCM(DS:RCX).R := 0;
> > EPCM(DS:RCX).W := 0;
> > EPCM(DS:RCX).X := 0; 
> > 
> 
> ok, this was how it was done in V1 [1] and I can go back to that.

I would name the fields as "flags" and "page_type" just to align
names with SGX instead of trying to mimim "posix names". Otherwise, 
I support that.

BR, Jarkko
Reinette Chatre April 5, 2022, 6:59 p.m. UTC | #5
Hi Jarkko,

On 4/5/2022 11:41 AM, Jarkko Sakkinen wrote:
> On Tue, 2022-04-05 at 10:05 -0700, Reinette Chatre wrote:
>> Hi Jarkko,
>>
>> On 4/5/2022 8:34 AM, Jarkko Sakkinen wrote:
>>> On Tue, 2022-04-05 at 10:06 +0300, Jarkko Sakkinen wrote:
>>
>>>>>
>>>>
>>>> To be coherent with other names, this should be
>>>> SGX_IOC_ENCLAVE_MODIFY_TYPES.
>>
>> This is not such a clear change request to me:
>>
>> SGX_IOC_ENCLAVE_ADD_PAGES - add multiple pages
>> SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS - restrict multiple permissions
>> SGX_IOC_ENCLAVE_REMOVE_PAGES - remove multiple pages
>> SGX_IOC_ENCLAVE_MODIFY_TYPE - set a single type
>>
>> Perhaps it should rather be SGX_IOC_ENCLAVE_SET_TYPE to indicate that
>> there is a single target type as opposed to the possibility
>> of multiple source types (TCS and regular pages can be trimmed).
>>
>>>

What is your opinion about what the ioctl() name should be? I prefer
to obtain a confirmation from you since you originally [1] requested
SGX_IOC_ENCLAVE_MODIFY_TYPE.

>>> This should take only page type given that flags are zeroed:
>>>
>>> EPCM(DS:RCX).R := 0;
>>> EPCM(DS:RCX).W := 0;
>>> EPCM(DS:RCX).X := 0; 
>>>
>>
>> ok, this was how it was done in V1 [1] and I can go back to that.
> 
> I would name the fields as "flags" and "page_type" just to align
> names with SGX instead of trying to mimim "posix names". Otherwise, 
> I support that.

I will move this ioctl() to use "page_type" instead of "secinfo"
within struct sgx_enclave_modify_type.

Your guidance of "flags" is not clear to me. I assume that you
refer to the field for struct sgx_enclave_restrict_permissions
where I think "permissions" to only contain the new permissions
would be more appropriate. None of the other values in
secinfo.flags are relevant.

Reinette

[1] https://lore.kernel.org/linux-sgx/Yav9g4+L8zg48DRf@iki.fi/
Jarkko Sakkinen April 6, 2022, 7:32 a.m. UTC | #6
On Tue, 2022-04-05 at 11:59 -0700, Reinette Chatre wrote:
> Hi Jarkko,
> 
> On 4/5/2022 11:41 AM, Jarkko Sakkinen wrote:
> > On Tue, 2022-04-05 at 10:05 -0700, Reinette Chatre wrote:
> > > Hi Jarkko,
> > > 
> > > On 4/5/2022 8:34 AM, Jarkko Sakkinen wrote:
> > > > On Tue, 2022-04-05 at 10:06 +0300, Jarkko Sakkinen wrote:
> > > 
> > > > > > 
> > > > > 
> > > > > To be coherent with other names, this should be
> > > > > SGX_IOC_ENCLAVE_MODIFY_TYPES.
> > > 
> > > This is not such a clear change request to me:
> > > 
> > > SGX_IOC_ENCLAVE_ADD_PAGES - add multiple pages
> > > SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS - restrict multiple permissions
> > > SGX_IOC_ENCLAVE_REMOVE_PAGES - remove multiple pages
> > > SGX_IOC_ENCLAVE_MODIFY_TYPE - set a single type
> > > 
> > > Perhaps it should rather be SGX_IOC_ENCLAVE_SET_TYPE to indicate that
> > > there is a single target type as opposed to the possibility
> > > of multiple source types (TCS and regular pages can be trimmed).
> > > 
> > > > 
> 
> What is your opinion about what the ioctl() name should be? I prefer
> to obtain a confirmation from you since you originally [1] requested
> SGX_IOC_ENCLAVE_MODIFY_TYPE.

s/TYPE/TYPES/g i.e. SGX_IOC_ENCLAVE_MODIFY_TYPES is fine.
> 
> > > > This should take only page type given that flags are zeroed:
> > > > 
> > > > EPCM(DS:RCX).R := 0;
> > > > EPCM(DS:RCX).W := 0;
> > > > EPCM(DS:RCX).X := 0; 
> > > > 
> > > 
> > > ok, this was how it was done in V1 [1] and I can go back to that.
> > 
> > I would name the fields as "flags" and "page_type" just to align
> > names with SGX instead of trying to mimim "posix names". Otherwise, 
> > I support that.
> 
> I will move this ioctl() to use "page_type" instead of "secinfo"
> within struct sgx_enclave_modify_type.
> 
> Your guidance of "flags" is not clear to me. I assume that you
> refer to the field for struct sgx_enclave_restrict_permissions
> where I think "permissions" to only contain the new permissions
> would be more appropriate. None of the other values in
> secinfo.flags are relevant.

I'm fine with your permissions field to the restrict ioctl.

> 
> Reinette
> 
> [1] https://lore.kernel.org/linux-sgx/Yav9g4+L8zg48DRf@iki.fi/
> 

BR, Jarkko
Reinette Chatre April 6, 2022, 5:50 p.m. UTC | #7
Hi Jarkko,

On 4/6/2022 12:32 AM, Jarkko Sakkinen wrote:
> On Tue, 2022-04-05 at 11:59 -0700, Reinette Chatre wrote:
>> Hi Jarkko,
>>
>> On 4/5/2022 11:41 AM, Jarkko Sakkinen wrote:
>>> On Tue, 2022-04-05 at 10:05 -0700, Reinette Chatre wrote:
>>>> Hi Jarkko,
>>>>
>>>> On 4/5/2022 8:34 AM, Jarkko Sakkinen wrote:
>>>>> On Tue, 2022-04-05 at 10:06 +0300, Jarkko Sakkinen wrote:
>>>>
>>>>>>>
>>>>>>
>>>>>> To be coherent with other names, this should be
>>>>>> SGX_IOC_ENCLAVE_MODIFY_TYPES.
>>>>
>>>> This is not such a clear change request to me:
>>>>
>>>> SGX_IOC_ENCLAVE_ADD_PAGES - add multiple pages
>>>> SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS - restrict multiple permissions
>>>> SGX_IOC_ENCLAVE_REMOVE_PAGES - remove multiple pages
>>>> SGX_IOC_ENCLAVE_MODIFY_TYPE - set a single type
>>>>
>>>> Perhaps it should rather be SGX_IOC_ENCLAVE_SET_TYPE to indicate that
>>>> there is a single target type as opposed to the possibility
>>>> of multiple source types (TCS and regular pages can be trimmed).
>>>>
>>>>>
>>
>> What is your opinion about what the ioctl() name should be? I prefer
>> to obtain a confirmation from you since you originally [1] requested
>> SGX_IOC_ENCLAVE_MODIFY_TYPE.
> 
> s/TYPE/TYPES/g i.e. SGX_IOC_ENCLAVE_MODIFY_TYPES is fine.

ok, thank you for confirming, will do.

>>
>>>>> This should take only page type given that flags are zeroed:
>>>>>
>>>>> EPCM(DS:RCX).R := 0;
>>>>> EPCM(DS:RCX).W := 0;
>>>>> EPCM(DS:RCX).X := 0; 
>>>>>
>>>>
>>>> ok, this was how it was done in V1 [1] and I can go back to that.
>>>
>>> I would name the fields as "flags" and "page_type" just to align
>>> names with SGX instead of trying to mimim "posix names". Otherwise, 
>>> I support that.
>>
>> I will move this ioctl() to use "page_type" instead of "secinfo"
>> within struct sgx_enclave_modify_type.
>>
>> Your guidance of "flags" is not clear to me. I assume that you
>> refer to the field for struct sgx_enclave_restrict_permissions
>> where I think "permissions" to only contain the new permissions
>> would be more appropriate. None of the other values in
>> secinfo.flags are relevant.
> 
> I'm fine with your permissions field to the restrict ioctl.

Will do, thank you.

Reinette
diff mbox series

Patch

diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
index a0a24e94fb27..529f4ab28410 100644
--- a/arch/x86/include/uapi/asm/sgx.h
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -31,6 +31,8 @@  enum sgx_page_flags {
 	_IO(SGX_MAGIC, 0x04)
 #define SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS \
 	_IOWR(SGX_MAGIC, 0x05, struct sgx_enclave_restrict_permissions)
+#define SGX_IOC_ENCLAVE_MODIFY_TYPE \
+	_IOWR(SGX_MAGIC, 0x06, struct sgx_enclave_modify_type)
 
 /**
  * struct sgx_enclave_create - parameter structure for the
@@ -97,6 +99,24 @@  struct sgx_enclave_restrict_permissions {
 	__u64 count;
 };
 
+/**
+ * struct sgx_enclave_modify_type - parameters for %SGX_IOC_ENCLAVE_MODIFY_TYPE
+ * @offset:	starting page offset (page aligned relative to enclave base
+ *		address defined in SECS)
+ * @length:	length of memory (multiple of the page size)
+ * @secinfo:	address for the SECINFO data containing the new type
+ *		for pages in range described by @offset and @length
+ * @result:	(output) SGX result code of ENCLS[EMODT] function
+ * @count:	(output) bytes successfully changed (multiple of page size)
+ */
+struct sgx_enclave_modify_type {
+	__u64 offset;
+	__u64 length;
+	__u64 secinfo;
+	__u64 result;
+	__u64 count;
+};
+
 struct sgx_enclave_run;
 
 /**
diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c
index 4d88bfd163e7..6f769e67ec2d 100644
--- a/arch/x86/kernel/cpu/sgx/ioctl.c
+++ b/arch/x86/kernel/cpu/sgx/ioctl.c
@@ -898,6 +898,212 @@  static long sgx_ioc_enclave_restrict_permissions(struct sgx_encl *encl,
 	return ret;
 }
 
+/**
+ * sgx_enclave_modify_type() - Modify type of SGX enclave pages
+ * @encl:	Enclave to which the pages belong.
+ * @modt:	Checked parameters from user about which pages need modifying.
+ * @page_type:	New page type.
+ *
+ * Return:
+ * - 0:		Success
+ * - -errno:	Otherwise
+ */
+static long sgx_enclave_modify_type(struct sgx_encl *encl,
+				    struct sgx_enclave_modify_type *modt,
+				    enum sgx_page_type page_type)
+{
+	unsigned long max_prot_restore;
+	struct sgx_encl_page *entry;
+	struct sgx_secinfo secinfo;
+	unsigned long prot;
+	unsigned long addr;
+	unsigned long c;
+	void *epc_virt;
+	int ret;
+
+	/*
+	 * The only new page types allowed by hardware are PT_TCS and PT_TRIM.
+	 */
+	if (page_type != SGX_PAGE_TYPE_TCS && page_type != SGX_PAGE_TYPE_TRIM)
+		return -EINVAL;
+
+	memset(&secinfo, 0, sizeof(secinfo));
+
+	secinfo.flags = page_type << 8;
+
+	for (c = 0 ; c < modt->length; c += PAGE_SIZE) {
+		addr = encl->base + modt->offset + c;
+
+		mutex_lock(&encl->lock);
+
+		entry = sgx_encl_load_page(encl, addr);
+		if (IS_ERR(entry)) {
+			ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT;
+			goto out_unlock;
+		}
+
+		/*
+		 * Borrow the logic from the Intel SDM. Regular pages
+		 * (SGX_PAGE_TYPE_REG) can change type to SGX_PAGE_TYPE_TCS
+		 * or SGX_PAGE_TYPE_TRIM but TCS pages can only be trimmed.
+		 * CET pages not supported yet.
+		 */
+		if (!(entry->type == SGX_PAGE_TYPE_REG ||
+		      (entry->type == SGX_PAGE_TYPE_TCS &&
+		       page_type == SGX_PAGE_TYPE_TRIM))) {
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+
+		max_prot_restore = entry->vm_max_prot_bits;
+
+		/*
+		 * Once a regular page becomes a TCS page it cannot be
+		 * changed back. So the maximum allowed protection reflects
+		 * the TCS page that is always RW from kernel perspective but
+		 * will be inaccessible from within enclave. Before doing
+		 * so, do make sure that the new page type continues to
+		 * respect the originally vetted page permissions.
+		 */
+		if (entry->type == SGX_PAGE_TYPE_REG &&
+		    page_type == SGX_PAGE_TYPE_TCS) {
+			if (~entry->vm_max_prot_bits & (VM_READ | VM_WRITE)) {
+				ret = -EPERM;
+				goto out_unlock;
+			}
+			prot = PROT_READ | PROT_WRITE;
+			entry->vm_max_prot_bits = calc_vm_prot_bits(prot, 0);
+
+			/*
+			 * Prevent page from being reclaimed while mutex
+			 * is released.
+			 */
+			if (sgx_unmark_page_reclaimable(entry->epc_page)) {
+				ret = -EAGAIN;
+				goto out_entry_changed;
+			}
+
+			/*
+			 * Do not keep encl->lock because of dependency on
+			 * mmap_lock acquired in sgx_zap_enclave_ptes().
+			 */
+			mutex_unlock(&encl->lock);
+
+			sgx_zap_enclave_ptes(encl, addr);
+
+			mutex_lock(&encl->lock);
+
+			sgx_mark_page_reclaimable(entry->epc_page);
+		}
+
+		/* Change EPC type */
+		epc_virt = sgx_get_epc_virt_addr(entry->epc_page);
+		ret = __emodt(&secinfo, epc_virt);
+		if (encls_faulted(ret)) {
+			/*
+			 * All possible faults should be avoidable:
+			 * parameters have been checked, will only change
+			 * valid page types, and no concurrent
+			 * SGX1/SGX2 ENCLS instructions since these are
+			 * protected with mutex.
+			 */
+			pr_err_once("EMODT encountered exception %d\n",
+				    ENCLS_TRAPNR(ret));
+			ret = -EFAULT;
+			goto out_entry_changed;
+		}
+		if (encls_failed(ret)) {
+			modt->result = ret;
+			ret = -EFAULT;
+			goto out_entry_changed;
+		}
+
+		ret = sgx_enclave_etrack(encl);
+		if (ret) {
+			ret = -EFAULT;
+			goto out_unlock;
+		}
+
+		entry->type = page_type;
+
+		mutex_unlock(&encl->lock);
+	}
+
+	ret = 0;
+	goto out;
+
+out_entry_changed:
+	entry->vm_max_prot_bits = max_prot_restore;
+out_unlock:
+	mutex_unlock(&encl->lock);
+out:
+	modt->count = c;
+
+	return ret;
+}
+
+/**
+ * sgx_ioc_enclave_modify_type() - handler for %SGX_IOC_ENCLAVE_MODIFY_TYPE
+ * @encl:	an enclave pointer
+ * @arg:	userspace pointer to a &struct sgx_enclave_modify_type instance
+ *
+ * Ability to change the enclave page type supports the following use cases:
+ *
+ * * It is possible to add TCS pages to an enclave by changing the type of
+ *   regular pages (%SGX_PAGE_TYPE_REG) to TCS (%SGX_PAGE_TYPE_TCS) pages.
+ *   With this support the number of threads supported by an initialized
+ *   enclave can be increased dynamically.
+ *
+ * * Regular or TCS pages can dynamically be removed from an initialized
+ *   enclave by changing the page type to %SGX_PAGE_TYPE_TRIM. Changing the
+ *   page type to %SGX_PAGE_TYPE_TRIM marks the page for removal with actual
+ *   removal done by handler of %SGX_IOC_ENCLAVE_REMOVE_PAGES ioctl() called
+ *   after ENCLU[EACCEPT] is run on %SGX_PAGE_TYPE_TRIM page from within the
+ *   enclave.
+ *
+ * Return:
+ * - 0:		Success
+ * - -errno:	Otherwise
+ */
+static long sgx_ioc_enclave_modify_type(struct sgx_encl *encl, void __user *arg)
+{
+	struct sgx_enclave_modify_type params;
+	enum sgx_page_type page_type;
+	struct sgx_secinfo secinfo;
+	long ret;
+
+	ret = sgx_ioc_sgx2_ready(encl);
+	if (ret)
+		return ret;
+
+	if (copy_from_user(&params, arg, sizeof(params)))
+		return -EFAULT;
+
+	if (sgx_validate_offset_length(encl, params.offset, params.length))
+		return -EINVAL;
+
+	if (copy_from_user(&secinfo, (void __user *)params.secinfo,
+			   sizeof(secinfo)))
+		return -EFAULT;
+
+	if (secinfo.flags & ~SGX_SECINFO_PAGE_TYPE_MASK)
+		return -EINVAL;
+
+	if (memchr_inv(secinfo.reserved, 0, sizeof(secinfo.reserved)))
+		return -EINVAL;
+
+	if (params.result || params.count)
+		return -EINVAL;
+
+	page_type = (secinfo.flags & SGX_SECINFO_PAGE_TYPE_MASK) >> 8;
+	ret = sgx_enclave_modify_type(encl, &params, page_type);
+
+	if (copy_to_user(arg, &params, sizeof(params)))
+		return -EFAULT;
+
+	return ret;
+}
+
 long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 {
 	struct sgx_encl *encl = filep->private_data;
@@ -923,6 +1129,9 @@  long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 		ret = sgx_ioc_enclave_restrict_permissions(encl,
 							   (void __user *)arg);
 		break;
+	case SGX_IOC_ENCLAVE_MODIFY_TYPE:
+		ret = sgx_ioc_enclave_modify_type(encl, (void __user *)arg);
+		break;
 	default:
 		ret = -ENOIOCTLCMD;
 		break;