diff mbox series

[PULL,10/17] tests/functional: Convert most Aspeed machine tests

Message ID 20241024063507.1585765-11-clg@redhat.com (mailing list archive)
State New
Headers show
Series [PULL,01/17] hw/gpio/aspeed: Fix coding style | expand

Commit Message

Cédric Le Goater Oct. 24, 2024, 6:35 a.m. UTC
This is a simple conversion of the tests with some cleanups and
adjustments to match the new test framework. Replace the zephyr image
MD5 hashes with SHA256 hashes while at it.

The SDK tests depend on a ssh class from avocado.utils which is
difficult to replace. To be addressed separately.

Signed-off-by: Cédric Le Goater <clg@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Tested-by: Thomas Huth <thuth@redhat.com>
---
 tests/avocado/machine_aspeed.py     | 292 ----------------------------
 tests/functional/meson.build        |   2 +
 tests/functional/test_arm_aspeed.py | 282 +++++++++++++++++++++++++++
 3 files changed, 284 insertions(+), 292 deletions(-)
 create mode 100644 tests/functional/test_arm_aspeed.py

Comments

Peter Maydell Nov. 5, 2024, 4:14 p.m. UTC | #1
On Thu, 24 Oct 2024 at 07:39, Cédric Le Goater <clg@redhat.com> wrote:
>
> This is a simple conversion of the tests with some cleanups and
> adjustments to match the new test framework. Replace the zephyr image
> MD5 hashes with SHA256 hashes while at it.

(ccing Stefan Berger for possible insight into swtpm)

Hi; I find that this swtpm-using test fails for me on my
local system due to an apparmor/swtpm problem...

> +    @skipUnless(*has_cmd('swtpm'))
> +    def test_arm_ast2600_evb_buildroot_tpm(self):
> +        self.set_machine('ast2600-evb')
> +
> +        image_path = self.ASSET_BR2_202302_AST2600_TPM_FLASH.fetch()
> +
> +        socket_dir = tempfile.TemporaryDirectory(prefix="qemu_")
> +        socket = os.path.join(socket_dir.name, 'swtpm-socket')
> +
> +        subprocess.run(['swtpm', 'socket', '-d', '--tpm2',
> +                        '--tpmstate', f'dir={self.vm.temp_dir}',
> +                        '--ctrl', f'type=unixio,path={socket}'])
> +
> +        self.vm.add_args('-chardev', f'socket,id=chrtpm,path={socket}')
> +        self.vm.add_args('-tpmdev', 'emulator,id=tpm0,chardev=chrtpm')
> +        self.vm.add_args('-device',
> +                         'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e')
> +        self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB')
> +
> +        exec_command_and_wait_for_pattern(self,
> +            'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device',
> +            'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)');
> +        exec_command_and_wait_for_pattern(self,
> +            'cat /sys/class/tpm/tpm0/pcr-sha256/0',
> +            'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0');
> +
> +        self.do_test_arm_aspeed_buildroot_poweroff()

The test fails like this:

qemu-system-arm: tpm-emulator: TPM result for CMD_INIT: 0x9 operation failed

Adding extra logging to swtpm (--log file=/tmp/swtpm.log,level=20)
reveals:

SWTPM_NVRAM_Lock_Lockfile: Could not open lockfile: Permission denied
Error: Could not initialize libtpms.
Error: Could not initialize the TPM

Checking the system logs, this is because apparmor has denied it:

Nov  5 16:01:14 e104462 kernel: [946406.489088] audit: type=1400
audit(1730822474.384:446): apparmor="DENIED" operation="mknod"
profile="swtpm"
name="/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/tests/functional/arm/test_arm_aspeed.AST2x00Machine.test_arm_ast2600_evb_buildroot_tpm/qemu-machine-hhuvwytc/.lock"
pid=2820156 comm="swtpm" requested_mask="c" denied_mask="c" fsuid=1000
ouid=1000



Q1: why is apparmor forbidding swtpm from doing something that
it needs to do to work?

Q2: is there a way to run swtpm such that it is not
confined by apparmor, for purposes of running it in a test case?

Q3: if not, is there a way to at least detect that swtpm is
broken on this system so we can skip the test case?

(I note that there is a thing in the apparmor config
"owner @{HOME}/** rwk" which I think means you only run into
this if you happen to be building/testing QEMU somewhere other
than your own home directory -- but that's hardly an
unreasonable configuration...)

thanks
-- PMM
Stefan Berger Nov. 5, 2024, 4:35 p.m. UTC | #2
On 11/5/24 11:14 AM, Peter Maydell wrote:
> On Thu, 24 Oct 2024 at 07:39, Cédric Le Goater <clg@redhat.com> wrote:
>>
>> This is a simple conversion of the tests with some cleanups and
>> adjustments to match the new test framework. Replace the zephyr image
>> MD5 hashes with SHA256 hashes while at it.
> 
> (ccing Stefan Berger for possible insight into swtpm)
> 
> Hi; I find that this swtpm-using test fails for me on my
> local system due to an apparmor/swtpm problem...
> 
>> +    @skipUnless(*has_cmd('swtpm'))
>> +    def test_arm_ast2600_evb_buildroot_tpm(self):
>> +        self.set_machine('ast2600-evb')
>> +
>> +        image_path = self.ASSET_BR2_202302_AST2600_TPM_FLASH.fetch()
>> +
>> +        socket_dir = tempfile.TemporaryDirectory(prefix="qemu_")
>> +        socket = os.path.join(socket_dir.name, 'swtpm-socket')
>> +
>> +        subprocess.run(['swtpm', 'socket', '-d', '--tpm2',
>> +                        '--tpmstate', f'dir={self.vm.temp_dir}',
>> +                        '--ctrl', f'type=unixio,path={socket}'])
>> +
>> +        self.vm.add_args('-chardev', f'socket,id=chrtpm,path={socket}')
>> +        self.vm.add_args('-tpmdev', 'emulator,id=tpm0,chardev=chrtpm')
>> +        self.vm.add_args('-device',
>> +                         'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e')
>> +        self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB')
>> +
>> +        exec_command_and_wait_for_pattern(self,
>> +            'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device',
>> +            'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)');
>> +        exec_command_and_wait_for_pattern(self,
>> +            'cat /sys/class/tpm/tpm0/pcr-sha256/0',
>> +            'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0');
>> +
>> +        self.do_test_arm_aspeed_buildroot_poweroff()
> 
> The test fails like this:
> 
> qemu-system-arm: tpm-emulator: TPM result for CMD_INIT: 0x9 operation failed
> 
> Adding extra logging to swtpm (--log file=/tmp/swtpm.log,level=20)
> reveals:
> 
> SWTPM_NVRAM_Lock_Lockfile: Could not open lockfile: Permission denied
> Error: Could not initialize libtpms.
> Error: Could not initialize the TPM
> 
> Checking the system logs, this is because apparmor has denied it:
> 
> Nov  5 16:01:14 e104462 kernel: [946406.489088] audit: type=1400
> audit(1730822474.384:446): apparmor="DENIED" operation="mknod"
> profile="swtpm"
> name="/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/tests/functional/arm/test_arm_aspeed.AST2x00Machine.test_arm_ast2600_evb_buildroot_tpm/qemu-machine-hhuvwytc/.lock"
> pid=2820156 comm="swtpm" requested_mask="c" denied_mask="c" fsuid=1000
> ouid=1000
> 
> 
> 
> Q1: why is apparmor forbidding swtpm from doing something that
> it needs to do to work?

