diff mbox series

[08/23] xen/arm: dom0less seed xenstore grant table entry

Message ID 20250306220343.203047-9-jason.andryuk@amd.com (mailing list archive)
State New
Headers show
Series ARM split hardware and control domains | expand

Commit Message

Jason Andryuk March 6, 2025, 10:03 p.m. UTC
With a split hardware and control domain, the control domain may still
want and xenstore access.  Currently this relies on init-dom0less to
seed the grants.  This is problematic since we don't want hardware
domain to be able to map the control domain's resources.  Instead have
the hypervisor see the grant table entry.  The grant is then accessible
as normal.

This is also useful with a xenstore stubdom to setup the xenbus page
much earlier.

This works with C xenstored.  OCaml xenstored does not use grants and
would fail to foreign map the page.

Signed-off-by: Jason Andryuk <jason.andryuk@amd.com>
---
 xen/arch/arm/dom0less-build.c |  9 +++++++++
 xen/common/grant_table.c      | 10 ++++++++++
 xen/include/xen/grant_table.h |  8 ++++++++
 3 files changed, 27 insertions(+)

Comments

Stefano Stabellini March 7, 2025, 1:47 a.m. UTC | #1
On Thu, 6 Mar 2025, Jason Andryuk wrote:
> With a split hardware and control domain, the control domain may still
> want and xenstore access.  Currently this relies on init-dom0less to
> seed the grants.  This is problematic since we don't want hardware
> domain to be able to map the control domain's resources.  Instead have
> the hypervisor see the grant table entry.  The grant is then accessible
> as normal.
> 
> This is also useful with a xenstore stubdom to setup the xenbus page
> much earlier.

Reading the patch, it seems that what is doing is letting the xenstore
domain map the domU's grant table page. Is that correct?

If so, I would suggest to update the commit message as follows:

With split hardware/control/xenstore domains, the xenstore domain may
still want to access other domains' xenstore page. Currently this relies
on init-dom0less to seed the grants from Dom0.  This is problematic
since we don't want the hardware domain to be able to map other domains'
resources without their permission.  Instead have the hypervisor seed
the grant table entry for every dom0less domain.  The grant is then
accessible as normal.


