diff mbox series

[RFC,4/6] capabilities: introduce console io as a domain capability

Message ID 20230801202006.20322-5-dpsmith@apertussolutions.com (mailing list archive)
State New, archived
Headers show
Series Hyperlaunch domain roles and capabilities | expand

Commit Message

Daniel P. Smith Aug. 1, 2023, 8:20 p.m. UTC
The field `is_console` suggests that the field represents a state of being or
posession, not that it reflects the privilege to access the console. In this
patch the field is renamed to capabilities to encapsulate the capabilities a
domain has been granted. The first capability being the ability to read/write
the Xen console.

Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
---
 xen/arch/arm/domain_build.c |  4 +++-
 xen/include/xen/sched.h     | 25 +++++++++++++++++++++++--
 xen/include/xsm/dummy.h     |  2 +-
 3 files changed, 27 insertions(+), 4 deletions(-)

Comments

Stefano Stabellini Aug. 2, 2023, 1:01 a.m. UTC | #1
On Tue, 1 Aug 2023, Daniel P. Smith wrote:
> The field `is_console` suggests that the field represents a state of being or
> posession, not that it reflects the privilege to access the console. In this
  ^ possession

> patch the field is renamed to capabilities to encapsulate the capabilities a
> domain has been granted. The first capability being the ability to read/write
> the Xen console.
> 
> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>

Patch looks fine to me aside the two minor nits. I am not sure I
understand 100% the difference between capabilities and roles but I am
OK with the patch. I'd like to hear Julien's feedback on this as well. 


> ---
>  xen/arch/arm/domain_build.c |  4 +++-
>  xen/include/xen/sched.h     | 25 +++++++++++++++++++++++--
>  xen/include/xsm/dummy.h     |  2 +-
>  3 files changed, 27 insertions(+), 4 deletions(-)
> 
> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
> index 51b4daefe1..ad7432b029 100644
> --- a/xen/arch/arm/domain_build.c
> +++ b/xen/arch/arm/domain_build.c
> @@ -4076,7 +4076,9 @@ void __init create_domUs(void)
>              panic("Error creating domain %s (rc = %ld)\n",
>                    dt_node_name(node), PTR_ERR(d));
>  
> -        d->is_console = true;
> +        if ( ! domain_set_cap(d, CAP_CONSOLE_IO) )

code style