What distro and version is this?

The profile may be too strict and not reflecting all the paths needed 
for running the test cases. Ubuntu for example would have to update 
their profile in such a case.

> 
> Q2: is there a way to run swtpm such that it is not
> confined by apparmor, for purposes of running it in a test case?

Try either one:
- sudo aa-complain /usr/bin/swtpm
- sudo aa-disable /usr/bin/swtpm

> 
> Q3: if not, is there a way to at least detect that swtpm is
> broken on this system so we can skip the test case?

It's not swtpm that is broken but the AppArmor profile is too strict. 
Above command lines should work.

> 
> (I note that there is a thing in the apparmor config
> "owner @{HOME}/** rwk" which I think means you only run into
> this if you happen to be building/testing QEMU somewhere other
> than your own home directory -- but that's hardly an
> unreasonable configuration...)
> 
> thanks
> -- PMM
Peter Maydell Nov. 5, 2024, 5:13 p.m. UTC | #3
On Tue, 5 Nov 2024 at 17:02, Stefan Berger <stefanb@linux.ibm.com> wrote:
> On 11/5/24 11:14 AM, Peter Maydell wrote:
> > Q1: why is apparmor forbidding swtpm from doing something that
> > it needs to do to work?
>
> What distro and version is this?
>
> The profile may be too strict and not reflecting all the paths needed
> for running the test cases. Ubuntu for example would have to update
> their profile in such a case.

This is Ubuntu 22.04 "jammy" (with swtpm 0.6.3-0ubuntu3.3).

> > Q2: is there a way to run swtpm such that it is not
> > confined by apparmor, for purposes of running it in a test case?
>
> Try either one:
> - sudo aa-complain /usr/bin/swtpm
> - sudo aa-disable /usr/bin/swtpm

We don't have root access from QEMU's 'make check',
though (and shouldn't be globally disabling apparmor
even if we could). I had in mind more a way that an
individual user can say "run this swtpm process but don't
apply the apparmor profile to it".

> > Q3: if not, is there a way to at least detect that swtpm is
> > broken on this system so we can skip the test case?
>
> It's not swtpm that is broken but the AppArmor profile is too strict.
> Above command lines should work.

But this is a widely deployed distro in its default
configuration. We have to either work with it or detect
that it's broken so we can skip the test.

thanks
-- PMM
Stefan Berger Nov. 5, 2024, 6:02 p.m. UTC | #4
On 11/5/24 12:13 PM, Peter Maydell wrote:
> On Tue, 5 Nov 2024 at 17:02, Stefan Berger <stefanb@linux.ibm.com> wrote:
>> On 11/5/24 11:14 AM, Peter Maydell wrote:
>>> Q1: why is apparmor forbidding swtpm from doing something that
>>> it needs to do to work?
>>
>> What distro and version is this?
>>
>> The profile may be too strict and not reflecting all the paths needed
>> for running the test cases. Ubuntu for example would have to update
>> their profile in such a case.
> 
> This is Ubuntu 22.04 "jammy" (with swtpm 0.6.3-0ubuntu3.3).
> 
>>> Q2: is there a way to run swtpm such that it is not
>>> confined by apparmor, for purposes of running it in a test case?
>>
>> Try either one:
>> - sudo aa-complain /usr/bin/swtpm
>> - sudo aa-disable /usr/bin/swtpm
> 
> We don't have root access from QEMU's 'make check',
> though (and shouldn't be globally disabling apparmor
> even if we could). I had in mind more a way that an
> individual user can say "run this swtpm process but don't
> apply the apparmor profile to it".

So the problem is that the avocado tests are using /var/tmp but we only 
have AppArmor rules for /tmp/

The following solutions should work:
- do not install swtpm at all
- sudo cp /usr/bin/swtpm  /usr/local/bin/swtpm
- as root: echo "include <abstractions/user-tmp>" >> 
/etc/apparmor.d/local/usr.bin.swtpm && apparmor_parser -r 
/etc/apparmor.d/usr.bin.swtpm


Lena, it looks like we would need the following additional line in the 
profile:

include <abstractions/user-tmp>

> 
>>> Q3: if not, is there a way to at least detect that swtpm is
>>> broken on this system so we can skip the test case?
>>
>> It's not swtpm that is broken but the AppArmor profile is too strict.
>> Above command lines should work.
> 
> But this is a widely deployed distro in its default
> configuration. We have to either work with it or detect
> that it's broken so we can skip the test.

Cc'in Lena from Ubuntu.

> 
> thanks
> -- PMM
Peter Maydell Nov. 5, 2024, 6:12 p.m. UTC | #5
On Tue, 5 Nov 2024 at 18:02, Stefan Berger <stefanb@linux.ibm.com> wrote:
>
>
>
> On 11/5/24 12:13 PM, Peter Maydell wrote:
> > On Tue, 5 Nov 2024 at 17:02, Stefan Berger <stefanb@linux.ibm.com> wrote:
> >> On 11/5/24 11:14 AM, Peter Maydell wrote:
> >>> Q1: why is apparmor forbidding swtpm from doing something that
> >>> it needs to do to work?
> >>
> >> What distro and version is this?
> >>
> >> The profile may be too strict and not reflecting all the paths needed
> >> for running the test cases. Ubuntu for example would have to update
> >> their profile in such a case.
> >
> > This is Ubuntu 22.04 "jammy" (with swtpm 0.6.3-0ubuntu3.3).
> >
> >>> Q2: is there a way to run swtpm such that it is not
> >>> confined by apparmor, for purposes of running it in a test case?
> >>
> >> Try either one:
> >> - sudo aa-complain /usr/bin/swtpm
> >> - sudo aa-disable /usr/bin/swtpm
> >
> > We don't have root access from QEMU's 'make check',
> > though (and shouldn't be globally disabling apparmor
> > even if we could). I had in mind more a way that an
> > individual user can say "run this swtpm process but don't
> > apply the apparmor profile to it".
>
> So the problem is that the avocado tests are using /var/tmp but we only
> have AppArmor rules for /tmp/

The file AppArmor gives the error for is not in /var/tmp:
it's in a local directory inside QEMU's build dir:

Nov  5 16:01:14 e104462 kernel: [946406.489088] audit: type=1400
audit(1730822474.384:446): apparmor="DENIED" operation="mknod"
profile="swtpm"
name="/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/tests/functional/arm/test_arm_aspeed.AST2x00Machine.test_arm_ast2600_evb_buildroot_tpm/qemu-machine-hhuvwytc/.lock"
pid=2820156 comm="swtpm" requested_mask="c" denied_mask="c" fsuid=1000
ouid=1000
> The following solutions should work:
> - do not install swtpm at all
> - sudo cp /usr/bin/swtpm  /usr/local/bin/swtpm
> - as root: echo "include <abstractions/user-tmp>" >>
> /etc/apparmor.d/local/usr.bin.swtpm && apparmor_parser -r
> /etc/apparmor.d/usr.bin.swtpm

Is there no way to just have apparmor not apply at all
here? I can see why you might want it to apply for the
case of "I'm using it as part of a sandboxed VM setup",
but in this scenario I am a local user running this binary
which is not setuid root and it is accessing a file in a
directory which my user owns and has permissions for.
This should not be being rejected: there is no security
boundary involved and swtpm is not doing anything
that I could not directly do myself anyway (as you
can tell from the fact that copying the swtpm binary
to a different location and running it works).