> This works with C xenstored.  OCaml xenstored does not use grants and
> would fail to foreign map the page.
> 
> Signed-off-by: Jason Andryuk <jason.andryuk@amd.com>
> ---
>  xen/arch/arm/dom0less-build.c |  9 +++++++++
>  xen/common/grant_table.c      | 10 ++++++++++
>  xen/include/xen/grant_table.h |  8 ++++++++
>  3 files changed, 27 insertions(+)
> 
> diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c
> index 068bf99294..f1d5bbb097 100644
> --- a/xen/arch/arm/dom0less-build.c
> +++ b/xen/arch/arm/dom0less-build.c
> @@ -21,6 +21,8 @@
>  #include <asm/static-memory.h>
>  #include <asm/static-shmem.h>
>  
> +static domid_t __initdata xs_domid = DOMID_INVALID;
> +
>  bool __init is_dom0less_mode(void)
>  {
>      struct bootmodules *mods = &bootinfo.modules;
> @@ -753,6 +755,10 @@ static int __init alloc_xenstore_page(struct domain *d)
>      interface->connection = XENSTORE_RECONNECT;
>      unmap_domain_page(interface);
>  
> +    if ( xs_domid != DOMID_INVALID )
> +        gnttab_seed_entry(d, GNTTAB_RESERVED_XENSTORE, xs_domid,
> +                          gfn_x(gfn), GTF_permit_access);
> +
>      return 0;
>  }
>  
> @@ -1173,6 +1179,9 @@ void __init create_domUs(void)
>          if ( rc )
>              panic("Could not set up domain %s (rc = %d)\n",
>                    dt_node_name(node), rc);
> +
> +        if ( d_cfg.flags & XEN_DOMCTL_CDF_xs_domain )
> +            xs_domid = d->domain_id;
>      }
>  }
>  
> diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c
> index 6c77867f8c..ba93cdcbca 100644
> --- a/xen/common/grant_table.c
> +++ b/xen/common/grant_table.c
> @@ -4346,6 +4346,16 @@ static void gnttab_usage_print(struct domain *rd)
>          printk("no active grant table entries\n");
>  }
>  
> +void gnttab_seed_entry(struct domain *d, int idx, domid_t be_domid,
> +                       uint64_t frame, unsigned int flags)
> +{
> +    struct grant_table *gt = d->grant_table;
> +
> +    shared_entry_v1(gt, idx).flags = flags;
> +    shared_entry_v1(gt, idx).domid = be_domid;
> +    shared_entry_v1(gt, idx).frame = frame;
> +}
> +
>  static void cf_check gnttab_usage_print_all(unsigned char key)
>  {
>      struct domain *d;
> diff --git a/xen/include/xen/grant_table.h b/xen/include/xen/grant_table.h
> index 50edfecfb6..63150fa497 100644
> --- a/xen/include/xen/grant_table.h
> +++ b/xen/include/xen/grant_table.h
> @@ -45,6 +45,10 @@ void grant_table_destroy(
>      struct domain *d);
>  void grant_table_init_vcpu(struct vcpu *v);
>  
> +/* Seed a gnttab entry for Hyperlaunch/dom0less. */
> +void gnttab_seed_entry(struct domain *d, int idx, domid_t be_domid,
> +                       uint64_t frame, unsigned int flags);
> +
>  /*
>   * Check if domain has active grants and log first 10 of them.
>   */
> @@ -85,6 +89,10 @@ static inline void grant_table_destroy(struct domain *d) {}
>  
>  static inline void grant_table_init_vcpu(struct vcpu *v) {}
>  
> +static inline void gnttab_seed_entry(struct domain *d, int idx,
> +                                     domid_t be_domid, uint64_t frame,
> +                                     unsigned int flags) {}
> +
>  static inline void grant_table_warn_active_grants(struct domain *d) {}
>  
>  static inline int gnttab_release_mappings(struct domain *d) { return 0; }
> -- 
> 2.48.1
> 
>
Jason Andryuk March 7, 2025, 6:34 p.m. UTC | #2
On 2025-03-06 20:47, Stefano Stabellini wrote:
> On Thu, 6 Mar 2025, Jason Andryuk wrote:
>> With a split hardware and control domain, the control domain may still
>> want and xenstore access.  Currently this relies on init-dom0less to
>> seed the grants.  This is problematic since we don't want hardware
>> domain to be able to map the control domain's resources.  Instead have
>> the hypervisor see the grant table entry.  The grant is then accessible
>> as normal.
>>
>> This is also useful with a xenstore stubdom to setup the xenbus page
>> much earlier.
> 
> Reading the patch, it seems that what is doing is letting the xenstore
> domain map the domU's grant table page. Is that correct?

The end result is everything is setup for xenstored to map 
GNTTAB_RESERVED_XENSTORE at some time later.

> If so, I would suggest to update the commit message as follows:
> 
> With split hardware/control/xenstore domains, the xenstore domain may
> still want to access other domains' xenstore page. Currently this relies
> on init-dom0less to seed the grants from Dom0.  This is problematic
> since we don't want the hardware domain to be able to map other domains'
> resources without their permission.  Instead have the hypervisor seed
> the grant table entry for every dom0less domain.  The grant is then
> accessible as normal.

I'll go with a tweaked version of yours:
xenstored maps other domains' xenstore pages.  Currently this relies
on init-dom0less or xl to seed the grants from Dom0.  With split 
hardware/control/xenstore domains, this is problematic since we don't 
want the hardware domain to be able to map other domains' resources 
without their permission.  Instead have the hypervisor seed the grant 
table entry for every dom0less domain.  The grant is then accessible as 
normal.

Regards,
Jason
Julien Grall March 7, 2025, 9:24 p.m. UTC | #3
Hi,

On 06/03/2025 22:03, Jason Andryuk wrote:
> With a split hardware and control domain, the control domain may still
> want and xenstore access.  Currently this relies on init-dom0less to
> seed the grants.  This is problematic since we don't want hardware
> domain to be able to map the control domain's resources.  Instead have
> the hypervisor see the grant table entry.  The grant is then accessible
> as normal.

I am probably missing something, but why would run xenstored in the 
hardware domain rather than the control domain? Isn't xenstored more 
related to the VM management than HW?

Cheers,
Jason Andryuk March 7, 2025, 11:03 p.m. UTC | #4
On 2025-03-07 16:24, Julien Grall wrote:
> Hi,
> 
> On 06/03/2025 22:03, Jason Andryuk wrote:
>> With a split hardware and control domain, the control domain may still
>> want and xenstore access.  Currently this relies on init-dom0less to
>> seed the grants.  This is problematic since we don't want hardware
>> domain to be able to map the control domain's resources.  Instead have
>> the hypervisor see the grant table entry.  The grant is then accessible
>> as normal.
> 
> I am probably missing something, but why would run xenstored in the 
> hardware domain rather than the control domain? Isn't xenstored more 
> related to the VM management than HW?

I addressed this in my other email.  You're probably right that 
xenstored should run in control, but implementation details prevent that 
in the short term.

Regardless, of the xenstored placement, I think it's a better design for 
Xen to seed the grants.  With Xen allocating the xenstore page and event 
channel, and now seeding the grant table, they can just be used.  A 
xenstore stubdom can just establish all the connections without relying 
on another domain to perform an action.

I tested that with hyperlaunching the xenstore stubdom.  That is where 
the two XS_PRIV changes later in the series come from.  xenstore stubdom 
iterates the domains, reads the hvm_param for the event channel, and 
then runs introduce to set up the connection.

Regards,
Jason
Stefano Stabellini March 8, 2025, 12:48 a.m. UTC | #5
On Fri, 7 Mar 2025, Jason Andryuk wrote:
> On 2025-03-07 16:24, Julien Grall wrote:
> > Hi,
> > 
> > On 06/03/2025 22:03, Jason Andryuk wrote:
> > > With a split hardware and control domain, the control domain may still
> > > want and xenstore access.  Currently this relies on init-dom0less to
> > > seed the grants.  This is problematic since we don't want hardware
> > > domain to be able to map the control domain's resources.  Instead have
> > > the hypervisor see the grant table entry.  The grant is then accessible
> > > as normal.
> > 
> > I am probably missing something, but why would run xenstored in the hardware
> > domain rather than the control domain? Isn't xenstored more related to the
> > VM management than HW?
> 
> I addressed this in my other email.  You're probably right that xenstored
> should run in control, but implementation details prevent that in the short
> term.

I wrote a longer reply here:
https://marc.info/?l=xen-devel&m=174139414000462

I think there are valid reasons to run xenstored in either the control
domain or the hardware domain, so it should be configurable. If no
specific preference is indicated, I would place it in the hardware
domain because the control domain must remain free from interference.
Given that I don't think we know for sure today whether the Xenstore
protocol could be a potential vector for interference, it is safer to
avoid placing it in the control domain by default.


> Regardless, of the xenstored placement, I think it's a better design for Xen
> to seed the grants.  With Xen allocating the xenstore page and event channel,
> and now seeding the grant table, they can just be used.  A xenstore stubdom
> can just establish all the connections without relying on another domain to
> perform an action.

+1


> I tested that with hyperlaunching the xenstore stubdom.  That is where the two
> XS_PRIV changes later in the series come from.  xenstore stubdom iterates the
> domains, reads the hvm_param for the event channel, and then runs introduce to
> set up the connection.
Jan Beulich March 10, 2025, 8:01 a.m. UTC | #6
On 06.03.2025 23:03, Jason Andryuk wrote:
> --- a/xen/common/grant_table.c
> +++ b/xen/common/grant_table.c
> @@ -4346,6 +4346,16 @@ static void gnttab_usage_print(struct domain *rd)
>          printk("no active grant table entries\n");
>  }
>  
> +void gnttab_seed_entry(struct domain *d, int idx, domid_t be_domid,

Can idx be negative? If not, unsigned int please.

Furthermore, as with any additions to common code, please keep in mind
Misra: We better wouldn't gain new unreachable code for certain configs,
even if - didn't check - the respective rule continues to be non-blocking.

> +                       uint64_t frame, unsigned int flags)
> +{
> +    struct grant_table *gt = d->grant_table;

This looks like it could be pointer-to-const. Same apparently for the d
function parameter. Question is - would it perhaps make sense to have
const struct grant_table *gt be the function parameter in the first place?

> +    shared_entry_v1(gt, idx).flags = flags;
> +    shared_entry_v1(gt, idx).domid = be_domid;
> +    shared_entry_v1(gt, idx).frame = frame;
> +}

