new file mode 100755
@@ -0,0 +1,37 @@
+#!/usr/bin/perl -w
+# This is part of "osstest", an automated testing framework for Xen.
+# Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+use strict qw(vars);
+use DBI;
+use Osstest;
+use Osstest::TestSupport;
+
+tsreadconfig();
+
+our ($whhost) = @ARGV;
+$whhost ||= 'host';
+
+our $ho= selecthost($whhost);
+
+sub extract () {
+ my %distpath;
+ target_extract_jobdistpath($ho, "xen", "path_xenlptdist",
+ $r{"$ho->{Ident}_xenbuildjob"} // $r{"xenbuildjob"},
+ \%distpath);
+}
+
+extract();
new file mode 100755
@@ -0,0 +1,171 @@
+#!/usr/bin/perl -w
+# This is part of "osstest", an automated testing framework for Xen.
+# Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+use strict qw(vars);
+use Osstest;
+use POSIX;
+use Osstest::TestSupport;
+
+tsreadconfig();
+
+my @livepatch_files = qw(xen_hello_world.livepatch
+ xen_replace_world.livepatch
+ xen_bye_world.livepatch
+ xen_nop.livepatch);
+
+my $xen_extra_info;
+my $xen_minor_ver;
+
+sub populate_data {
+ my @lines = split('\n', $_);
+ foreach my $line (@lines) {
+ my ($key, $values) = split /:/, $line;
+ $values = join("", $values);
+ chomp($values);
+ if ($key =~ m/xen_extra/) {
+ $xen_extra_info = $values;
+ }
+ if ($key =~ m/xen_minor/) {
+ $xen_minor_ver = $values;
+ }
+ }
+ return 1 if $xen_extra_info && $xen_minor_ver;
+ return 0;
+}
+
+sub check_for_hello_world {
+ return m/xen_extra/ && m/Hello World/;
+}
+
+sub check_for_stock {
+ return m/xen_extra/ && m/$xen_extra_info/;
+}
+
+# Make sure the xen_major or xen_minor are not the same as
+# $xen_major_ver or $xen_minor_ver
+sub check_versions {
+ my @lines = split('\n', $_);
+ foreach my $line (@lines) {
+ my ($key, $values) = split /:/, $line;
+ $values = join("", $values);
+ chomp($values);
+ if ($key =~ m/xen_minor/) {
+ if ($values ne $xen_minor_ver ) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+my @livepatch_tests = (
+ # Whether we can actually execute it.
+ { C => "xen-livepatch list" },
+ # And we better have a clean slate..
+ { C => "xen-livepatch list", OutputCheck => sub { return !m/xen_/; } },
+ # Sets the default values
+ { C => "xl info", OutputCheck => \&populate_data },
+ # Sanity check that populate_data did its job.
+ { C => "xl info",
+ OutputCheck => \&check_for_stock },
+ # Let it rip!
+ { C => "xen-livepatch revert xen_hello_world", rc => 256 },
+ { C => "xen-livepatch load xen_hello_world.livepatch" },
+ { C => "xen-livepatch load xen_hello_world.livepatch", rc => 256 },
+ { C => "xen-livepatch list",
+ OutputCheck => sub { m/xen_hello_world/ } },
+ { C => "xl info",
+ OutputCheck => \&check_for_hello_world },
+ { C => "xen-livepatch revert xen_hello_world" },
+ { C => "xl info",
+ OutputCheck => \&check_for_stock },
+ { C => "xen-livepatch unload xen_hello_world" },
+ { C => "xen-livepatch unload xen_hello_world", rc => 256 },
+ { C => "xl info",
+ OutputCheck => \&check_for_stock },
+ { C => "xen-livepatch load xen_hello_world.livepatch" },
+ { C => "xl info",
+ OutputCheck => \&check_for_hello_world },
+ { C => "xen-livepatch load xen_bye_world.livepatch" },
+ { C => "xl info",
+ OutputCheck => sub { m/xen_extra/ && m/Bye World/ } },
+ { C => "xen-livepatch upload xen_replace xen_replace_world.livepatch" },
+ { C => "xen-livepatch replace xen_replace" },
+ { C => "xl info",
+ OutputCheck => sub { m/xen_extra/ && m/Hello Again Wo/ } },
+ { C => "xen-livepatch apply xen_hello_world", rc => 256 },
+ { C => "xen-livepatch apply xen_bye_world", rc => 256 },
+ { C => "xen-livepatch apply xen_replace" },
+ { C => "xen-livepatch revert xen_replace" },
+ { C => "xen-livepatch unload xen_replace" },
+ { C => "xen-livepatch unload xen_hello_world" },
+ { C => "xen-livepatch unload xen_bye_world" },
+ { C => "xen-livepatch list",
+ OutputCheck => sub { !m/xen_/ } },
+ { C => "xl info",
+ OutputCheck => \&check_for_stock },
+ { C => "xen-livepatch load xen_nop.livepatch" },
+ { C => "xen-livepatch revert xen_nop" },
+ { C => "xen-livepatch apply xen_nop" },
+ { C => "xl info",
+ OutputCheck => \&check_versions },
+ { C => "xen-livepatch unload xen_nop", rc => 256 },
+ { C => "xen-livepatch revert xen_nop" },
+ { C => "xen-livepatch unload xen_nop" },
+ );
+
+our $ho = selecthost('host');
+
+sub livepatch_test () {
+ logm "Have ".(scalar @livepatch_tests)." test-cases\n";
+ my $rc;
+ my $output;
+
+ foreach my $test (@livepatch_tests) {
+ # Default rc is zero.
+ my $expected_rc = defined($test->{rc}) ? $test->{rc} : 0;
+ my $cmd = "(set -e;cd /usr/lib/debug/xen-livepatch;$test->{C})";
+ my ($rc, $output) = target_cmd_output_root_status($ho, $cmd);
+
+ if ($rc != $expected_rc) {
+ logm "FAILED (got $rc, expected: $expected_rc): \n";
+ die $rc;
+ }
+ if (defined($test->{OutputCheck})) {
+ $_ = $output;
+ $rc=$test->{OutputCheck}->();
+ if ($rc ne 1) {
+ die "FAILED! OutputCheck=$test->{OutputCheck}, input=$output\n";
+ }
+ }
+ }
+ return 0;
+}
+
+sub livepatch_check () {
+ foreach my $file (@livepatch_files) {
+ if (!target_file_exists($ho, "/usr/lib/debug/xen-livepatch/$$file")) {
+ die "$file is missing!\n";
+ }
+ }
+}
+
+
+livepatch_check();
+my $livepatch_result = livepatch_test();
+logm("Livepatch test returned $livepatch_result");
+exit $livepatch_result;