thanks
-- PMM
Stefan Berger Nov. 5, 2024, 6:35 p.m. UTC | #6
On 11/5/24 1:12 PM, Peter Maydell wrote:
> On Tue, 5 Nov 2024 at 18:02, Stefan Berger <stefanb@linux.ibm.com> wrote:
>>
>>
>>
>> On 11/5/24 12:13 PM, Peter Maydell wrote:
>>> On Tue, 5 Nov 2024 at 17:02, Stefan Berger <stefanb@linux.ibm.com> wrote:
>>>> On 11/5/24 11:14 AM, Peter Maydell wrote:
>>>>> Q1: why is apparmor forbidding swtpm from doing something that
>>>>> it needs to do to work?
>>>>
>>>> What distro and version is this?
>>>>
>>>> The profile may be too strict and not reflecting all the paths needed
>>>> for running the test cases. Ubuntu for example would have to update
>>>> their profile in such a case.
>>>
>>> This is Ubuntu 22.04 "jammy" (with swtpm 0.6.3-0ubuntu3.3).
>>>
>>>>> Q2: is there a way to run swtpm such that it is not
>>>>> confined by apparmor, for purposes of running it in a test case?
>>>>
>>>> Try either one:
>>>> - sudo aa-complain /usr/bin/swtpm
>>>> - sudo aa-disable /usr/bin/swtpm
>>>
>>> We don't have root access from QEMU's 'make check',
>>> though (and shouldn't be globally disabling apparmor
>>> even if we could). I had in mind more a way that an
>>> individual user can say "run this swtpm process but don't
>>> apply the apparmor profile to it".
>>
>> So the problem is that the avocado tests are using /var/tmp but we only
>> have AppArmor rules for /tmp/
> 
> The file AppArmor gives the error for is not in /var/tmp:
> it's in a local directory inside QEMU's build dir:
> 
> Nov  5 16:01:14 e104462 kernel: [946406.489088] audit: type=1400
> audit(1730822474.384:446): apparmor="DENIED" operation="mknod"
> profile="swtpm"
> name="/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/tests/functional/arm/test_arm_aspeed.AST2x00Machine.test_arm_ast2600_evb_buildroot_tpm/qemu-machine-hhuvwytc/.lock"
> pid=2820156 comm="swtpm" requested_mask="c" denied_mask="c" fsuid=1000
> ouid=1000
>> The following solutions should work:
>> - do not install swtpm at all
>> - sudo cp /usr/bin/swtpm  /usr/local/bin/swtpm
>> - as root: echo "include <abstractions/user-tmp>" >>
>> /etc/apparmor.d/local/usr.bin.swtpm && apparmor_parser -r
>> /etc/apparmor.d/usr.bin.swtpm
> 
> Is there no way to just have apparmor not apply at all
> here? I can see why you might want it to apply for the

If you are root you can change things. I have shown the options using 
aa-complain and aa-disable that you can revert once the test has 
finished: sudo aa-enforce /usr/bin/swtpm

You could also copy swtpm into a user-owned directory but you will have 
to adapt the user's PATH. That's an easy option.

The most compatible option is the 3rd option since I would expect that 
we will have this rule in a future version of the usr.bin.swtpm Ubuntu 
profile provided by the swtpm package:

echo "include <abstractions/user-tmp>" >> 
/etc/apparmor.d/local/usr.bin.swtpm
apparmor_parser -r /etc/apparmor.d/usr.bin.swtpm

> case of "I'm using it as part of a sandboxed VM setup",
> but in this scenario I am a local user running this binary
> which is not setuid root and it is accessing a file in a
> directory which my user owns and has permissions for.
> This should not be being rejected: there is no security
> boundary involved and swtpm is not doing anything
> that I could not directly do myself anyway (as you
> can tell from the fact that copying the swtpm binary
> to a different location and running it works).

I am not aware of how user/non-root-started programs can be generally 
made exempt from AppArmor.

There may still be a security boundary if a user runs QEMU and swtpm was 
able to manipulate (with malicious input) the user's files in some 
undesirable way or copy the user's data elsewhere. In this case it may 
be desirable for the user that the profile be applied and the PATH he is 
using points to the standard swtpm.

> 
> thanks
> -- PMM
>
Peter Maydell Nov. 5, 2024, 7:54 p.m. UTC | #7
On Tue, 5 Nov 2024 at 18:36, Stefan Berger <stefanb@linux.ibm.com> wrote:
> On 11/5/24 1:12 PM, Peter Maydell wrote:
> > On Tue, 5 Nov 2024 at 18:02, Stefan Berger <stefanb@linux.ibm.com> wrote:
> >> On 11/5/24 12:13 PM, Peter Maydell wrote:
> > Is there no way to just have apparmor not apply at all
> > here? I can see why you might want it to apply for the
>
> If you are root you can change things. I have shown the options using
> aa-complain and aa-disable that you can revert once the test has
> finished: sudo aa-enforce /usr/bin/swtpm
>
> You could also copy swtpm into a user-owned directory but you will have
> to adapt the user's PATH. That's an easy option.
>
> The most compatible option is the 3rd option since I would expect that
> we will have this rule in a future version of the usr.bin.swtpm Ubuntu
> profile provided by the swtpm package:
>
> echo "include <abstractions/user-tmp>" >>
> /etc/apparmor.d/local/usr.bin.swtpm
> apparmor_parser -r /etc/apparmor.d/usr.bin.swtpm
>
> > case of "I'm using it as part of a sandboxed VM setup",
> > but in this scenario I am a local user running this binary
> > which is not setuid root and it is accessing a file in a
> > directory which my user owns and has permissions for.
> > This should not be being rejected: there is no security
> > boundary involved and swtpm is not doing anything
> > that I could not directly do myself anyway (as you
> > can tell from the fact that copying the swtpm binary
> > to a different location and running it works).
>
> I am not aware of how user/non-root-started programs can be generally
> made exempt from AppArmor.
>
> There may still be a security boundary if a user runs QEMU and swtpm was
> able to manipulate (with malicious input) the user's files in some
> undesirable way or copy the user's data elsewhere. In this case it may
> be desirable for the user that the profile be applied and the PATH he is
> using points to the standard swtpm.

But our test makefiles could equally well just run "cp" !
swtpm has no privilege here that we do not already have.

Anyway, the thing here is that we run swtpm like this:

 swtpm socket -d  --tpm2 --tpmstate dir=/path/to/somewhere --ctrl
type=unixio,path=/path/to/socket

where we use command line arguments to tell it where to
put the tpmstate and the socket.

Either:
 (1) there are places where it's not valid for us to tell swtpm to
put the tpmstate or to put the control socket
 (2) it's valid to put those anywhere we like

If (1), then swtpm should give a clear error message that we've
given it an invalid argument (and its manpage should say what
the restrictions are)
If (2), then apparmor should not be rejecting this usage

One of swtpm or apparmor must be wrong here and I think it should
be fixed. In particular, having the failure mode be "something
goes wrong after swtpm has successfully started and only once
it gets sent the TPM_INIT command, and the information about it
only winds up in the syslog" is pretty awkward -- it would
be much nicer if it failed fast, as soon as you ran it, and
printed the error to stderr.

In the interim, since we'd like to be able to run the test suite
on Ubuntu, it sounds like we can work around this by putting
the tpmstate and socket in either /tmp/ or somewhere under
the user's home directory.