In common code there shouldn't be an assumption that gnttab v1 is in use.

Jan
Juergen Gross March 10, 2025, 8:17 a.m. UTC | #7
On 10.03.25 09:01, Jan Beulich wrote:
> On 06.03.2025 23:03, Jason Andryuk wrote:
>> +    shared_entry_v1(gt, idx).flags = flags;
>> +    shared_entry_v1(gt, idx).domid = be_domid;
>> +    shared_entry_v1(gt, idx).frame = frame;
>> +}
> 
> In common code there shouldn't be an assumption that gnttab v1 is in use.

But isn't the grant table in V1 format until the guest potentially switches
to V2?


Juergen
Jan Beulich March 10, 2025, 8:21 a.m. UTC | #8
On 10.03.2025 09:17, Juergen Gross wrote:
> On 10.03.25 09:01, Jan Beulich wrote:
>> On 06.03.2025 23:03, Jason Andryuk wrote:
>>> +    shared_entry_v1(gt, idx).flags = flags;
>>> +    shared_entry_v1(gt, idx).domid = be_domid;
>>> +    shared_entry_v1(gt, idx).frame = frame;
>>> +}
>>
>> In common code there shouldn't be an assumption that gnttab v1 is in use.
> 
> But isn't the grant table in V1 format until the guest potentially switches
> to V2?