> +            printk("failed setting console_io on %pd\n", d);
> +
>          dt_device_set_used_by(node, d->domain_id);
>  
>          rc = construct_domU(d, node);
> diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
> index ec0f9baff6..b04fbe0565 100644
> --- a/xen/include/xen/sched.h
> +++ b/xen/include/xen/sched.h
> @@ -472,8 +472,8 @@ struct domain
>  #define ROLE_HARDWARE_DOMAIN   (1U<<2)
>  #define ROLE_XENSTORE_DOMAIN   (1U<<3)
>      uint8_t          role;
> -    /* Can this guest access the Xen console? */
> -    bool             is_console;
> +#define CAP_CONSOLE_IO  (1U<<0)
> +    uint8_t          capabilities;
>      /* Is this guest being debugged by dom0? */
>      bool             debugger_attached;
>      /*
> @@ -1146,6 +1146,27 @@ static always_inline bool is_hvm_vcpu(const struct vcpu *v)
>      return is_hvm_domain(v->domain);
>  }
>  
> +static always_inline bool domain_has_cap(
> +    const struct domain *d, uint8_t cap)
> +{
> +    return d->capabilities & cap;
> +}
> +
> +static always_inline bool domain_set_cap(
> +    struct domain *d, uint8_t cap)
> +{
> +    switch ( cap )
> +    {
> +    case CAP_CONSOLE_IO:
> +        d->capabilities |= cap;
> +        break;
> +    default:
> +        return false;
> +    }
> +
> +    return domain_has_cap(d, cap);
> +}
> +
>  static always_inline bool hap_enabled(const struct domain *d)
>  {
>      /* sanitise_domain_config() rejects HAP && !HVM */
> diff --git a/xen/include/xsm/dummy.h b/xen/include/xsm/dummy.h
> index 18f1ddd127..067ff1d111 100644
> --- a/xen/include/xsm/dummy.h
> +++ b/xen/include/xsm/dummy.h
> @@ -268,7 +268,7 @@ static XSM_INLINE int cf_check xsm_console_io(
>      XSM_DEFAULT_ARG struct domain *d, int cmd)
>  {
>      XSM_ASSERT_ACTION(XSM_OTHER);
> -    if ( d->is_console )
> +    if ( domain_has_cap(d, CAP_CONSOLE_IO) )
>          return xsm_default_action(XSM_HOOK, d, NULL);
>  #ifdef CONFIG_VERBOSE_DEBUG
>      if ( cmd == CONSOLEIO_write )
> -- 
> 2.20.1
>
Daniel P. Smith Aug. 3, 2023, 3:41 p.m. UTC | #2
On 8/1/23 21:01, Stefano Stabellini wrote:
> On Tue, 1 Aug 2023, Daniel P. Smith wrote:
>> The field `is_console` suggests that the field represents a state of being or
>> posession, not that it reflects the privilege to access the console. In this
>    ^ possession

Thank you for the catch.

>> patch the field is renamed to capabilities to encapsulate the capabilities a
>> domain has been granted. The first capability being the ability to read/write
>> the Xen console.
>>
>> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
> 
> Patch looks fine to me aside the two minor nits. I am not sure I
> understand 100% the difference between capabilities and roles but I am
> OK with the patch. I'd like to hear Julien's feedback on this as well.

This might be worth a section in the hypervisor-guide. As mentioned in 
the cover letter, this was originally proposed as being under XSM. A 
challenge I ran into is that, depending on your view, the 
`is_privileged` field and `hardware_domain` global were either abused as 
a function check and a non-resource privilege check or are just 
multifaceted variables. This is why the concept of the role was struck 
upon, it is more intuitive (for me at least) that have a role is 
something that imparts accesses (privilege checks) and dictates 
hypervisor behaviors when handling the domain (function checks). This 
then brings us to an access or behavior that may be inherent to some 
role(s) but may want to grant on an individually to a guest. A prime 
example of this is console_io, for which it is inherent that the 
hardware domain role will have access but may want to grant to a guest 
without granting it an entire role. This is why I provided for 
identifying these capabilities so that they may be assigned individually 
to a domain.

While the role/capability is a natural progression from how the 
hypervisor currently operates. Another approach that could be consider 
to deliver a similar experience would be to break down every access and 
function into a capability and then define the standard roles as a 
conglomeration of certain capabilities.

I am open to suggestions here.

>> ---
>>   xen/arch/arm/domain_build.c |  4 +++-
>>   xen/include/xen/sched.h     | 25 +++++++++++++++++++++++--
>>   xen/include/xsm/dummy.h     |  2 +-
>>   3 files changed, 27 insertions(+), 4 deletions(-)
>>
>> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
>> index 51b4daefe1..ad7432b029 100644
>> --- a/xen/arch/arm/domain_build.c
>> +++ b/xen/arch/arm/domain_build.c
>> @@ -4076,7 +4076,9 @@ void __init create_domUs(void)
>>               panic("Error creating domain %s (rc = %ld)\n",
>>                     dt_node_name(node), PTR_ERR(d));
>>   
>> -        d->is_console = true;
>> +        if ( ! domain_set_cap(d, CAP_CONSOLE_IO) )
> 
> code style
> 
> 
>> +            printk("failed setting console_io on %pd\n", d);
>> +
>>           dt_device_set_used_by(node, d->domain_id);
>>   
>>           rc = construct_domU(d, node);
>> diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
>> index ec0f9baff6..b04fbe0565 100644
>> --- a/xen/include/xen/sched.h
>> +++ b/xen/include/xen/sched.h
>> @@ -472,8 +472,8 @@ struct domain
>>   #define ROLE_HARDWARE_DOMAIN   (1U<<2)
>>   #define ROLE_XENSTORE_DOMAIN   (1U<<3)
>>       uint8_t          role;
>> -    /* Can this guest access the Xen console? */
>> -    bool             is_console;
>> +#define CAP_CONSOLE_IO  (1U<<0)
>> +    uint8_t          capabilities;
>>       /* Is this guest being debugged by dom0? */
>>       bool             debugger_attached;
>>       /*
>> @@ -1146,6 +1146,27 @@ static always_inline bool is_hvm_vcpu(const struct vcpu *v)
>>       return is_hvm_domain(v->domain);
>>   }
>>   
>> +static always_inline bool domain_has_cap(
>> +    const struct domain *d, uint8_t cap)
>> +{
>> +    return d->capabilities & cap;
>> +}
>> +
>> +static always_inline bool domain_set_cap(
>> +    struct domain *d, uint8_t cap)
>> +{
>> +    switch ( cap )
>> +    {
>> +    case CAP_CONSOLE_IO:
>> +        d->capabilities |= cap;
>> +        break;
>> +    default:
>> +        return false;
>> +    }
>> +
>> +    return domain_has_cap(d, cap);
>> +}
>> +
>>   static always_inline bool hap_enabled(const struct domain *d)
>>   {
>>       /* sanitise_domain_config() rejects HAP && !HVM */
>> diff --git a/xen/include/xsm/dummy.h b/xen/include/xsm/dummy.h
>> index 18f1ddd127..067ff1d111 100644
>> --- a/xen/include/xsm/dummy.h
>> +++ b/xen/include/xsm/dummy.h
>> @@ -268,7 +268,7 @@ static XSM_INLINE int cf_check xsm_console_io(
>>       XSM_DEFAULT_ARG struct domain *d, int cmd)
>>   {
>>       XSM_ASSERT_ACTION(XSM_OTHER);
>> -    if ( d->is_console )
>> +    if ( domain_has_cap(d, CAP_CONSOLE_IO) )
>>           return xsm_default_action(XSM_HOOK, d, NULL);
>>   #ifdef CONFIG_VERBOSE_DEBUG
>>       if ( cmd == CONSOLEIO_write )
>> -- 
>> 2.20.1
>>
Julien Grall Aug. 3, 2023, 9:03 p.m. UTC | #3
Hi,

On 01/08/2023 21:20, Daniel P. Smith wrote:
> The field `is_console` suggests that the field represents a state of being or
> posession, not that it reflects the privilege to access the console. In this
> patch the field is renamed to capabilities to encapsulate the capabilities a
> domain has been granted. The first capability being the ability to read/write
> the Xen console.
> 
> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
> ---
>   xen/arch/arm/domain_build.c |  4 +++-
>   xen/include/xen/sched.h     | 25 +++++++++++++++++++++++--
>   xen/include/xsm/dummy.h     |  2 +-
>   3 files changed, 27 insertions(+), 4 deletions(-)
> 
> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
> index 51b4daefe1..ad7432b029 100644
> --- a/xen/arch/arm/domain_build.c
> +++ b/xen/arch/arm/domain_build.c
> @@ -4076,7 +4076,9 @@ void __init create_domUs(void)
>               panic("Error creating domain %s (rc = %ld)\n",
>                     dt_node_name(node), PTR_ERR(d));
>   
> -        d->is_console = true;
> +        if ( ! domain_set_cap(d, CAP_CONSOLE_IO) )

Coding style: We don't usually add a space after '!'.

> +            printk("failed setting console_io on %pd\n", d);

I find a bit odd that we would continue even if the cap cannot be set. 
Can you clarify?

> +
>           dt_device_set_used_by(node, d->domain_id);
>   
>           rc = construct_domU(d, node);
> diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
> index ec0f9baff6..b04fbe0565 100644
> --- a/xen/include/xen/sched.h
> +++ b/xen/include/xen/sched.h
> @@ -472,8 +472,8 @@ struct domain
>   #define ROLE_HARDWARE_DOMAIN   (1U<<2)
>   #define ROLE_XENSTORE_DOMAIN   (1U<<3)
>       uint8_t          role;
> -    /* Can this guest access the Xen console? */
> -    bool             is_console;
> +#define CAP_CONSOLE_IO  (1U<<0)
Coding style: Space before and after <<.

> +    uint8_t          capabilities;
>       /* Is this guest being debugged by dom0? */
>       bool             debugger_attached;
>       /*
> @@ -1146,6 +1146,27 @@ static always_inline bool is_hvm_vcpu(const struct vcpu *v)
>       return is_hvm_domain(v->domain);
>   }
>   
> +static always_inline bool domain_has_cap(
> +    const struct domain *d, uint8_t cap)

Coding style: We don't usually wrap the arguments this way. See 
domain_create() for an example.

> +{
> +    return d->capabilities & cap;
> +}
> +
> +static always_inline bool domain_set_cap(
> +    struct domain *d, uint8_t cap)

Same about the coding style here.

Also, do you expect the cap to be set only when the domain is created? 
If not, would you prevent potentially concurrent update to d->capabilities?


> +{
> +    switch ( cap )
> +    {
> +    case CAP_CONSOLE_IO:
> +        d->capabilities |= cap;
> +        break;
> +    default:

Is this meant to be reached? If not, maybe add ASSERT_UNREACHABLE()?

> +        return false;
> +    }
> +
> +    return domain_has_cap(d, cap);
> +} > +
>   static always_inline bool hap_enabled(const struct domain *d)
>   {
>       /* sanitise_domain_config() rejects HAP && !HVM */
> diff --git a/xen/include/xsm/dummy.h b/xen/include/xsm/dummy.h
> index 18f1ddd127..067ff1d111 100644
> --- a/xen/include/xsm/dummy.h
> +++ b/xen/include/xsm/dummy.h
> @@ -268,7 +268,7 @@ static XSM_INLINE int cf_check xsm_console_io(
>       XSM_DEFAULT_ARG struct domain *d, int cmd)
>   {
>       XSM_ASSERT_ACTION(XSM_OTHER);
> -    if ( d->is_console )
> +    if ( domain_has_cap(d, CAP_CONSOLE_IO) )
>           return xsm_default_action(XSM_HOOK, d, NULL);
>   #ifdef CONFIG_VERBOSE_DEBUG
>       if ( cmd == CONSOLEIO_write )

Cheers,
Julien Grall Aug. 3, 2023, 9:24 p.m. UTC | #4
Hi Daniel,

On 03/08/2023 16:41, Daniel P. Smith wrote:
> On 8/1/23 21:01, Stefano Stabellini wrote:
>> On Tue, 1 Aug 2023, Daniel P. Smith wrote:
>>> patch the field is renamed to capabilities to encapsulate the 
>>> capabilities a
>>> domain has been granted. The first capability being the ability to 
>>> read/write
>>> the Xen console.
>>>
>>> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
>>
>> Patch looks fine to me aside the two minor nits. I am not sure I
>> understand 100% the difference between capabilities and roles but I am
>> OK with the patch. I'd like to hear Julien's feedback on this as well.
> 
> This might be worth a section in the hypervisor-guide. As mentioned in 
> the cover letter, this was originally proposed as being under XSM. A 
> challenge I ran into is that, depending on your view, the 
> `is_privileged` field and `hardware_domain` global were either abused as 
> a function check and a non-resource privilege check or are just 
> multifaceted variables. This is why the concept of the role was struck 
> upon, it is more intuitive (for me at least) that have a role is 
> something that imparts accesses (privilege checks) and dictates 
> hypervisor behaviors when handling the domain (function checks). This 
> then brings us to an access or behavior that may be inherent to some 
> role(s) but may want to grant on an individually to a guest. A prime 
> example of this is console_io, for which it is inherent that the 
> hardware domain role will have access but may want to grant to a guest 
> without granting it an entire role. This is why I provided for 
> identifying these capabilities so that they may be assigned individually 
> to a domain.

Thanks for the explanation. Just to confirm my understanding, what you 
are suggesting is that for a given role, a domain will at least have the 
matching capabilities (more could be granted). Is that correct?

If so, this wouldn't this mean we can remove d->role and simply use 
d->capabilities?

> 
> While the role/capability is a natural progression from how the 
> hypervisor currently operates. Another approach that could be consider 
> to deliver a similar experience would be to break down every access and 
> function into a capability and then define the standard roles as a 
> conglomeration of certain capabilities.

At least from the explanation above, I think it would make sense to 
break down role to multiple capabilities.

Cheers,
Jan Beulich Aug. 8, 2023, 3:24 p.m. UTC | #5
On 01.08.2023 22:20, Daniel P. Smith wrote:
> --- a/xen/include/xen/sched.h
> +++ b/xen/include/xen/sched.h
> @@ -472,8 +472,8 @@ struct domain
>  #define ROLE_HARDWARE_DOMAIN   (1U<<2)
>  #define ROLE_XENSTORE_DOMAIN   (1U<<3)
>      uint8_t          role;
> -    /* Can this guest access the Xen console? */
> -    bool             is_console;
> +#define CAP_CONSOLE_IO  (1U<<0)
> +    uint8_t          capabilities;
>      /* Is this guest being debugged by dom0? */
>      bool             debugger_attached;
>      /*
> @@ -1146,6 +1146,27 @@ static always_inline bool is_hvm_vcpu(const struct vcpu *v)
>      return is_hvm_domain(v->domain);
>  }
>  
> +static always_inline bool domain_has_cap(
> +    const struct domain *d, uint8_t cap)
> +{
> +    return d->capabilities & cap;
> +}

What you implement here is effectively domain_has_any_cap(), which I
don't think is of much use. At the very least you want to assert that
cap is a power of two. But perhaps you rather want the caller to pass
in a bit number, not a mask, such that it's obvious that only one
thing can be checked at a time.

> +static always_inline bool domain_set_cap(
> +    struct domain *d, uint8_t cap)
> +{
> +    switch ( cap )
> +    {
> +    case CAP_CONSOLE_IO:
> +        d->capabilities |= cap;
> +        break;
> +    default:
> +        return false;
> +    }
> +
> +    return domain_has_cap(d, cap);
> +}

The "set" operation doesn't need to be an inline function, does it?

Jan
Daniel P. Smith Aug. 8, 2023, 10:31 p.m. UTC | #6
On 8/3/23 17:24, Julien Grall wrote:
> Hi Daniel,
> 
> On 03/08/2023 16:41, Daniel P. Smith wrote:
>> On 8/1/23 21:01, Stefano Stabellini wrote:
>>> On Tue, 1 Aug 2023, Daniel P. Smith wrote:
>>>> patch the field is renamed to capabilities to encapsulate the 
>>>> capabilities a
>>>> domain has been granted. The first capability being the ability to 
>>>> read/write
>>>> the Xen console.
>>>>
>>>> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
>>>
>>> Patch looks fine to me aside the two minor nits. I am not sure I
>>> understand 100% the difference between capabilities and roles but I am
>>> OK with the patch. I'd like to hear Julien's feedback on this as well.
>>
>> This might be worth a section in the hypervisor-guide. As mentioned in 
>> the cover letter, this was originally proposed as being under XSM. A 
>> challenge I ran into is that, depending on your view, the 
>> `is_privileged` field and `hardware_domain` global were either abused 
>> as a function check and a non-resource privilege check or are just 
>> multifaceted variables. This is why the concept of the role was struck 
>> upon, it is more intuitive (for me at least) that have a role is 
>> something that imparts accesses (privilege checks) and dictates 
>> hypervisor behaviors when handling the domain (function checks). This 
>> then brings us to an access or behavior that may be inherent to some 
>> role(s) but may want to grant on an individually to a guest. A prime 
>> example of this is console_io, for which it is inherent that the 
>> hardware domain role will have access but may want to grant to a guest 
>> without granting it an entire role. This is why I provided for 
>> identifying these capabilities so that they may be assigned 
>> individually to a domain.
> 
> Thanks for the explanation. Just to confirm my understanding, what you 
> are suggesting is that for a given role, a domain will at least have the 
> matching capabilities (more could be granted). Is that correct?
> 
> If so, this wouldn't this mean we can remove d->role and simply use 
> d->capabilities?

We could start out with CAP_CTRL and CAP_HW, but it is a little 
illogical. For instance, control domain has many capabilities, they just 
have never been fully broken out. XSM did some, but the focus there was 
just on system resources. Similar with the hardware domain. You are 
right that it would better deal with the limited number of bits 
currently available.

>>
>> While the role/capability is a natural progression from how the 
>> hypervisor currently operates. Another approach that could be consider 
>> to deliver a similar experience would be to break down every access 
>> and function into a capability and then define the standard roles as a 
>> conglomeration of certain capabilities.
> 
> At least from the explanation above, I think it would make sense to 
> break down role to multiple capabilities.

Would it be acceptable to do this incrementally over time as we are able 
to determine what needs to be broken out as a distinct capability?
Daniel P. Smith Aug. 8, 2023, 10:49 p.m. UTC | #7
On 8/3/23 17:03, Julien Grall wrote:
> Hi,

Greetings

> On 01/08/2023 21:20, Daniel P. Smith wrote:
>> The field `is_console` suggests that the field represents a state of 
>> being or
>> posession, not that it reflects the privilege to access the console. 
>> In this
>> patch the field is renamed to capabilities to encapsulate the 
>> capabilities a
>> domain has been granted. The first capability being the ability to 
>> read/write
>> the Xen console.
>>
>> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
>> ---
>>   xen/arch/arm/domain_build.c |  4 +++-
>>   xen/include/xen/sched.h     | 25 +++++++++++++++++++++++--
>>   xen/include/xsm/dummy.h     |  2 +-
>>   3 files changed, 27 insertions(+), 4 deletions(-)
>>
>> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
>> index 51b4daefe1..ad7432b029 100644
>> --- a/xen/arch/arm/domain_build.c
>> +++ b/xen/arch/arm/domain_build.c
>> @@ -4076,7 +4076,9 @@ void __init create_domUs(void)
>>               panic("Error creating domain %s (rc = %ld)\n",
>>                     dt_node_name(node), PTR_ERR(d));
>> -        d->is_console = true;
>> +        if ( ! domain_set_cap(d, CAP_CONSOLE_IO) )
> 
> Coding style: We don't usually add a space after '!'.

Ack.

>> +            printk("failed setting console_io on %pd\n", d);
> 
> I find a bit odd that we would continue even if the cap cannot be set. 
> Can you clarify?

This is the construction of a domU, so the system is very much capable 
of coming up and reviewing the hypervisor messages from dom0 to discover 
the issue. I am hard pressed to believe the hypervisor should be 
panicked because the domU is not allowed to use the hypervisor's console.

g>> +
>>           dt_device_set_used_by(node, d->domain_id);
>>           rc = construct_domU(d, node);
>> diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
>> index ec0f9baff6..b04fbe0565 100644
>> --- a/xen/include/xen/sched.h
>> +++ b/xen/include/xen/sched.h
>> @@ -472,8 +472,8 @@ struct domain
>>   #define ROLE_HARDWARE_DOMAIN   (1U<<2)
>>   #define ROLE_XENSTORE_DOMAIN   (1U<<3)
>>       uint8_t          role;
>> -    /* Can this guest access the Xen console? */
>> -    bool             is_console;
>> +#define CAP_CONSOLE_IO  (1U<<0)
> Coding style: Space before and after <<.

Ack.

>> +    uint8_t          capabilities;
>>       /* Is this guest being debugged by dom0? */
>>       bool             debugger_attached;
>>       /*
>> @@ -1146,6 +1146,27 @@ static always_inline bool is_hvm_vcpu(const 
>> struct vcpu *v)
>>       return is_hvm_domain(v->domain);
>>   }
>> +static always_inline bool domain_has_cap(
>> +    const struct domain *d, uint8_t cap)
> 
> Coding style: We don't usually wrap the arguments this way. See 
> domain_create() for an example.

I was informed it was[1], also, please see next_domain_in_cpupool() 
amongst many others further below.

[1] 
https://lists.xenproject.org/archives/html/xen-devel/2021-07/msg01133.html

>> +{
>> +    return d->capabilities & cap;
>> +}
>> +
>> +static always_inline bool domain_set_cap(
>> +    struct domain *d, uint8_t cap)
> 
> Same about the coding style here.

Ditto.

> Also, do you expect the cap to be set only when the domain is created? 
> If not, would you prevent potentially concurrent update to d->capabilities?

Currently the only means being devise to set this is via hyperlaunch 
domain creation. If a domctl op was added to be able to manipulate the 
caps, then yes a lock on the domain would be advised to block. With that 
said, if we switch over to CAP_CTRL/HW, then it might be good to grab a 
lock on the domain for the late hardware domain case.

>> +{
>> +    switch ( cap )
>> +    {
>> +    case CAP_CONSOLE_IO:
>> +        d->capabilities |= cap;
>> +        break;
>> +    default:
> 
> Is this meant to be reached? If not, maybe add ASSERT_UNREACHABLE()?

Yah, that would be a good idea.

>> +        return false;
>> +    }
>> +
>> +    return domain_has_cap(d, cap);
>> +} > +
>>   static always_inline bool hap_enabled(const struct domain *d)
>>   {
>>       /* sanitise_domain_config() rejects HAP && !HVM */
>> diff --git a/xen/include/xsm/dummy.h b/xen/include/xsm/dummy.h
>> index 18f1ddd127..067ff1d111 100644
>> --- a/xen/include/xsm/dummy.h
>> +++ b/xen/include/xsm/dummy.h
>> @@ -268,7 +268,7 @@ static XSM_INLINE int cf_check xsm_console_io(
>>       XSM_DEFAULT_ARG struct domain *d, int cmd)
>>   {
>>       XSM_ASSERT_ACTION(XSM_OTHER);
>> -    if ( d->is_console )
>> +    if ( domain_has_cap(d, CAP_CONSOLE_IO) )
>>           return xsm_default_action(XSM_HOOK, d, NULL);
>>   #ifdef CONFIG_VERBOSE_DEBUG
>>       if ( cmd == CONSOLEIO_write )


v/r,
dps
Daniel P. Smith Aug. 8, 2023, 11:32 p.m. UTC | #8
On 8/8/23 11:24, Jan Beulich wrote:
> On 01.08.2023 22:20, Daniel P. Smith wrote:
>> --- a/xen/include/xen/sched.h
>> +++ b/xen/include/xen/sched.h
>> @@ -472,8 +472,8 @@ struct domain
>>   #define ROLE_HARDWARE_DOMAIN   (1U<<2)
>>   #define ROLE_XENSTORE_DOMAIN   (1U<<3)
>>       uint8_t          role;
>> -    /* Can this guest access the Xen console? */
>> -    bool             is_console;
>> +#define CAP_CONSOLE_IO  (1U<<0)
>> +    uint8_t          capabilities;
>>       /* Is this guest being debugged by dom0? */
>>       bool             debugger_attached;
>>       /*
>> @@ -1146,6 +1146,27 @@ static always_inline bool is_hvm_vcpu(const struct vcpu *v)
>>       return is_hvm_domain(v->domain);
>>   }
>>   
>> +static always_inline bool domain_has_cap(
>> +    const struct domain *d, uint8_t cap)
>> +{
>> +    return d->capabilities & cap;
>> +}
> 
> What you implement here is effectively domain_has_any_cap(), which I
> don't think is of much use. At the very least you want to assert that
> cap is a power of two. But perhaps you rather want the caller to pass
> in a bit number, not a mask, such that it's obvious that only one
> thing can be checked at a time.

I did this thinking I was going to save keystrokes and encapsulate the 
check, but I just double checked and it would have only saved one. I 
will drop this and put the explicit bit checks at all the check sites.

>> +static always_inline bool domain_set_cap(
>> +    struct domain *d, uint8_t cap)
>> +{
>> +    switch ( cap )
>> +    {
>> +    case CAP_CONSOLE_IO:
>> +        d->capabilities |= cap;
>> +        break;
>> +    default:
>> +        return false;
>> +    }
>> +
>> +    return domain_has_cap(d, cap);
>> +}
> 
> The "set" operation doesn't need to be an inline function, does it?

I guess not, I can move into common/domain.c.

v/r,
dps
Julien Grall Aug. 9, 2023, 9 a.m. UTC | #9
Hi Daniel,

On 08/08/2023 23:31, Daniel P. Smith wrote:
> 
> 
> On 8/3/23 17:24, Julien Grall wrote:
>> Hi Daniel,
>>
>> On 03/08/2023 16:41, Daniel P. Smith wrote:
>>> On 8/1/23 21:01, Stefano Stabellini wrote:
>>>> On Tue, 1 Aug 2023, Daniel P. Smith wrote:
>>>>> patch the field is renamed to capabilities to encapsulate the 
>>>>> capabilities a
>>>>> domain has been granted. The first capability being the ability to 
>>>>> read/write
>>>>> the Xen console.
>>>>>
>>>>> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
>>>>
>>>> Patch looks fine to me aside the two minor nits. I am not sure I
>>>> understand 100% the difference between capabilities and roles but I am
>>>> OK with the patch. I'd like to hear Julien's feedback on this as well.
>>>
>>> This might be worth a section in the hypervisor-guide. As mentioned 
>>> in the cover letter, this was originally proposed as being under XSM. 
>>> A challenge I ran into is that, depending on your view, the 
>>> `is_privileged` field and `hardware_domain` global were either abused 
>>> as a function check and a non-resource privilege check or are just 
>>> multifaceted variables. This is why the concept of the role was 
>>> struck upon, it is more intuitive (for me at least) that have a role 
>>> is something that imparts accesses (privilege checks) and dictates 
>>> hypervisor behaviors when handling the domain (function checks). This 
>>> then brings us to an access or behavior that may be inherent to some 
>>> role(s) but may want to grant on an individually to a guest. A prime 
>>> example of this is console_io, for which it is inherent that the 
>>> hardware domain role will have access but may want to grant to a 
>>> guest without granting it an entire role. This is why I provided for 
>>> identifying these capabilities so that they may be assigned 
>>> individually to a domain.
>>
>> Thanks for the explanation. Just to confirm my understanding, what you 
>> are suggesting is that for a given role, a domain will at least have 
>> the matching capabilities (more could be granted). Is that correct?
>>
>> If so, this wouldn't this mean we can remove d->role and simply use 
>> d->capabilities?
> 
> We could start out with CAP_CTRL and CAP_HW, but it is a little 
> illogical. For instance, control domain has many capabilities, they just 
> have never been fully broken out. XSM did some, but the focus there was 
> just on system resources. Similar with the hardware domain. You are 
> right that it would better deal with the limited number of bits 
> currently available.
> 
>>>
>>> While the role/capability is a natural progression from how the 
>>> hypervisor currently operates. Another approach that could be 
>>> consider to deliver a similar experience would be to break down every 
>>> access and function into a capability and then define the standard 
>>> roles as a conglomeration of certain capabilities.
>>
>> At least from the explanation above, I think it would make sense to 
>> break down role to multiple capabilities.
> 
> Would it be acceptable to do this incrementally over time as we are able 
> to determine what needs to be broken out as a distinct capability?

I would be fine with that. Note that some care will be needed for the 
Device-Tree to either version the capabilities or at least not break 
boot when using an old DT on a new Xen.

Cheers,
Julien Grall Aug. 9, 2023, 9:44 a.m. UTC | #10
Hi Daniel,

On 08/08/2023 23:49, Daniel P. Smith wrote:
> On 8/3/23 17:03, Julien Grall wrote:
>> On 01/08/2023 21:20, Daniel P. Smith wrote:
>>> The field `is_console` suggests that the field represents a state of 
>>> being or
>>> posession, not that it reflects the privilege to access the console. 
>>> In this
>>> patch the field is renamed to capabilities to encapsulate the 
>>> capabilities a
>>> domain has been granted. The first capability being the ability to 
>>> read/write
>>> the Xen console.
>>>
>>> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
>>> ---
>>>   xen/arch/arm/domain_build.c |  4 +++-
>>>   xen/include/xen/sched.h     | 25 +++++++++++++++++++++++--
>>>   xen/include/xsm/dummy.h     |  2 +-
>>>   3 files changed, 27 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
>>> index 51b4daefe1..ad7432b029 100644
>>> --- a/xen/arch/arm/domain_build.c
>>> +++ b/xen/arch/arm/domain_build.c
>>> @@ -4076,7 +4076,9 @@ void __init create_domUs(void)
>>>               panic("Error creating domain %s (rc = %ld)\n",
>>>                     dt_node_name(node), PTR_ERR(d));
>>> -        d->is_console = true;
>>> +        if ( ! domain_set_cap(d, CAP_CONSOLE_IO) )
>>
>> Coding style: We don't usually add a space after '!'.
> 
> Ack.
> 
>>> +            printk("failed setting console_io on %pd\n", d);
>>
>> I find a bit odd that we would continue even if the cap cannot be set. 
>> Can you clarify?
> 
> This is the construction of a domU, so the system is very much capable 
> of coming up and reviewing the hypervisor messages from dom0 to discover 
> the issue. I am hard pressed to believe the hypervisor should be 
> panicked because the domU is not allowed to use the hypervisor's console.

I understand the system may be able to boot. However, the problem is 
that it may take a while to discover that the console is not working 
properly (the more if you only use it for error logging).

So on Arm, we have so always decided to fail early rather than late in 
order to help debugging. So I would rather not change the behavior even 
if this is "just" for the console.

If you expect the console to be disabled, then we should provide a 
property in the Device-Tree to select/deselect. It should not be hidden.

> 
> g>> +
>>>           dt_device_set_used_by(node, d->domain_id);
>>>           rc = construct_domU(d, node);
>>> diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
>>> index ec0f9baff6..b04fbe0565 100644
>>> --- a/xen/include/xen/sched.h
>>> +++ b/xen/include/xen/sched.h
>>> @@ -472,8 +472,8 @@ struct domain
>>>   #define ROLE_HARDWARE_DOMAIN   (1U<<2)
>>>   #define ROLE_XENSTORE_DOMAIN   (1U<<3)
>>>       uint8_t          role;
>>> -    /* Can this guest access the Xen console? */
>>> -    bool             is_console;
>>> +#define CAP_CONSOLE_IO  (1U<<0)
>> Coding style: Space before and after <<.
> 
> Ack.
> 
>>> +    uint8_t          capabilities;
>>>       /* Is this guest being debugged by dom0? */
>>>       bool             debugger_attached;
>>>       /*
>>> @@ -1146,6 +1146,27 @@ static always_inline bool is_hvm_vcpu(const 
>>> struct vcpu *v)
>>>       return is_hvm_domain(v->domain);
>>>   }
>>> +static always_inline bool domain_has_cap(
>>> +    const struct domain *d, uint8_t cap)
>>
>> Coding style: We don't usually wrap the arguments this way. See 
>> domain_create() for an example.
> 
> I was informed it was[1], also, please see next_domain_in_cpupool() 
> amongst many others further below.

The unwritten coding style strike again... I am not sure where the 
agreement comes from. At least on Arm, we have been using the first 
version in that thread and if it can't be wrapped to 80 characters, then 
move the "static inline void " on its own line.

The advantage with the Arm approach is that parameters are always 
indented the same way. Anyway, the way you wrote is not my personal 
preference but I am also not up to bikeshed too much on it. Hopefully 
this sort of style discussion will be resolved with clang-format.

[...]

> 
>> Also, do you expect the cap to be set only when the domain is created? 
>> If not, would you prevent potentially concurrent update to 
>> d->capabilities?
> 
> Currently the only means being devise to set this is via hyperlaunch 
> domain creation. If a domctl op was added to be able to manipulate the 
> caps, then yes a lock on the domain would be advised to block.

Loking at patch #6, you are using domctl there.

> With that 
> said, if we switch over to CAP_CTRL/HW, then it might be good to grab a 
> lock on the domain for the late hardware domain case.

Are you planning to clear the caps? If not, then using set_bit() and 
test_bit() should be enough.

Cheers,
diff mbox series

Patch

diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
index 51b4daefe1..ad7432b029 100644
--- a/xen/arch/arm/domain_build.c
+++ b/xen/arch/arm/domain_build.c
@@ -4076,7 +4076,9 @@  void __init create_domUs(void)
             panic("Error creating domain %s (rc = %ld)\n",
                   dt_node_name(node), PTR_ERR(d));
 
-        d->is_console = true;
+        if ( ! domain_set_cap(d, CAP_CONSOLE_IO) )
+            printk("failed setting console_io on %pd\n", d);
+
         dt_device_set_used_by(node, d->domain_id);
 
         rc = construct_domU(d, node);
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index ec0f9baff6..b04fbe0565 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -472,8 +472,8 @@  struct domain
 #define ROLE_HARDWARE_DOMAIN   (1U<<2)
 #define ROLE_XENSTORE_DOMAIN   (1U<<3)
     uint8_t          role;
-    /* Can this guest access the Xen console? */
-    bool             is_console;
+#define CAP_CONSOLE_IO  (1U<<0)
+    uint8_t          capabilities;
     /* Is this guest being debugged by dom0? */
     bool             debugger_attached;
     /*
@@ -1146,6 +1146,27 @@  static always_inline bool is_hvm_vcpu(const struct vcpu *v)
     return is_hvm_domain(v->domain);
 }
 
+static always_inline bool domain_has_cap(
+    const struct domain *d, uint8_t cap)
+{
+    return d->capabilities & cap;
+}
+
+static always_inline bool domain_set_cap(
+    struct domain *d, uint8_t cap)
+{
+    switch ( cap )
+    {
+    case CAP_CONSOLE_IO:
+        d->capabilities |= cap;
+        break;
+    default:
+        return false;
+    }
+
+    return domain_has_cap(d, cap);
+}
+
 static always_inline bool hap_enabled(const struct domain *d)
 {
     /* sanitise_domain_config() rejects HAP && !HVM */
diff --git a/xen/include/xsm/dummy.h b/xen/include/xsm/dummy.h
index 18f1ddd127..067ff1d111 100644
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -268,7 +268,7 @@  static XSM_INLINE int cf_check xsm_console_io(
     XSM_DEFAULT_ARG struct domain *d, int cmd)
 {
     XSM_ASSERT_ACTION(XSM_OTHER);
-    if ( d->is_console )
+    if ( domain_has_cap(d, CAP_CONSOLE_IO) )
         return xsm_default_action(XSM_HOOK, d, NULL);
 #ifdef CONFIG_VERBOSE_DEBUG
     if ( cmd == CONSOLEIO_write )