thanks
-- PMM
Stefan Berger Nov. 5, 2024, 8:12 p.m. UTC | #8
On 11/5/24 2:54 PM, Peter Maydell wrote:
> On Tue, 5 Nov 2024 at 18:36, Stefan Berger <stefanb@linux.ibm.com> wrote:
>> On 11/5/24 1:12 PM, Peter Maydell wrote:
>>> On Tue, 5 Nov 2024 at 18:02, Stefan Berger <stefanb@linux.ibm.com> wrote:
>>>> On 11/5/24 12:13 PM, Peter Maydell wrote:
>>> Is there no way to just have apparmor not apply at all
>>> here? I can see why you might want it to apply for the
>>
>> If you are root you can change things. I have shown the options using
>> aa-complain and aa-disable that you can revert once the test has
>> finished: sudo aa-enforce /usr/bin/swtpm
>>
>> You could also copy swtpm into a user-owned directory but you will have
>> to adapt the user's PATH. That's an easy option.
>>
>> The most compatible option is the 3rd option since I would expect that
>> we will have this rule in a future version of the usr.bin.swtpm Ubuntu
>> profile provided by the swtpm package:
>>
>> echo "include <abstractions/user-tmp>" >>
>> /etc/apparmor.d/local/usr.bin.swtpm
>> apparmor_parser -r /etc/apparmor.d/usr.bin.swtpm
>>
>>> case of "I'm using it as part of a sandboxed VM setup",
>>> but in this scenario I am a local user running this binary
>>> which is not setuid root and it is accessing a file in a
>>> directory which my user owns and has permissions for.
>>> This should not be being rejected: there is no security
>>> boundary involved and swtpm is not doing anything
>>> that I could not directly do myself anyway (as you
>>> can tell from the fact that copying the swtpm binary
>>> to a different location and running it works).
>>
>> I am not aware of how user/non-root-started programs can be generally
>> made exempt from AppArmor.
>>
>> There may still be a security boundary if a user runs QEMU and swtpm was
>> able to manipulate (with malicious input) the user's files in some
>> undesirable way or copy the user's data elsewhere. In this case it may
>> be desirable for the user that the profile be applied and the PATH he is
>> using points to the standard swtpm.
> 
> But our test makefiles could equally well just run "cp" !
> swtpm has no privilege here that we do not already have.
> 
> Anyway, the thing here is that we run swtpm like this:
> 
>   swtpm socket -d  --tpm2 --tpmstate dir=/path/to/somewhere --ctrl
> type=unixio,path=/path/to/socket
> 
> where we use command line arguments to tell it where to
> put the tpmstate and the socket.
> 
> Either:
>   (1) there are places where it's not valid for us to tell swtpm to
> put the tpmstate or to put the control socket
>   (2) it's valid to put those anywhere we like
> 
> If (1), then swtpm should give a clear error message that we've
> given it an invalid argument (and its manpage should say what
> the restrictions are)

There are no restrictions on the swtpm level when it comes to paths.

> If (2), then apparmor should not be rejecting this usage

AppArmor file restrictions are all path based. We have support for home 
directory and /tmp, but were missing /var/tmp. So, please.

 > > One of swtpm or apparmor must be wrong here and I think it should
> be fixed. In particular, having the failure mode be "something

As stated, we were going to fix the AppArmor path in the swtpm Ubuntu 
package.

> goes wrong after swtpm has successfully started and only once
> it gets sent the TPM_INIT command, and the information about it
> only winds up in the syslog" is pretty awkward -- it would
> be much nicer if it failed fast, as soon as you ran it, and
> printed the error to stderr.
> 
> In the interim, since we'd like to be able to run the test suite
> on Ubuntu, it sounds like we can work around this by putting
> the tpmstate and socket in either /tmp/ or somewhere under
> the user's home directory.

Right, I gave several options.

> 
> thanks
> -- PMM
>
Peter Maydell Nov. 5, 2024, 9:34 p.m. UTC | #9
On Tue, 5 Nov 2024 at 20:12, Stefan Berger <stefanb@linux.ibm.com> wrote:
> On 11/5/24 2:54 PM, Peter Maydell wrote:
> > On Tue, 5 Nov 2024 at 18:36, Stefan Berger <stefanb@linux.ibm.com> wrote:
> > Anyway, the thing here is that we run swtpm like this:
> >
> >   swtpm socket -d  --tpm2 --tpmstate dir=/path/to/somewhere --ctrl
> > type=unixio,path=/path/to/socket
> >
> > where we use command line arguments to tell it where to
> > put the tpmstate and the socket.
> >
> > Either:
> >   (1) there are places where it's not valid for us to tell swtpm to
> > put the tpmstate or to put the control socket
> >   (2) it's valid to put those anywhere we like
> >
> > If (1), then swtpm should give a clear error message that we've
> > given it an invalid argument (and its manpage should say what
> > the restrictions are)
>
> There are no restrictions on the swtpm level when it comes to paths.

> > If (2), then apparmor should not be rejecting this usage
>
> AppArmor file restrictions are all path based. We have support for home
> directory and /tmp, but were missing /var/tmp. So, please.
>
>  > > One of swtpm or apparmor must be wrong here and I think it should
> > be fixed. In particular, having the failure mode be "something
>
> As stated, we were going to fix the AppArmor path in the swtpm Ubuntu
> package.

But AIUI the solution you've proposed is to add the user
temp directory -- abstractions/user-tmp looks like it
adds permissions for $HOME/tmp, /var/tmp and /tmp/. None
of those will fix the failure we ran into, because we're not
using any of those tmp directories. We use a directory
that's a subdirectory of wherever the user put the build
directory, which can be anywhere the user has permissions for.

That's why I'm confused -- as far as I can see the only
way to make swtpm work the way its documentation says it
should work is to for apparmor to permit anything
(or at least to permit anything that matches the file paths
the user handed swtmp, if it can do that).

Or if you want to say "this has to be in one of these
handful of authorised /tmp/ directories", then it should
say that in the manpage and check that at init time, not fail
near-silently much later. At the moment the docs and the
distro-integration of swtmp disagree, and the effect for
somebody trying to use it is very confusing.

thanks
-- PMM
Stefan Berger Nov. 5, 2024, 9:50 p.m. UTC | #10
On 11/5/24 4:34 PM, Peter Maydell wrote:
> On Tue, 5 Nov 2024 at 20:12, Stefan Berger <stefanb@linux.ibm.com> wrote:
>> On 11/5/24 2:54 PM, Peter Maydell wrote:
>>> On Tue, 5 Nov 2024 at 18:36, Stefan Berger <stefanb@linux.ibm.com> wrote:
>>> Anyway, the thing here is that we run swtpm like this:
>>>
>>>    swtpm socket -d  --tpm2 --tpmstate dir=/path/to/somewhere --ctrl
>>> type=unixio,path=/path/to/socket
>>>
>>> where we use command line arguments to tell it where to
>>> put the tpmstate and the socket.
>>>
>>> Either:
>>>    (1) there are places where it's not valid for us to tell swtpm to
>>> put the tpmstate or to put the control socket
>>>    (2) it's valid to put those anywhere we like
>>>
>>> If (1), then swtpm should give a clear error message that we've
>>> given it an invalid argument (and its manpage should say what
>>> the restrictions are)
>>
>> There are no restrictions on the swtpm level when it comes to paths.
> 
>>> If (2), then apparmor should not be rejecting this usage
>>
>> AppArmor file restrictions are all path based. We have support for home
>> directory and /tmp, but were missing /var/tmp. So, please.
>>
>>   > > One of swtpm or apparmor must be wrong here and I think it should
>>> be fixed. In particular, having the failure mode be "something
>>
>> As stated, we were going to fix the AppArmor path in the swtpm Ubuntu
>> package.
> 
> But AIUI the solution you've proposed is to add the user
> temp directory -- abstractions/user-tmp looks like it
> adds permissions for $HOME/tmp, /var/tmp and /tmp/. None
> of those will fix the failure we ran into, because we're not
> using any of those tmp directories. We use a directory
> that's a subdirectory of wherever the user put the build
> directory, which can be anywhere the user has permissions for.