Strictly speaking it's in v0 format initially. But yes, I see your point.
Provided this function is made clear that it may only ever be used on a
domain that wasn't started yet (perhaps proven by way of an assertion).

Jan
Julien Grall March 10, 2025, 9:32 a.m. UTC | #9
Hi Jason,

On 06/03/2025 22:03, Jason Andryuk wrote:
> With a split hardware and control domain, the control domain may still
> want and xenstore access.  Currently this relies on init-dom0less to
> seed the grants.  This is problematic since we don't want hardware
> domain to be able to map the control domain's resources.  Instead have
> the hypervisor see the grant table entry.  The grant is then accessible
> as normal.
> 
> This is also useful with a xenstore stubdom to setup the xenbus page
> much earlier.
> 
> This works with C xenstored.  OCaml xenstored does not use grants and
> would fail to foreign map the page.
> 
> Signed-off-by: Jason Andryuk <jason.andryuk@amd.com>
> ---
>   xen/arch/arm/dom0less-build.c |  9 +++++++++
>   xen/common/grant_table.c      | 10 ++++++++++
>   xen/include/xen/grant_table.h |  8 ++++++++
>   3 files changed, 27 insertions(+)
> 
> diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c
> index 068bf99294..f1d5bbb097 100644
> --- a/xen/arch/arm/dom0less-build.c
> +++ b/xen/arch/arm/dom0less-build.c
> @@ -21,6 +21,8 @@
>   #include <asm/static-memory.h>
>   #include <asm/static-shmem.h>
>   
> +static domid_t __initdata xs_domid = DOMID_INVALID;
> +
>   bool __init is_dom0less_mode(void)
>   {
>       struct bootmodules *mods = &bootinfo.modules;
> @@ -753,6 +755,10 @@ static int __init alloc_xenstore_page(struct domain *d)
>       interface->connection = XENSTORE_RECONNECT;
>       unmap_domain_page(interface);
>   
> +    if ( xs_domid != DOMID_INVALID )

Looking at this patch again, is this guarantee that the xenstore domain 
will be created first? If not, then I think your series needs to be 
re-ordered so patch #10 is before this patch.

> +        gnttab_seed_entry(d, GNTTAB_RESERVED_XENSTORE, xs_domid,
> +                          gfn_x(gfn), GTF_permit_access);
> +
>       return 0;
>   }
>   
> @@ -1173,6 +1179,9 @@ void __init create_domUs(void)
>           if ( rc )
>               panic("Could not set up domain %s (rc = %d)\n",
>                     dt_node_name(node), rc);
> +
> +        if ( d_cfg.flags & XEN_DOMCTL_CDF_xs_domain )
> +            xs_domid = d->domain_id;