Yes, you are right. The same test failed for me locally due to the usage 
of /var/tmp/ path but that's not what was originally reported.

I am not aware that user-started programs can have an exception from 
having their profiles applied, nor do I know whether rules exist that 
allow a user to circumvent any rule. So my guess is we need rules like 
either one of the following:

owner /mnt/** rwkl

or worse:

owner /** rwkl

I don't see another choice than adding one of these rules, maybe even 
the 2nd. Lena?

> 
> That's why I'm confused -- as far as I can see the only
> way to make swtpm work the way its documentation says it
> should work is to for apparmor to permit anything
> (or at least to permit anything that matches the file paths
> the user handed swtmp, if it can do that).

and from what I know we need explicit rules for allowing paths.

> 
> Or if you want to say "this has to be in one of these
> handful of authorised /tmp/ directories", then it should
> say that in the manpage and check that at init time, not fail
> near-silently much later. At the moment the docs and the
> distro-integration of swtmp disagree, and the effect for
> somebody trying to use it is very confusing.

We haven't run into this type of a problem with paths in a while. The 
applications return 'permission denied' but to find the exact reason 
(LSM) for it one may have to dig into the audit log.

> 
> thanks
> -- PMM
Stefan Berger Nov. 6, 2024, 3:21 p.m. UTC | #11
On 11/5/24 4:50 PM, Stefan Berger wrote:
> 
> 

>>>
>>>   > > One of swtpm or apparmor must be wrong here and I think it should
>>>> be fixed. In particular, having the failure mode be "something
>>>
>>> As stated, we were going to fix the AppArmor path in the swtpm Ubuntu
>>> package.
>>
>> But AIUI the solution you've proposed is to add the user
>> temp directory -- abstractions/user-tmp looks like it
>> adds permissions for $HOME/tmp, /var/tmp and /tmp/. None
>> of those will fix the failure we ran into, because we're not
>> using any of those tmp directories. We use a directory
>> that's a subdirectory of wherever the user put the build
>> directory, which can be anywhere the user has permissions for.
> 
> Yes, you are right. The same test failed for me locally due to the usage 
> of /var/tmp/ path but that's not what was originally reported.
> 
> I am not aware that user-started programs can have an exception from 
> having their profiles applied, nor do I know whether rules exist that 
> allow a user to circumvent any rule. So my guess is we need rules like 
> either one of the following:
> 
> owner /mnt/** rwkl
> 
> or worse:
> 
> owner /** rwkl
> 
> I don't see another choice than adding one of these rules, maybe even 
> the 2nd. Lena?

If there was value in the path-confinement of swtpm (for a few years) do 
we really want to loose it now because of a test case? We could either

- adjust the test case to have swtpm use a directory under one of the 
accepted paths, e.g., /tmp or /var/tmp
- or add /mnt as a newly supported path to the AppArmor profile

The latter works for the setup that Peter has but a new user creating 
paths under /mymnt would cause the same discussion again.


    Stefan
diff mbox series

Patch

diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py
index 4e144bde9131..241ef180affc 100644
--- a/tests/avocado/machine_aspeed.py
+++ b/tests/avocado/machine_aspeed.py
@@ -19,258 +19,6 @@ 
 from avocado_qemu import has_cmd
 from avocado.utils import archive
 from avocado import skipUnless
-from avocado import skipUnless
-
-
-class AST1030Machine(QemuSystemTest):
-    """Boots the zephyr os and checks that the console is operational"""
-
-    timeout = 10
-
-    def test_ast1030_zephyros_1_04(self):
-        """
-        :avocado: tags=arch:arm
-        :avocado: tags=machine:ast1030-evb
-        :avocado: tags=os:zephyr
-        """
-        tar_url = ('https://github.com/AspeedTech-BMC'
-                   '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip')
-        tar_hash = '4c6a8ce3a8ba76ef1a65dae419ae3409343c4b20'
-        tar_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
-        archive.extract(tar_path, self.workdir)
-        kernel_file = self.workdir + "/ast1030-evb-demo/zephyr.elf"
-        self.vm.set_console()
-        self.vm.add_args('-kernel', kernel_file,
-                         '-nographic')
-        self.vm.launch()
-        wait_for_console_pattern(self, "Booting Zephyr OS")
-        exec_command_and_wait_for_pattern(self, "help",
-                                          "Available commands")
-
-    def test_ast1030_zephyros_1_07(self):
-        """
-        :avocado: tags=arch:arm
-        :avocado: tags=machine:ast1030-evb
-        :avocado: tags=os:zephyr
-        """
-        tar_url = ('https://github.com/AspeedTech-BMC'
-                   '/zephyr/releases/download/v00.01.07/ast1030-evb-demo.zip')
-        tar_hash = '40ac87eabdcd3b3454ce5aad11fedc72a33ecda2'
-        tar_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
-        archive.extract(tar_path, self.workdir)
-        kernel_file = self.workdir + "/ast1030-evb-demo/zephyr.bin"
-        self.vm.set_console()
-        self.vm.add_args('-kernel', kernel_file,
-                         '-nographic')
-        self.vm.launch()
-        wait_for_console_pattern(self, "Booting Zephyr OS")
-        for shell_cmd in [
-                'kernel stacks',
-                'otp info conf',
-                'otp info scu',
-                'hwinfo devid',
-                'crypto aes256_cbc_vault',
-                'random get',
-                'jtag JTAG1 sw_xfer high TMS',
-                'adc ADC0 resolution 12',
-                'adc ADC0 read 42',
-                'adc ADC1 read 69',
-                'i2c scan I2C_0',
-                'i3c attach I3C_0',
-                'hash test',
-                'kernel uptime',
-                'kernel reboot warm',
-                'kernel uptime',
-                'kernel reboot cold',
-                'kernel uptime',
-        ]: exec_command_and_wait_for_pattern(self, shell_cmd, "uart:~$")
-
-class AST2x00Machine(QemuSystemTest):
-
-    timeout = 180
-
-    def wait_for_console_pattern(self, success_message, vm=None):
-        wait_for_console_pattern(self, success_message,
-                                 failure_message='Kernel panic - not syncing',
-                                 vm=vm)
-
-    def do_test_arm_aspeed(self, image):
-        self.vm.set_console()
-        self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw',
-                         '-net', 'nic')
-        self.vm.launch()
-
-        self.wait_for_console_pattern("U-Boot 2016.07")
-        self.wait_for_console_pattern("## Loading kernel from FIT Image at 20080000")
-        self.wait_for_console_pattern("Starting kernel ...")
-        self.wait_for_console_pattern("Booting Linux on physical CPU 0x0")
-        wait_for_console_pattern(self,
-                "aspeed-smc 1e620000.spi: read control register: 203b0641")
-        self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ")
-        self.wait_for_console_pattern("systemd[1]: Set hostname to")
-
-    def test_arm_ast2400_palmetto_openbmc_v2_9_0(self):
-        """
-        :avocado: tags=arch:arm
-        :avocado: tags=machine:palmetto-bmc
-        """
-
-        image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/'
-                     'obmc-phosphor-image-palmetto.static.mtd')
-        image_hash = ('3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d')
-        image_path = self.fetch_asset(image_url, asset_hash=image_hash,
-                                      algorithm='sha256')
-
-        self.do_test_arm_aspeed(image_path)
-
-    def test_arm_ast2500_romulus_openbmc_v2_9_0(self):
-        """
-        :avocado: tags=arch:arm
-        :avocado: tags=machine:romulus-bmc
-        """
-
-        image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/'
-                     'obmc-phosphor-image-romulus.static.mtd')
-        image_hash = ('820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25')
-        image_path = self.fetch_asset(image_url, asset_hash=image_hash,
-                                      algorithm='sha256')
-
-        self.do_test_arm_aspeed(image_path)
-
-    def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'):
-        self.require_netdev('user')
-
-        self.vm.set_console()
-        self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw',
-                         '-net', 'nic', '-net', 'user')
-        self.vm.launch()
-
-        self.wait_for_console_pattern('U-Boot 2019.04')
-        self.wait_for_console_pattern('## Loading kernel from FIT Image')
-        self.wait_for_console_pattern('Starting kernel ...')
-        self.wait_for_console_pattern('Booting Linux on physical CPU ' + cpu_id)
-        self.wait_for_console_pattern('lease of 10.0.2.15')
-        # the line before login:
-        self.wait_for_console_pattern(pattern)
-        time.sleep(0.1)
-        exec_command(self, 'root')
-        time.sleep(0.1)
-        exec_command(self, "passw0rd")
-
-    def do_test_arm_aspeed_buildroot_poweroff(self):
-        exec_command_and_wait_for_pattern(self, 'poweroff',
-                                          'reboot: System halted');
-
-    def test_arm_ast2500_evb_buildroot(self):
-        """
-        :avocado: tags=arch:arm
-        :avocado: tags=machine:ast2500-evb
-        """
-
-        image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/'
-                     'images/ast2500-evb/buildroot-2023.11/flash.img')
-        image_hash = ('c23db6160cf77d0258397eb2051162c8473a56c441417c52a91ba217186e715f')
-        image_path = self.fetch_asset(image_url, asset_hash=image_hash,
-                                      algorithm='sha256')
-
-        self.vm.add_args('-device',
-                         'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test');
-        self.do_test_arm_aspeed_buildroot_start(image_path, '0x0', 'Aspeed AST2500 EVB')
-
-        exec_command_and_wait_for_pattern(self,
-             'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device',
-             'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d');
-        exec_command_and_wait_for_pattern(self,
-                             'cat /sys/class/hwmon/hwmon1/temp1_input', '0')
-        self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test',
-                    property='temperature', value=18000);
-        exec_command_and_wait_for_pattern(self,
-                             'cat /sys/class/hwmon/hwmon1/temp1_input', '18000')
-
-        self.do_test_arm_aspeed_buildroot_poweroff()
-
-    def test_arm_ast2600_evb_buildroot(self):
-        """
-        :avocado: tags=arch:arm
-        :avocado: tags=machine:ast2600-evb
-        """
-
-        image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/'
-                     'images/ast2600-evb/buildroot-2023.11/flash.img')
-        image_hash = ('b62808daef48b438d0728ee07662290490ecfa65987bb91294cafb1bb7ad1a68')
-        image_path = self.fetch_asset(image_url, asset_hash=image_hash,
-                                      algorithm='sha256')
-
-        self.vm.add_args('-device',
-                         'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test');
-        self.vm.add_args('-device',
-                         'ds1338,bus=aspeed.i2c.bus.3,address=0x32');
-        self.vm.add_args('-device',
-                         'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42');
-        self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB')
-
-        exec_command_and_wait_for_pattern(self,
-             'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device',
-             'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d');
-        exec_command_and_wait_for_pattern(self,
-                             'cat /sys/class/hwmon/hwmon1/temp1_input', '0')
-        self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test',
-                    property='temperature', value=18000);
-        exec_command_and_wait_for_pattern(self,
-                             'cat /sys/class/hwmon/hwmon1/temp1_input', '18000')
-
-        exec_command_and_wait_for_pattern(self,
-             'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-3/device/new_device',
-             'i2c i2c-3: new_device: Instantiated device ds1307 at 0x32');
-        year = time.strftime("%Y")
-        exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year);
-
-        exec_command_and_wait_for_pattern(self,
-             'echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-3/new_device',
-             'i2c i2c-3: new_device: Instantiated device slave-24c02 at 0x64');
-        exec_command(self, 'i2cset -y 3 0x42 0x64 0x00 0xaa i');
-        time.sleep(0.1)
-        exec_command_and_wait_for_pattern(self,
-             'hexdump /sys/bus/i2c/devices/3-1064/slave-eeprom',
-             '0000000 ffaa ffff ffff ffff ffff ffff ffff ffff');
-        self.do_test_arm_aspeed_buildroot_poweroff()
-
-    @skipUnless(*has_cmd('swtpm'))
-    def test_arm_ast2600_evb_buildroot_tpm(self):
-        """
-        :avocado: tags=arch:arm
-        :avocado: tags=machine:ast2600-evb
-        """
-
-        image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/'
-                     'images/ast2600-evb/buildroot-2023.02-tpm/flash.img')
-        image_hash = ('a46009ae8a5403a0826d607215e731a8c68d27c14c41e55331706b8f9c7bd997')
-        image_path = self.fetch_asset(image_url, asset_hash=image_hash,
-                                      algorithm='sha256')
-
-        # force creation of VM object, which also defines self._sd
-        vm = self.vm
-
-        socket = os.path.join(self._sd.name, 'swtpm-socket')
-
-        subprocess.run(['swtpm', 'socket', '-d', '--tpm2',
-                        '--tpmstate', f'dir={self.vm.temp_dir}',
-                        '--ctrl', f'type=unixio,path={socket}'])
-
-        self.vm.add_args('-chardev', f'socket,id=chrtpm,path={socket}')
-        self.vm.add_args('-tpmdev', 'emulator,id=tpm0,chardev=chrtpm')
-        self.vm.add_args('-device',
-                         'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e')
-        self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB')
-
-        exec_command_and_wait_for_pattern(self,
-            'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device',
-            'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)');
-        exec_command_and_wait_for_pattern(self,
-            'cat /sys/class/tpm/tpm0/pcr-sha256/0',
-            'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0');
-
-        self.do_test_arm_aspeed_buildroot_poweroff()
 
 class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn):
 
@@ -452,43 +200,3 @@  def test_aarch64_ast2700_evb_sdk_v09_02(self):
                     property='temperature', value=18000)
         self.ssh_command_output_contains(
             'cat /sys/class/hwmon/hwmon20/temp1_input', '18000')
-
-class AST2x00MachineMMC(QemuSystemTest):
-
-    timeout = 240
-
-    def wait_for_console_pattern(self, success_message, vm=None):
-        wait_for_console_pattern(self, success_message,
-                                 failure_message='Kernel panic - not syncing',
-                                 vm=vm)
-
-    def test_arm_aspeed_emmc_boot(self):
-        """
-        :avocado: tags=arch:arm
-        :avocado: tags=machine:rainier-bmc
-        :avocado: tags=device:emmc
-        """
-
-        image_url = ('https://fileserver.linaro.org/s/B6pJTwWEkzSDi36/download/'
-                     'mmc-p10bmc-20240617.qcow2')
-        image_hash = ('d523fb478d2b84d5adc5658d08502bc64b1486955683814f89c6137518acd90b')
-        image_path = self.fetch_asset(image_url, asset_hash=image_hash,
-                                      algorithm='sha256')
-
-        self.require_netdev('user')
-
-        self.vm.set_console()
-        self.vm.add_args('-drive',
-                         'file=' + image_path + ',if=sd,id=sd2,index=2',
-                         '-net', 'nic', '-net', 'user')
-        self.vm.launch()
-
-        self.wait_for_console_pattern('U-Boot SPL 2019.04')
-        self.wait_for_console_pattern('Trying to boot from MMC1')
-        self.wait_for_console_pattern('U-Boot 2019.04')
-        self.wait_for_console_pattern('eMMC 2nd Boot')
-        self.wait_for_console_pattern('## Loading kernel from FIT Image')
-        self.wait_for_console_pattern('Starting kernel ...')
-        self.wait_for_console_pattern('Booting Linux on physical CPU 0xf00')
-        self.wait_for_console_pattern('mmcblk0: p1 p2 p3 p4 p5 p6 p7')
-        self.wait_for_console_pattern('IBM eBMC (OpenBMC for IBM Enterprise')
diff --git a/tests/functional/meson.build b/tests/functional/meson.build
index 5ccc1aa66ddc..97c1c597e861 100644
--- a/tests/functional/meson.build
+++ b/tests/functional/meson.build
@@ -15,6 +15,7 @@  test_timeouts = {
   'aarch64_sbsaref' : 600,
   'aarch64_virt' : 360,
   'acpi_bits' : 240,
+  'arm_aspeed' : 600,
   'arm_raspi2' : 120,
   'arm_tuxrun' : 120,
   'mips_malta' : 120,
@@ -51,6 +52,7 @@  tests_alpha_system_thorough = [
 ]
 
 tests_arm_system_thorough = [
+  'arm_aspeed',
   'arm_canona1100',
   'arm_integratorcp',
   'arm_raspi2',
diff --git a/tests/functional/test_arm_aspeed.py b/tests/functional/test_arm_aspeed.py
new file mode 100644
index 000000000000..9761fc06a454
--- /dev/null
+++ b/tests/functional/test_arm_aspeed.py
@@ -0,0 +1,282 @@ 
+#!/usr/bin/env python3
+#
+# Functional test that boots the ASPEED SoCs with firmware
+#
+# Copyright (C) 2022 ASPEED Technology Inc
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+import time
+import subprocess
+import tempfile
+
+from qemu_test import LinuxKernelTest, Asset
+from qemu_test import exec_command_and_wait_for_pattern
+from qemu_test import interrupt_interactive_console_until_pattern
+from qemu_test import exec_command
+from qemu_test import has_cmd
+from qemu_test.utils import archive_extract
+from zipfile import ZipFile
+from unittest import skipUnless
+
+class AST1030Machine(LinuxKernelTest):
+
+    ASSET_ZEPHYR_1_04 = Asset(
+        ('https://github.com/AspeedTech-BMC'
+         '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip'),
+        '4ac6210adcbc61294927918707c6762483fd844dde5e07f3ba834ad1f91434d3')
+
+    def test_ast1030_zephyros_1_04(self):
+        self.set_machine('ast1030-evb')
+
+        zip_file = self.ASSET_ZEPHYR_1_04.fetch()
+
+        kernel_name = "ast1030-evb-demo/zephyr.elf"
+        with ZipFile(zip_file, 'r') as zf:
+                     zf.extract(kernel_name, path=self.workdir)
+        kernel_file = os.path.join(self.workdir, kernel_name)
+
+        self.vm.set_console()
+        self.vm.add_args('-kernel', kernel_file, '-nographic')
+        self.vm.launch()
+        self.wait_for_console_pattern("Booting Zephyr OS")
+        exec_command_and_wait_for_pattern(self, "help",
+                                          "Available commands")
+
+    ASSET_ZEPHYR_1_07 = Asset(
+        ('https://github.com/AspeedTech-BMC'
+         '/zephyr/releases/download/v00.01.07/ast1030-evb-demo.zip'),
+        'ad52e27959746988afaed8429bf4e12ab988c05c4d07c9d90e13ec6f7be4574c')
+
+    def test_ast1030_zephyros_1_07(self):
+        self.set_machine('ast1030-evb')
+
+        zip_file = self.ASSET_ZEPHYR_1_07.fetch()
+
+        kernel_name = "ast1030-evb-demo/zephyr.bin"
+        with ZipFile(zip_file, 'r') as zf:
+                     zf.extract(kernel_name, path=self.workdir)
+        kernel_file = os.path.join(self.workdir, kernel_name)
+
+        self.vm.set_console()
+        self.vm.add_args('-kernel', kernel_file, '-nographic')
+        self.vm.launch()
+        self.wait_for_console_pattern("Booting Zephyr OS")
+        for shell_cmd in [
+                'kernel stacks',
+                'otp info conf',
+                'otp info scu',
+                'hwinfo devid',
+                'crypto aes256_cbc_vault',
+                'random get',
+                'jtag JTAG1 sw_xfer high TMS',
+                'adc ADC0 resolution 12',
+                'adc ADC0 read 42',
+                'adc ADC1 read 69',
+                'i2c scan I2C_0',
+                'i3c attach I3C_0',
+                'hash test',
+                'kernel uptime',
+                'kernel reboot warm',
+                'kernel uptime',
+                'kernel reboot cold',
+                'kernel uptime',
+        ]: exec_command_and_wait_for_pattern(self, shell_cmd, "uart:~$")
+
+class AST2x00Machine(LinuxKernelTest):
+
+    def do_test_arm_aspeed(self, machine, image):
+        self.set_machine(machine)
+        self.vm.set_console()
+        self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw',
+                         '-net', 'nic', '-snapshot')
+        self.vm.launch()
+
+        self.wait_for_console_pattern("U-Boot 2016.07")
+        self.wait_for_console_pattern("## Loading kernel from FIT Image at 20080000")
+        self.wait_for_console_pattern("Starting kernel ...")
+        self.wait_for_console_pattern("Booting Linux on physical CPU 0x0")
+        self.wait_for_console_pattern(
+                "aspeed-smc 1e620000.spi: read control register: 203b0641")
+        self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ")
+        self.wait_for_console_pattern("systemd[1]: Set hostname to")
+
+    ASSET_PALMETTO_FLASH = Asset(
+        ('https://github.com/openbmc/openbmc/releases/download/2.9.0/'
+         'obmc-phosphor-image-palmetto.static.mtd'),
+        '3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d');
+
+    def test_arm_ast2400_palmetto_openbmc_v2_9_0(self):
+        image_path = self.ASSET_PALMETTO_FLASH.fetch()
+
+        self.do_test_arm_aspeed('palmetto-bmc', image_path)
+
+    ASSET_ROMULUS_FLASH = Asset(
+        ('https://github.com/openbmc/openbmc/releases/download/2.9.0/'
+         'obmc-phosphor-image-romulus.static.mtd'),
+        '820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25')
+
+    def test_arm_ast2500_romulus_openbmc_v2_9_0(self):
+        image_path = self.ASSET_ROMULUS_FLASH.fetch()
+
+        self.do_test_arm_aspeed('romulus-bmc', image_path)
+
+    def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'):
+        self.require_netdev('user')
+        self.vm.set_console()
+        self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw',
+                         '-net', 'nic', '-net', 'user')
+        self.vm.launch()
+
+        self.wait_for_console_pattern('U-Boot 2019.04')
+        self.wait_for_console_pattern('## Loading kernel from FIT Image')
+        self.wait_for_console_pattern('Starting kernel ...')
+        self.wait_for_console_pattern('Booting Linux on physical CPU ' + cpu_id)
+        self.wait_for_console_pattern('lease of 10.0.2.15')
+        # the line before login:
+        self.wait_for_console_pattern(pattern)
+        time.sleep(0.1)
+        exec_command(self, 'root')
+        time.sleep(0.1)
+        exec_command(self, "passw0rd")
+
+    def do_test_arm_aspeed_buildroot_poweroff(self):
+        exec_command_and_wait_for_pattern(self, 'poweroff',
+                                          'reboot: System halted');
+
+    ASSET_BR2_202311_AST2500_FLASH = Asset(
+        ('https://github.com/legoater/qemu-aspeed-boot/raw/master/'
+         'images/ast2500-evb/buildroot-2023.11/flash.img'),
+        'c23db6160cf77d0258397eb2051162c8473a56c441417c52a91ba217186e715f')
+
+    def test_arm_ast2500_evb_buildroot(self):
+        self.set_machine('ast2500-evb')
+
+        image_path = self.ASSET_BR2_202311_AST2500_FLASH.fetch()
+
+        self.vm.add_args('-device',
+                         'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test');
+        self.do_test_arm_aspeed_buildroot_start(image_path, '0x0',
+                                                'Aspeed AST2500 EVB')
+
+        exec_command_and_wait_for_pattern(self,
+             'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device',
+             'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d');
+        exec_command_and_wait_for_pattern(self,
+                             'cat /sys/class/hwmon/hwmon1/temp1_input', '0')
+        self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test',
+                    property='temperature', value=18000);
+        exec_command_and_wait_for_pattern(self,
+                             'cat /sys/class/hwmon/hwmon1/temp1_input', '18000')
+
+        self.do_test_arm_aspeed_buildroot_poweroff()
+
+    ASSET_BR2_202311_AST2600_FLASH = Asset(
+        ('https://github.com/legoater/qemu-aspeed-boot/raw/master/'
+         'images/ast2600-evb/buildroot-2023.11/flash.img'),
+        'b62808daef48b438d0728ee07662290490ecfa65987bb91294cafb1bb7ad1a68')
+
+    def test_arm_ast2600_evb_buildroot(self):
+        self.set_machine('ast2600-evb')
+
+        image_path = self.ASSET_BR2_202311_AST2600_FLASH.fetch()
+
+        self.vm.add_args('-device',
+                         'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test');
+        self.vm.add_args('-device',
+                         'ds1338,bus=aspeed.i2c.bus.3,address=0x32');
+        self.vm.add_args('-device',
+                         'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42');
+        self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB')
+
+        exec_command_and_wait_for_pattern(self,
+             'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device',
+             'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d');
+        exec_command_and_wait_for_pattern(self,
+                             'cat /sys/class/hwmon/hwmon1/temp1_input', '0')
+        self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test',
+                    property='temperature', value=18000);
+        exec_command_and_wait_for_pattern(self,
+                             'cat /sys/class/hwmon/hwmon1/temp1_input', '18000')
+
+        exec_command_and_wait_for_pattern(self,
+             'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-3/device/new_device',
+             'i2c i2c-3: new_device: Instantiated device ds1307 at 0x32');
+        year = time.strftime("%Y")
+        exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year);
+
+        exec_command_and_wait_for_pattern(self,
+             'echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-3/new_device',
+             'i2c i2c-3: new_device: Instantiated device slave-24c02 at 0x64');
+        exec_command(self, 'i2cset -y 3 0x42 0x64 0x00 0xaa i');
+        time.sleep(0.1)
+        exec_command_and_wait_for_pattern(self,
+             'hexdump /sys/bus/i2c/devices/3-1064/slave-eeprom',
+             '0000000 ffaa ffff ffff ffff ffff ffff ffff ffff');
+        self.do_test_arm_aspeed_buildroot_poweroff()
+
+    ASSET_BR2_202302_AST2600_TPM_FLASH = Asset(
+        ('https://github.com/legoater/qemu-aspeed-boot/raw/master/'
+         'images/ast2600-evb/buildroot-2023.02-tpm/flash.img'),
+        'a46009ae8a5403a0826d607215e731a8c68d27c14c41e55331706b8f9c7bd997')
+
+    @skipUnless(*has_cmd('swtpm'))
+    def test_arm_ast2600_evb_buildroot_tpm(self):
+        self.set_machine('ast2600-evb')
+
+        image_path = self.ASSET_BR2_202302_AST2600_TPM_FLASH.fetch()
+
+        socket_dir = tempfile.TemporaryDirectory(prefix="qemu_")
+        socket = os.path.join(socket_dir.name, 'swtpm-socket')
+
+        subprocess.run(['swtpm', 'socket', '-d', '--tpm2',
+                        '--tpmstate', f'dir={self.vm.temp_dir}',
+                        '--ctrl', f'type=unixio,path={socket}'])
+
+        self.vm.add_args('-chardev', f'socket,id=chrtpm,path={socket}')
+        self.vm.add_args('-tpmdev', 'emulator,id=tpm0,chardev=chrtpm')
+        self.vm.add_args('-device',
+                         'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e')
+        self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB')
+
+        exec_command_and_wait_for_pattern(self,
+            'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device',
+            'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)');
+        exec_command_and_wait_for_pattern(self,
+            'cat /sys/class/tpm/tpm0/pcr-sha256/0',
+            'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0');
+
+        self.do_test_arm_aspeed_buildroot_poweroff()
+
+class AST2x00MachineMMC(LinuxKernelTest):
+
+    ASSET_RAINIER_EMMC = Asset(
+        ('https://fileserver.linaro.org/s/B6pJTwWEkzSDi36/download/'
+         'mmc-p10bmc-20240617.qcow2'),
+        'd523fb478d2b84d5adc5658d08502bc64b1486955683814f89c6137518acd90b')
+
+    def test_arm_aspeed_emmc_boot(self):
+        self.set_machine('rainier-bmc')
+        self.require_netdev('user')
+
+        image_path = self.ASSET_RAINIER_EMMC.fetch()
+
+        self.vm.set_console()
+        self.vm.add_args('-drive',
+                         'file=' + image_path + ',if=sd,id=sd2,index=2',
+                         '-net', 'nic', '-net', 'user', '-snapshot')
+        self.vm.launch()
+
+        self.wait_for_console_pattern('U-Boot SPL 2019.04')
+        self.wait_for_console_pattern('Trying to boot from MMC1')
+        self.wait_for_console_pattern('U-Boot 2019.04')
+        self.wait_for_console_pattern('eMMC 2nd Boot')
+        self.wait_for_console_pattern('## Loading kernel from FIT Image')
+        self.wait_for_console_pattern('Starting kernel ...')
+        self.wait_for_console_pattern('Booting Linux on physical CPU 0xf00')
+        self.wait_for_console_pattern('mmcblk0: p1 p2 p3 p4 p5 p6 p7')
+        self.wait_for_console_pattern('IBM eBMC (OpenBMC for IBM Enterprise')
+
+if __name__ == '__main__':
+    LinuxKernelTest.main()