What if there is multiple domain with XEN_DOMCTL_CDF_xs_domain? Should 
we throw an error?

Cheers,
Jason Andryuk March 10, 2025, 2:12 p.m. UTC | #10
On 2025-03-10 05:32, Julien Grall wrote:
> Hi Jason,
> 
> On 06/03/2025 22:03, Jason Andryuk wrote:
>> With a split hardware and control domain, the control domain may still
>> want and xenstore access.  Currently this relies on init-dom0less to
>> seed the grants.  This is problematic since we don't want hardware
>> domain to be able to map the control domain's resources.  Instead have
>> the hypervisor see the grant table entry.  The grant is then accessible
>> as normal.
>>
>> This is also useful with a xenstore stubdom to setup the xenbus page
>> much earlier.
>>
>> This works with C xenstored.  OCaml xenstored does not use grants and
>> would fail to foreign map the page.
>>
>> Signed-off-by: Jason Andryuk <jason.andryuk@amd.com>
>> ---
>>   xen/arch/arm/dom0less-build.c |  9 +++++++++
>>   xen/common/grant_table.c      | 10 ++++++++++
>>   xen/include/xen/grant_table.h |  8 ++++++++
>>   3 files changed, 27 insertions(+)
>>
>> diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less- 
>> build.c
>> index 068bf99294..f1d5bbb097 100644
>> --- a/xen/arch/arm/dom0less-build.c
>> +++ b/xen/arch/arm/dom0less-build.c
>> @@ -21,6 +21,8 @@
>>   #include <asm/static-memory.h>
>>   #include <asm/static-shmem.h>
>> +static domid_t __initdata xs_domid = DOMID_INVALID;
>> +
>>   bool __init is_dom0less_mode(void)
>>   {
>>       struct bootmodules *mods = &bootinfo.modules;
>> @@ -753,6 +755,10 @@ static int __init alloc_xenstore_page(struct 
>> domain *d)
>>       interface->connection = XENSTORE_RECONNECT;
>>       unmap_domain_page(interface);
>> +    if ( xs_domid != DOMID_INVALID )
> 
> Looking at this patch again, is this guarantee that the xenstore domain 
> will be created first? If not, then I think your series needs to be re- 
> ordered so patch #10 is before this patch.

Yes, you are right.

>> +        gnttab_seed_entry(d, GNTTAB_RESERVED_XENSTORE, xs_domid,
>> +                          gfn_x(gfn), GTF_permit_access);
>> +
>>       return 0;
>>   }
>> @@ -1173,6 +1179,9 @@ void __init create_domUs(void)
>>           if ( rc )
>>               panic("Could not set up domain %s (rc = %d)\n",
>>                     dt_node_name(node), rc);
>> +
>> +        if ( d_cfg.flags & XEN_DOMCTL_CDF_xs_domain )
>> +            xs_domid = d->domain_id;
> 
> What if there is multiple domain with XEN_DOMCTL_CDF_xs_domain? Should 
> we throw an error?

Before this series, there was no restriction on its use, but only 
init-xenstore-domain used it.  The XEN_DOMCTL_CDF_xs_domain flag allows 
the domain XSM_XS_PRIV, which grants a few more operations to an 
otherwise unprivileged domU

With the use of xs_domid (only during construction), maybe it should be 
limited to just 1 to avoid surprises.  Otherwise the last built xenstore 
domain would be configured as the backend.  Nothing would break - it 
would just be surprising.  I'll restrict to just 1.

Thanks,
Jason
Jason Andryuk March 10, 2025, 2:27 p.m. UTC | #11
On 2025-03-10 04:21, Jan Beulich wrote:
> On 10.03.2025 09:17, Juergen Gross wrote:
>> On 10.03.25 09:01, Jan Beulich wrote:
>>> On 06.03.2025 23:03, Jason Andryuk wrote:
>>>> +    shared_entry_v1(gt, idx).flags = flags;
>>>> +    shared_entry_v1(gt, idx).domid = be_domid;
>>>> +    shared_entry_v1(gt, idx).frame = frame;
>>>> +}
>>>
>>> In common code there shouldn't be an assumption that gnttab v1 is in use.
>>
>> But isn't the grant table in V1 format until the guest potentially switches
>> to V2?
> 
> Strictly speaking it's in v0 format initially. But yes, I see your point.
> Provided this function is made clear that it may only ever be used on a
> domain that wasn't started yet (perhaps proven by way of an assertion).

Yes, this is what I was relying on.

If d is still passed in, we could do
ASSERT(!d->creation_finished);

Hmmm, the function might could be marked __init, too, I think.

struct grant_table and shared_entry_v1 are defined in 
xen/common/grant_table.c, which is why the function lives there (and 
it'll be useful for hyperlaunch).  To avoid unreachable code, I guess 
I'll wrap it inside #ifdef CONFIG_DOM0LESS_BOOT.

Regards,
Jason
diff mbox series

Patch

diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c
index 068bf99294..f1d5bbb097 100644
--- a/xen/arch/arm/dom0less-build.c
+++ b/xen/arch/arm/dom0less-build.c
@@ -21,6 +21,8 @@ 
 #include <asm/static-memory.h>
 #include <asm/static-shmem.h>
 
+static domid_t __initdata xs_domid = DOMID_INVALID;
+
 bool __init is_dom0less_mode(void)
 {
     struct bootmodules *mods = &bootinfo.modules;
@@ -753,6 +755,10 @@  static int __init alloc_xenstore_page(struct domain *d)
     interface->connection = XENSTORE_RECONNECT;
     unmap_domain_page(interface);
 
+    if ( xs_domid != DOMID_INVALID )
+        gnttab_seed_entry(d, GNTTAB_RESERVED_XENSTORE, xs_domid,
+                          gfn_x(gfn), GTF_permit_access);
+
     return 0;
 }
 
@@ -1173,6 +1179,9 @@  void __init create_domUs(void)
         if ( rc )
             panic("Could not set up domain %s (rc = %d)\n",
                   dt_node_name(node), rc);
+
+        if ( d_cfg.flags & XEN_DOMCTL_CDF_xs_domain )
+            xs_domid = d->domain_id;
     }
 }
 
diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c
index 6c77867f8c..ba93cdcbca 100644
--- a/xen/common/grant_table.c
+++ b/xen/common/grant_table.c
@@ -4346,6 +4346,16 @@  static void gnttab_usage_print(struct domain *rd)
         printk("no active grant table entries\n");
 }
 
+void gnttab_seed_entry(struct domain *d, int idx, domid_t be_domid,
+                       uint64_t frame, unsigned int flags)
+{
+    struct grant_table *gt = d->grant_table;
+
+    shared_entry_v1(gt, idx).flags = flags;
+    shared_entry_v1(gt, idx).domid = be_domid;
+    shared_entry_v1(gt, idx).frame = frame;
+}
+
 static void cf_check gnttab_usage_print_all(unsigned char key)
 {
     struct domain *d;
diff --git a/xen/include/xen/grant_table.h b/xen/include/xen/grant_table.h
index 50edfecfb6..63150fa497 100644
--- a/xen/include/xen/grant_table.h
+++ b/xen/include/xen/grant_table.h
@@ -45,6 +45,10 @@  void grant_table_destroy(
     struct domain *d);
 void grant_table_init_vcpu(struct vcpu *v);
 
+/* Seed a gnttab entry for Hyperlaunch/dom0less. */
+void gnttab_seed_entry(struct domain *d, int idx, domid_t be_domid,
+                       uint64_t frame, unsigned int flags);
+
 /*
  * Check if domain has active grants and log first 10 of them.
  */
@@ -85,6 +89,10 @@  static inline void grant_table_destroy(struct domain *d) {}
 
 static inline void grant_table_init_vcpu(struct vcpu *v) {}
 
+static inline void gnttab_seed_entry(struct domain *d, int idx,
+                                     domid_t be_domid, uint64_t frame,
+                                     unsigned int flags) {}
+
 static inline void grant_table_warn_active_grants(struct domain *d) {}
 
 static inline int gnttab_release_mappings(struct domain *d) { return 0; }