diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..8abc7283f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf + +[*.py] +indent_style = space +indent_size = 4 + +[*.{c|h}] +indent_style = tab +indent_size = 8 + diff --git a/.github/ISSUE_TEMPLATE/bug-report-wd19.md b/.github/ISSUE_TEMPLATE/bug-report-wd19.md new file mode 100644 index 000000000..fa0045c2e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report-wd19.md @@ -0,0 +1,64 @@ +--- +name: Bug report (Dell WD19) +about: Create a report to help us improve +title: 'Dell WD19 upgrade issue' +labels: bug +assignees: 'superm1' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + + +**Steps to Reproduce** +Steps to reproduce the behavior. + + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**fwupd version information** +Please provide the version of the daemon and client. +```shell +$ fwupdmgr --version +``` + +Please note how you installed it (`apt`, `dnf`, `pacman`, source, etc): + +**fwupd device information** +Please provide the output of the external fwupd devices recognized in your system. + +```shell +$ fwupdmgr get-devices --filter=~internal +``` + +**Dock SKU** +Please mention which module is installed in your WD19. + +- [ ] WD19 (Single-C) +- [ ] WD19TB (Thunderbolt) +- [ ] WD19DC (Dual-C) + +**Peripherals connected to the dock** +Please describe all devices connected to the dock. Be as specific as possible, +including USB devices, hubs, monitors, and downstream type-C devices. + +**Verbose daemon logs** +First enable daemon verbose logs collection. +```shell +fwupdmgr modify-config "VerboseDomains" "*" +``` + +Then try to reproduce the issue. Even if it doesn't reproduce, please attach the +daemon verbose logs collected from the system journal. +```shell +journalctl -b -u fwupd.service +``` + +**Additional questions** +- Operating system and version: +- Have you tried unplugging the dock or any peripherals from your machine? +- Have you tried to power cycle the dock from the AC adapter? +- Is this a regression? + diff --git a/.gitignore b/.gitignore index d5a589422..18fdd8a78 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,12 @@ /*.xz /*.gz __pycache__ +plugins/acpi-dmar/tests/ +plugins/acpi-facp/tests/ +plugins/ata/tests/ +plugins/dfu/tests/ +plugins/nvme/tests/ +plugins/synaptics-mst/tests/ +plugins/synaptics-prometheus/tests/ +plugins/tpm-eventlog/tests/ +plugins/uefi-dbx/tests/ diff --git a/README.md b/README.md index d9f514146..b5548e714 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Enterprise use The flow of updates can be controlled in the enterprise using the "approved updates" feature. This allows the domain administrator to filter the possible updates from a central server (e.g. the LVFS, or a mirror) -to only firmware that have been tested specifically in your organisation. +to only firmware that have been tested specifically in your organization. The list of approved updates can be enabled by adding `ApprovalRequired=true` to the remote configuration file, e.g. `lvfs.conf`. Once enabled, the @@ -103,7 +103,7 @@ Other frontends After the firmware has been downloaded a popup will be displayed in GNOME Software to perform the update. -2. [KDE Discover](https://userbase.kde.org/Discover) is the software centre, +2. [KDE Discover](https://userbase.kde.org/Discover) is the software center, generally bundled with KDE Plasma. With the release of [KDE Plasma 5.14](https://www.kde.org/announcements/plasma-5.14.0.php), a new fwupd backend has been implemented in KDE Discover for firmware updates. @@ -125,3 +125,4 @@ There are several automated fuzzing tests in fwupd. These take some time to run: ninja fuzz-synaptics-rmi ninja fuzz-firmware ninja fuzz-smbios + ninja fuzz-efidbx diff --git a/RELEASE b/RELEASE index a07f63d47..c59276643 100644 --- a/RELEASE +++ b/RELEASE @@ -2,14 +2,22 @@ fwupd Release Notes Write release entries: -git log --format="%s" --cherry-pick --right-only 1.4.5... | grep -i -v trivial | grep -v Merge | sort | uniq +git log --format="%s" --cherry-pick --right-only 1.5.0... | grep -i -v trivial | grep -v Merge | sort | uniq Add any user visible changes into ../data/org.freedesktop.fwupd.metainfo.xml appstream-util appdata-to-news ../data/org.freedesktop.fwupd.metainfo.xml > NEWS +Update translations: + +ninja-build fwupd-pot +tx push --source +tx pull --all --force --minimum-perc=5 +ninja-build fix-translations +git add ../po/*.po + 2. Commit changes to git: # MAKE SURE THIS IS CORRECT -export release_ver="1.4.6" +export release_ver="1.5.1" git commit -a -m "Release fwupd ${release_ver}" git tag -s -f -m "Release fwupd ${release_ver}" "${release_ver}" diff --git a/SECURITY.md b/SECURITY.md index 850248c95..a09bbd58a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -31,5 +31,5 @@ this repository. Failing that, please report the issue against the `fwupd` component in Red Hat bugzilla, with the security checkbox set. You should get a response within 3 -days. We have no bug bountry program, but we're happy to credit you in updates +days. We have no bug bounty program, but we're happy to credit you in updates if this is what you would like us to do. diff --git a/contrib/ci/Dockerfile-arch.in b/contrib/ci/Dockerfile-arch.in index 57194602c..e47aaf467 100644 --- a/contrib/ci/Dockerfile-arch.in +++ b/contrib/ci/Dockerfile-arch.in @@ -2,6 +2,7 @@ FROM archlinux/base %%%OS%%% ENV LANG en_US.UTF-8 ENV LC_ALL en_US.UTF-8 +ENV CI_NETWORK true RUN echo fubar > /etc/machine-id RUN rm /usr/share/libalpm/hooks/package-cleanup.hook RUN echo fubar > /etc/machine-id diff --git a/contrib/ci/Dockerfile-debian.in b/contrib/ci/Dockerfile-debian.in index b82e11abb..2af6cfa4e 100644 --- a/contrib/ci/Dockerfile-debian.in +++ b/contrib/ci/Dockerfile-debian.in @@ -1,5 +1,6 @@ FROM %%%ARCH_PREFIX%%%debian:testing %%%OS%%% +ENV CI_NETWORK true RUN echo "deb http://ftp.us.debian.org/debian unstable main contrib non-free" >> /etc/apt/sources.list RUN echo 'Package: *\n\ Pin: release a=testing\n\ diff --git a/contrib/ci/Dockerfile-ubuntu.in b/contrib/ci/Dockerfile-ubuntu.in index 694ae2471..921ecbcaf 100644 --- a/contrib/ci/Dockerfile-ubuntu.in +++ b/contrib/ci/Dockerfile-ubuntu.in @@ -1,5 +1,6 @@ FROM ubuntu:devel %%%OS%%% +ENV CI_NETWORK true ENV CC clang RUN echo fubar > /etc/machine-id %%%ARCH_SPECIFIC_COMMAND%%% diff --git a/contrib/ci/arch.sh b/contrib/ci/arch.sh index 5a08166ff..81522ab51 100755 --- a/contrib/ci/arch.sh +++ b/contrib/ci/arch.sh @@ -3,6 +3,12 @@ set -e set -x shopt -s extglob +#clone test firmware +if [ "$CI_NETWORK" = "true" ]; then + ./contrib/ci/get_test_firmware.sh + export G_TEST_SRCDIR=`pwd`/fwupd-test-firmware/installed-tests +fi + # prepare the build tree rm -rf build mkdir build && pushd build @@ -13,8 +19,8 @@ popd chown nobody . -R # install and run TPM simulator necessary for plugins/uefi/uefi-self-test -pacman -S --noconfirm ibm-sw-tpm2 tpm2-tools -tpm_server & +pacman -S --noconfirm swtpm tpm2-tools +swtpm socket --tpm2 --server port=2321 --ctrl type=tcp,port=2322 --flags not-need-init --tpmstate "dir=$PWD" & trap "kill $!" EXIT # extend a PCR0 value for test suite sleep 2 diff --git a/contrib/ci/build_windows.sh b/contrib/ci/build_windows.sh index 239eb5cad..01cc13a4c 100755 --- a/contrib/ci/build_windows.sh +++ b/contrib/ci/build_windows.sh @@ -15,6 +15,7 @@ meson .. \ --libexecdir=$target \ --bindir=$target \ -Dbuild=standalone \ + -Dpolkit=false \ -Dplugin_coreboot=false \ -Dplugin_flashrom=false \ -Dplugin_uefi=false \ @@ -22,7 +23,7 @@ meson .. \ -Dplugin_altos=false \ -Dplugin_dell=false \ -Dplugin_nvme=false \ - -Dplugin_tpm=false \ + -Dtpm=false \ -Dsystemd=false \ -Dplugin_emmc=false \ -Dplugin_amt=false \ @@ -40,6 +41,10 @@ meson .. \ -Dlibjcat:man=false \ -Dlibjcat:gpg=false \ -Dlibjcat:introspection=false \ + -Dgusb:tests=false \ + -Dgusb:docs=false \ + -Dgusb:introspection=false \ + -Dgusb:vapi=false \ -Dgudev=false $@ VERSION=$(meson introspect . --projectinfo | jq -r .version) ninja -v diff --git a/contrib/ci/centos.sh b/contrib/ci/centos.sh index 990ada1b7..7005fb596 100755 --- a/contrib/ci/centos.sh +++ b/contrib/ci/centos.sh @@ -2,6 +2,13 @@ set -e set -x +#clone test firmware +if [ "$CI_NETWORK" = "true" ]; then + ./contrib/ci/get_test_firmware.sh + export G_TEST_SRCDIR=`pwd`/fwupd-test-firmware/installed-tests +fi + +#build rm -rf build mkdir -p build cd build diff --git a/contrib/ci/debian.sh b/contrib/ci/debian.sh index d7c55f11d..782c49e60 100755 --- a/contrib/ci/debian.sh +++ b/contrib/ci/debian.sh @@ -2,6 +2,9 @@ set -e set -x +# remove when tpm2-tss is fixed +mkdir -p /usr/include/tss + #although it's debian, we don't build packages if [ "$OS" = "debian-s390x" ]; then ./contrib/ci/debian_s390x.sh @@ -23,6 +26,20 @@ sed s/quilt/native/ debian/source/format -i #generate control file ./contrib/ci/generate_debian.py +#check if we have all deps available +#if some are missing, we're going to use subproject instead and +#packaging CI will fail +if ! dpkg-checkbuilddeps; then + ./contrib/ci/ubuntu.sh + exit 0 +fi + +#clone test firmware +if [ "$CI_NETWORK" = "true" ]; then + ./contrib/ci/get_test_firmware.sh + export G_TEST_SRCDIR=`pwd`/fwupd-test-firmware/installed-tests +fi + #disable unit tests if fwupd is already installed (may cause problems) if [ -x /usr/lib/fwupd/fwupd ]; then export DEB_BUILD_OPTIONS=nocheck diff --git a/contrib/ci/debian_s390x.sh b/contrib/ci/debian_s390x.sh index 9b277ff5f..29797bb4c 100755 --- a/contrib/ci/debian_s390x.sh +++ b/contrib/ci/debian_s390x.sh @@ -20,6 +20,7 @@ meson .. \ -Dplugin_uefi=false \ -Dplugin_dell=false \ -Dplugin_modem_manager=false \ + -Dplugin_msr=false \ -Dplugin_redfish=false \ -Dintrospection=false \ -Dgtkdoc=false \ diff --git a/contrib/ci/dependencies.xml b/contrib/ci/dependencies.xml index 679c76ad6..8c9fc8d6a 100644 --- a/contrib/ci/dependencies.xml +++ b/contrib/ci/dependencies.xml @@ -766,7 +766,7 @@ - (>= 0.2.9) + (>= 0.3.5) libgusb-dev:s390x @@ -774,7 +774,7 @@ - (>= 0.2.9) + (>= 0.3.3) diff --git a/contrib/ci/fedora.sh b/contrib/ci/fedora.sh index 759424a63..dcc6590b0 100755 --- a/contrib/ci/fedora.sh +++ b/contrib/ci/fedora.sh @@ -54,7 +54,7 @@ mkdir -p dist cp $HOME/rpmbuild/RPMS/*/*.rpm dist if [ "$CI" = "true" ]; then - sed "s,^BlacklistPlugins=test;invalid,BlacklistPlugins=," -i /etc/fwupd/daemon.conf + sed "s,^DisabledPlugins=test;invalid,DisabledPlugins=," -i /etc/fwupd/daemon.conf # set up enough PolicyKit and D-Bus to run the daemon mkdir -p /run/dbus diff --git a/contrib/ci/get_test_firmware.sh b/contrib/ci/get_test_firmware.sh new file mode 100755 index 000000000..486fc4f70 --- /dev/null +++ b/contrib/ci/get_test_firmware.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +#clone fwupd-test-firmware +rm -rf fwupd-test-firmware +git clone https://github.com/fwupd/fwupd-test-firmware +#set up build tests to work +cp fwupd-test-firmware/ci-tests/* . -R +export G_TEST_SRCDIR=`pwd`/fwupd-test-firmware/installed-tests diff --git a/contrib/ci/trust.sh b/contrib/ci/trust.sh index 4b8d27e9a..7223d186a 100755 --- a/contrib/ci/trust.sh +++ b/contrib/ci/trust.sh @@ -2,14 +2,21 @@ set -e set -x +#clone test firmware +if [ "$CI_NETWORK" = "true" ]; then + ./contrib/ci/get_test_firmware.sh + export G_TEST_SRCDIR=`pwd`/fwupd-test-firmware/installed-tests +fi + # Builds using GPG and PKCS7 turned off to make # sure no assumptions of a trust backend rm -rf build meson build \ -Dman=false \ - -Ddaemon=false \ + -Ddaemon=true \ + -Dpolkit=false \ -Dgusb:tests=false \ - -Dplugin_tpm=false \ + -Dtpm=false \ -Dplugin_modem_manager=false \ -Dplugin_flashrom=false \ -Dplugin_uefi=false \ diff --git a/contrib/ci/ubuntu.sh b/contrib/ci/ubuntu.sh index 91c265dd5..38ac943b5 100755 --- a/contrib/ci/ubuntu.sh +++ b/contrib/ci/ubuntu.sh @@ -2,7 +2,14 @@ set -e set -x +#clone test firmware +if [ "$CI_NETWORK" = "true" ]; then + ./contrib/ci/get_test_firmware.sh + export G_TEST_SRCDIR=`pwd`/fwupd-test-firmware/installed-tests +fi + #evaluate using Ubuntu's buildflags +#evaluate using Debian/Ubuntu's buildflags eval "$(dpkg-buildflags --export=sh)" #filter out -Bsymbolic-functions export LDFLAGS=$(dpkg-buildflags --get LDFLAGS | sed "s/-Wl,-Bsymbolic-functions\s//") diff --git a/contrib/firmware_packager/firmware_packager.py b/contrib/firmware_packager/firmware_packager.py index 45b268ebe..4deaafc5d 100755 --- a/contrib/firmware_packager/firmware_packager.py +++ b/contrib/firmware_packager/firmware_packager.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -# Copyright (C) 2017 Max Ehrlich max.ehr@gmail.com +# Copyright (C) 2017 Max Ehrlich maxehr@gmail.com # # SPDX-License-Identifier: LGPL-2.1+ # @@ -107,7 +107,7 @@ def main(args): print('Creating metainfo') make_firmware_metainfo(args, dir) - print('Cabbing firmware files') + print('Creating cabinet file') create_firmware_cab(args, dir) print('Done') diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index c42a346d7..139b98db0 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -1,6 +1,6 @@ %global glib2_version 2.45.8 %global libxmlb_version 0.1.3 -%global libgusb_version 0.2.11 +%global libgusb_version 0.3.5 %global libsoup_version 2.51.92 %global libjcat_version 0.1.0 %global systemd_version 231 @@ -22,6 +22,15 @@ %global have_uefi 1 %endif +# flashrom is only available on these arches +%ifarch i686 x86_64 armv7hl aarch64 ppc64le +%global have_flashrom 1 +%endif + +%ifarch i686 x86_64 +%global have_msr 1 +%endif + # redfish is only available on this arch %ifarch x86_64 %global have_redfish 1 @@ -73,7 +82,9 @@ BuildRequires: json-glib-devel >= %{json_glib_version} BuildRequires: vala BuildRequires: bash-completion BuildRequires: git-core +%if 0%{?have_flashrom} BuildRequires: flashrom-devel >= 1.2-2 +%endif %if 0%{?have_modem_manager} BuildRequires: ModemManager-glib-devel >= 1.10.0 @@ -133,6 +144,14 @@ Provides: fwupdate Provides: fwupdate-efi %endif +# optional, but a really good idea +%if 0%{?have_modem_manager} +Recommends: %{name}-plugin-modem-manager +%endif +%if 0%{?have_flashrom} +Recommends: %{name}-plugin-flashrom +%endif + %description fwupd is a daemon to allow session software to update device firmware. @@ -147,11 +166,28 @@ Files for development with %{name}. %package tests Summary: Data files for installed tests -BuildArch: noarch %description tests Data files for installed tests. +%if 0%{?have_modem_manager} +%package plugin-modem-manager +Summary: fwupd plugin using ModemManger + +%description plugin-modem-manager +This provides the optional package which is only required on hardware that +might have mobile broadband hardware. It is probably not required on servers. +%endif + +%if 0%{?have_flashrom} +%package plugin-flashrom +Summary: fwupd plugin using flashrom + +%description plugin-flashrom +This provides the optional package which is only required on hardware that +can be flashed using flashrom. It is probably not required on servers. +%endif + %prep %autosetup -p1 @@ -172,7 +208,16 @@ Data files for installed tests. %else -Dplugin_dummy=false \ %endif +%if 0%{?have_flashrom} -Dplugin_flashrom=true \ +%else + -Dplugin_flashrom=false \ +%endif +%if 0%{?have_msr} + -Dplugin_msr=true \ +%else + -Dplugin_msr=false \ +%endif -Dplugin_thunderbolt=true \ %if 0%{?have_redfish} -Dplugin_redfish=true \ @@ -182,11 +227,11 @@ Data files for installed tests. %if 0%{?have_uefi} -Dplugin_uefi=true \ -Dplugin_nvme=true \ - -Dplugin_tpm=true \ + -Dtpm=true \ %else -Dplugin_uefi=false \ -Dplugin_nvme=false \ - -Dplugin_tpm=false \ + -Dtpm=false \ %endif %if 0%{?have_dell} -Dplugin_dell=true \ @@ -205,6 +250,9 @@ Data files for installed tests. %meson_build %if 0%{?enable_tests} +%if 0%{?enable_ci} + ./contrib/ci/get_test_firmware.sh +%endif %check %meson_test %endif @@ -256,6 +304,9 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %config(noreplace)%{_sysconfdir}/fwupd/thunderbolt.conf %dir %{_libexecdir}/fwupd %{_libexecdir}/fwupd/fwupd +%ifarch i686 x86_64 +%{_libexecdir}/fwupd/fwupd-detect-cet +%endif %{_libexecdir}/fwupd/fwupdoffline %if 0%{?have_uefi} %{_libexecdir}/fwupd/efi/*.efi @@ -281,6 +332,10 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %config(noreplace)%{_sysconfdir}/fwupd/remotes.d/vendor-directory.conf %config(noreplace)%{_sysconfdir}/pki/fwupd %{_sysconfdir}/pki/fwupd-metadata +%if 0%{?have_msr} +%{_sysconfdir}/modules-load.d/fwupd-msr.conf +%endif +%{_sysconfdir}/modules-load.d/fwupd-platform-integrity.conf %{_datadir}/dbus-1/system.d/org.freedesktop.fwupd.conf %{_datadir}/bash-completion/completions/fwupdmgr %{_datadir}/bash-completion/completions/fwupdtool @@ -328,12 +383,17 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg /usr/lib/udev/rules.d/*.rules /usr/lib/systemd/system-shutdown/fwupd.shutdown %dir %{_libdir}/fwupd-plugins-3 +%{_libdir}/fwupd-plugins-3/libfu_plugin_acpi_dmar.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_acpi_facp.so %{_libdir}/fwupd-plugins-3/libfu_plugin_altos.so %{_libdir}/fwupd-plugins-3/libfu_plugin_amt.so %{_libdir}/fwupd-plugins-3/libfu_plugin_ata.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_bcm57xx.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_bios.so %{_libdir}/fwupd-plugins-3/libfu_plugin_ccgx.so %{_libdir}/fwupd-plugins-3/libfu_plugin_colorhug.so %{_libdir}/fwupd-plugins-3/libfu_plugin_coreboot.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_cros_ec.so %{_libdir}/fwupd-plugins-3/libfu_plugin_csr.so %{_libdir}/fwupd-plugins-3/libfu_plugin_cpu.so %if 0%{?have_dell} @@ -343,20 +403,28 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %{_libdir}/fwupd-plugins-3/libfu_plugin_dell_dock.so %{_libdir}/fwupd-plugins-3/libfu_plugin_dfu.so %{_libdir}/fwupd-plugins-3/libfu_plugin_ebitdo.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_elantp.so %{_libdir}/fwupd-plugins-3/libfu_plugin_emmc.so %{_libdir}/fwupd-plugins-3/libfu_plugin_ep963x.so %{_libdir}/fwupd-plugins-3/libfu_plugin_fastboot.so -%{_libdir}/fwupd-plugins-3/libfu_plugin_flashrom.so %{_libdir}/fwupd-plugins-3/libfu_plugin_fresco_pd.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_iommu.so %{_libdir}/fwupd-plugins-3/libfu_plugin_jabra.so -%if 0%{?have_modem_manager} -%{_libdir}/fwupd-plugins-3/libfu_plugin_modem_manager.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_linux_lockdown.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_linux_sleep.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_linux_swap.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_linux_tainted.so +%if 0%{?have_msr} +%{_libdir}/fwupd-plugins-3/libfu_plugin_msr.so %endif %{_libdir}/fwupd-plugins-3/libfu_plugin_nitrokey.so %if 0%{?have_uefi} %{_libdir}/fwupd-plugins-3/libfu_plugin_nvme.so %endif %{_libdir}/fwupd-plugins-3/libfu_plugin_optionrom.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_pci_bcr.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_pci_mei.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_platform_integrity.so %if 0%{?have_redfish} %{_libdir}/fwupd-plugins-3/libfu_plugin_redfish.so %endif @@ -390,11 +458,21 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %{_libdir}/fwupd-plugins-3/libfu_plugin_vli.so %{_libdir}/fwupd-plugins-3/libfu_plugin_wacom_raw.so %{_libdir}/fwupd-plugins-3/libfu_plugin_wacom_usb.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_goodixmoc.so %ghost %{_localstatedir}/lib/fwupd/gnupg %if 0%{?have_uefi} %{_datadir}/locale/*/LC_IMAGES/fwupd* %endif +%if 0%{?have_modem_manager} +%files plugin-modem-manager +%{_libdir}/fwupd-plugins-3/libfu_plugin_modem_manager.so +%endif +%if 0%{?have_flashrom} +%files plugin-flashrom +%{_libdir}/fwupd-plugins-3/libfu_plugin_flashrom.so +%endif + %files devel %{_datadir}/gir-1.0/Fwupd-2.0.gir %{_datadir}/gir-1.0/FwupdPlugin-1.0.gir @@ -405,14 +483,17 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %{_libdir}/pkgconfig/fwupd.pc %{_libdir}/pkgconfig/fwupdplugin.pc +%if 0%{?enable_tests} %files tests %dir %{_datadir}/installed-tests/fwupd %{_datadir}/installed-tests/fwupd/fwupd-tests.xml %{_datadir}/installed-tests/fwupd/*.test %{_datadir}/installed-tests/fwupd/*.cab %{_datadir}/installed-tests/fwupd/*.sh +%{_libexecdir}/installed-tests/fwupd/* %dir %{_sysconfdir}/fwupd/remotes.d %config(noreplace)%{_sysconfdir}/fwupd/remotes.d/fwupd-tests.conf +%endif %changelog * #LONGDATE# Richard Hughes #VERSION#-0.#BUILD##ALPHATAG# diff --git a/data/90-fwupd-devices.rules b/data/90-fwupd-devices.rules index 12b5fe991..2bc9efc49 100644 --- a/data/90-fwupd-devices.rules +++ b/data/90-fwupd-devices.rules @@ -4,23 +4,5 @@ # SPDX-License-Identifier: LGPL-2.1+ # -# VIA USB 3.0 VL811 Hub -SUBSYSTEM=="usb", DRIVER=="hub", ATTRS{idVendor}=="2109", ATTRS{idProduct}=="0810", ENV{FWUPD_GUID}="adbb9034-b577-42c2-a661-1ee4f49ef64c", ENV{FWUPD_VENDOR}="VIA", ENV{FWUPD_MODEL}="USB 3.0 VL811 Hub" - -# VIA USB 3.0 VL811+ Hub -SUBSYSTEM=="usb", DRIVER=="hub", ATTRS{idVendor}=="2109", ATTRS{idProduct}=="0811", ENV{FWUPD_GUID}="54f84d05-c917-4c50-8b35-44feabaaa323", ENV{FWUPD_VENDOR}="VIA", ENV{FWUPD_MODEL}="USB 3.0 VL811+ Hub" - -# VIA USB 3.0 VL812 Hub -SUBSYSTEM=="usb", DRIVER=="hub", ATTRS{idVendor}=="2109", ATTRS{idProduct}=="0812", ENV{FWUPD_GUID}="cd0314ec-b80f-4d1a-a24f-c409183a8b2d", ENV{FWUPD_VENDOR}="VIA", ENV{FWUPD_MODEL}="USB 3.0 VL812 Hub" - -# VIA USB 3.0 VL812 B2 Hub -SUBSYSTEM=="usb", DRIVER=="hub", ATTRS{idVendor}=="2109", ATTRS{idProduct}=="2812", ENV{FWUPD_GUID}="26470009-97a8-4028-867a-bbbac6ee7bf0", ENV{FWUPD_VENDOR}="VIA", ENV{FWUPD_MODEL}="USB 3.0 VL812 B2 Hub" - -ENV{FWUPD_GUID}=="*?", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" -ENV{FWUPD_GUID}=="*?", ENV{ID_MODEL_FROM_DATABASE}=="", IMPORT{builtin}="hwdb --subsystem=usb" - -# PCI cards with ROM -SUBSYSTEM=="pci", TEST=="/sys$devpath/rom", ENV{FWUPD_GUID}="$attr{vendor}:$attr{device}" - # NVMe hardware SUBSYSTEM=="nvme", ENV{ID_VENDOR_FROM_DATABASE}=="", IMPORT{builtin}="hwdb --subsystem=pci" diff --git a/data/bash-completion/fwupdagent b/data/bash-completion/fwupdagent index f4c3fd582..5234625da 100644 --- a/data/bash-completion/fwupdagent +++ b/data/bash-completion/fwupdagent @@ -2,6 +2,7 @@ _fwupdagent_cmd_list=( 'get-devices' 'get-updates' 'get-upgrades' + 'security' ) _fwupdagent_opts=( diff --git a/data/bash-completion/fwupdmgr.in b/data/bash-completion/fwupdmgr.in index a372bab27..cfcc0ca5d 100644 --- a/data/bash-completion/fwupdmgr.in +++ b/data/bash-completion/fwupdmgr.in @@ -18,13 +18,16 @@ _fwupdmgr_cmd_list=( 'get-topology' 'get-updates' 'get-upgrades' + 'get-plugins' 'install' 'modify-config' 'modify-remote' 'reinstall' 'refresh' 'report-history' + 'security' 'set-approved-firmware' + 'switch-branch' 'unlock' 'unblock-firmware' 'update' @@ -39,6 +42,7 @@ _fwupdmgr_opts=( '--offline' '--allow-reinstall' '--allow-older' + '--allow-branch-switch' '--force' '--assume-yes' '--no-history' @@ -46,10 +50,11 @@ _fwupdmgr_opts=( '--no-metadata-check' '--no-reboot-check' '--no-safety-check' - '--show-all-devices' + '--show-all' '--sign' '--filter' '--disable-ssl-strict' + '--ignore-power' ) _show_filters() @@ -97,7 +102,7 @@ _fwupdmgr() esac case $command in - activate|clear-results|downgrade|get-releases|get-results|unlock|verify|verify-update) + activate|clear-results|downgrade|get-releases|get-results|unlock|verify|verify-update|get-updates|switch-branch) if [[ "$prev" = "$command" ]]; then _show_device_ids else diff --git a/data/bash-completion/fwupdtool.in b/data/bash-completion/fwupdtool.in index d00493ccb..2a153336d 100644 --- a/data/bash-completion/fwupdtool.in +++ b/data/bash-completion/fwupdtool.in @@ -4,7 +4,9 @@ _fwupdtool_cmd_list=( 'esp-list' 'esp-mount' 'esp-unmount' + 'firmware-build' 'firmware-convert' + 'firmware-extract' 'firmware-parse' 'get-updates' 'get-upgrades' @@ -23,14 +25,18 @@ _fwupdtool_cmd_list=( 'install-blob' 'monitor' 'reinstall' + 'security' + 'switch-branch' 'self-sign' 'smbios-dump' 'attach' 'detach' - 'firmware-read' + 'firmware-dump' 'refresh' 'verify-update' 'watch' + 'unbind-driver' + 'bind-driver' ) _fwupdtool_opts=( @@ -39,13 +45,16 @@ _fwupdtool_opts=( '--allow-reinstall' '--allow-older' '--force' - '--show-all-devices' - '--plugin-whitelist' + '--show-all' + '--plugins' '--prepare' '--cleanup' '--filter' '--disable-ssl-strict' '--no-safety-check' + '--ignore-checksum' + '--ignore-vid-pid' + '--ignore-power' ) _show_filters() @@ -84,7 +93,7 @@ _fwupdtool() command=${COMP_WORDS[1]} case $prev in - --plugin-whitelist) + --plugins) _show_plugins return 0 ;; @@ -95,7 +104,7 @@ _fwupdtool() esac case $command in - get-details|install|install-blob|firmware-read) + get-details|install|install-blob|firmware-dump) #find files if [[ "$prev" = "$command" ]]; then _filedir @@ -104,7 +113,7 @@ _fwupdtool() _show_modifiers fi ;; - attach|detach|activate|verify-update|reinstall) + attach|detach|activate|verify-update|reinstall|get-updates) if [[ "$prev" = "$command" ]]; then _show_device_ids #modifiers diff --git a/data/daemon.conf b/data/daemon.conf index 55926f3df..6a576cfd5 100644 --- a/data/daemon.conf +++ b/data/daemon.conf @@ -1,12 +1,12 @@ [fwupd] -# Allow blacklisting specific devices by their GUID +# Allow blocking specific devices by their GUID # Uses semicolons as delimiter -BlacklistDevices= +DisabledDevices= -# Allow blacklisting specific plugins +# Allow blocking specific plugins # Uses semicolons as delimiter -BlacklistPlugins=test;invalid +DisabledPlugins=test;invalid # Maximum archive size that can be loaded in Mb, with 0 for the default ArchiveSizeMax=0 diff --git a/data/device-tests/devices/broadcom-bcm5719.json b/data/device-tests/devices/broadcom-bcm5719.json new file mode 100644 index 000000000..052dc7e59 --- /dev/null +++ b/data/device-tests/devices/broadcom-bcm5719.json @@ -0,0 +1,12 @@ +{ + "name": "NetXtreme BCM5719", + "guids": [ + "ec5b8a9e-973b-58cc-935b-8322fabaebe9" + ], + "releases": [ + { + "version": "0.4.44", + "file": "dfbb6529d3f7051497cc7e736848b5b6e276ff93b39bc765948663606dc87c25-bcm5719-0.4.44.cab" + } + ] +} diff --git a/data/device-tests/devices/lenovo-40au0065-vli-tier1.json b/data/device-tests/devices/lenovo-40au0065-vli-tier1.json new file mode 100644 index 000000000..968b07e29 --- /dev/null +++ b/data/device-tests/devices/lenovo-40au0065-vli-tier1.json @@ -0,0 +1,16 @@ +{ + "name": "Lenovo USB-C Mini Dock [VL817]", + "guids": [ + "f281c1df-c3d5-5f8a-984d-e9548ffc95fe" + ], + "releases": [ + { + "version": "4.154", + "file": "c782946a9cf743abc37edefe35dec1b8b2d2a88b29f7058091b0a7ae3fa38e49-Lenovo-Mini_Dock_New.cab" + }, + { + "version": "4.94", + "file": "39da9917934ce162baefa3a67adfccd338bfd054933d6c40c0c887ab0d48e83f-Lenovo-Mini_Dock_Old.cab" + } + ] +} diff --git a/data/device-tests/devices/lenovo-40au0065-vli-tier2.json b/data/device-tests/devices/lenovo-40au0065-vli-tier2.json new file mode 100644 index 000000000..40fe46189 --- /dev/null +++ b/data/device-tests/devices/lenovo-40au0065-vli-tier2.json @@ -0,0 +1,16 @@ +{ + "name": "Lenovo USB-C Mini Dock [VL211]", + "guids": [ + "d636c717-44c4-5fcf-9d7f-b96f9c5f6608" + ], + "releases": [ + { + "version": "4.43", + "file": "c782946a9cf743abc37edefe35dec1b8b2d2a88b29f7058091b0a7ae3fa38e49-Lenovo-Mini_Dock_New.cab" + }, + { + "version": "4.33", + "file": "39da9917934ce162baefa3a67adfccd338bfd054933d6c40c0c887ab0d48e83f-Lenovo-Mini_Dock_Old.cab" + } + ] +} diff --git a/data/device-tests/devices/lenovo-40au0065-vli-tier3.json b/data/device-tests/devices/lenovo-40au0065-vli-tier3.json new file mode 100644 index 000000000..aafa032cb --- /dev/null +++ b/data/device-tests/devices/lenovo-40au0065-vli-tier3.json @@ -0,0 +1,16 @@ +{ + "name": "Lenovo USB-C Mini Dock [VL103]", + "guids": [ + "3ae6610b-5c33-5714-96e3-05735eb9b2a5" + ], + "releases": [ + { + "version": "138.4.24.38", + "file": "c782946a9cf743abc37edefe35dec1b8b2d2a88b29f7058091b0a7ae3fa38e49-Lenovo-Mini_Dock_New.cab" + }, + { + "version": "138.4.23.38", + "file": "39da9917934ce162baefa3a67adfccd338bfd054933d6c40c0c887ab0d48e83f-Lenovo-Mini_Dock_Old.cab" + } + ] +} diff --git a/data/fish-completion/fwupdmgr.fish b/data/fish-completion/fwupdmgr.fish index 1568554c4..b63bbcf86 100644 --- a/data/fish-completion/fwupdmgr.fish +++ b/data/fish-completion/fwupdmgr.fish @@ -18,7 +18,8 @@ complete -c fwupdmgr -l version -d 'Show client and daemon versions' complete -c fwupdmgr -l offline -d 'Schedule installation for next reboot when possible' complete -c fwupdmgr -l allow-reinstall -d 'Allow reinstalling existing firmware versions' complete -c fwupdmgr -l allow-older -d 'Allow downgrading firmware versions' -complete -c fwupdmgr -l force -d 'Override warnings and force the action' +complete -c fwupdmgr -l allow-branch-switch -d 'Allow switching firmware branch' +complete -c fwupdmgr -l force -d 'Force the action by relaxing some runtime checks' complete -c fwupdmgr -s y -l assume-yes -d 'Answer yes to all questions' complete -c fwupdmgr -l sign -d 'Sign the uploaded data with the client certificate' complete -c fwupdmgr -l no-unreported-check -d 'Do not check for unreported history' @@ -26,12 +27,14 @@ complete -c fwupdmgr -l no-metadata-check -d 'Do not check for old metadata' complete -c fwupdmgr -l no-reboot-check -d 'Do not check for reboot after update' complete -c fwupdmgr -l no-safety-check -d 'Do not perform device safety checks' complete -c fwupdmgr -l no-history -d 'Do not write to the history database' -complete -c fwupdmgr -l show-all-devices -d 'Show devices that are not updatable' -complete -c fwupdmgr -l disable-ssl-strict -d 'Ignore SSL strict checks when downloading files' +complete -c fwupdmgr -l show-all -d 'Show all results' +complete -c fwupdmgr -l disable-ssl-strict -d 'Ignore SSL strict checks when downloading' complete -c fwupdmgr -l filter -d 'Filter with a set of device flags' +complete -c fwupdmgr -l ignore-power -d 'Ignore requirement of external power source' # complete subcommands complete -c fwupdmgr -n '__fish_use_subcommand' -x -a activate -d 'Activate devices' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a block-firmware -d 'Blocks a specific firmware from being installed' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a clear-history -d 'Erase all firmware update history' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a clear-offline -d 'Clears any updates scheduled to be updated offline' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a clear-results -d 'Clears the results from the last update' @@ -39,6 +42,7 @@ complete -c fwupdmgr -n '__fish_use_subcommand' -x -a disable-remote -d 'Disable complete -c fwupdmgr -n '__fish_use_subcommand' -x -a downgrade -d 'Downgrades the firmware on a device' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a enable-remote -d 'Enables a given remote' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-approved-firmware -d 'Gets the list of approved firmware' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-blocked-firmware -d 'Gets the list of blocked firmware' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-details -d 'Gets details about a firmware file' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-devices -d 'Get all devices that support firmware updates' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-history -d 'Show history of firmware updates' @@ -47,19 +51,22 @@ complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-remotes -d 'Gets the c complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-results -d 'Gets the results from the last update' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-updates -d 'Gets the list of updates for connected hardware' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a install -d 'Install a firmware file on this hardware' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a modify-config -d 'Modifies a daemon configuration value.' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a modify-config -d 'Modifies a daemon configuration value' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a modify-remote -d 'Modifies a given remote' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a refresh -d 'Refresh metadata from remote server' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a reinstall -d 'Reinstall current firmware on the device.' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a reinstall -d 'Reinstall current firmware on the device' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a report-history -d 'Share firmware history with the developers' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a set-approved-firmware -d 'Sets the list of approved firmware.' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a security -d 'Gets the host security attributes' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a set-approved-firmware -d 'Sets the list of approved firmware' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a switch-branch -d 'Switch the firmware branch on the device' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a unblock-firmware -d 'Unblocks a specific firmware from being installed' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a unlock -d 'Unlocks the device for firmware access' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a update -d 'Updates all firmware to latest versions available' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a verify -d 'Checks cryptographic hash matches firmware' complete -c fwupdmgr -n '__fish_use_subcommand' -x -a verify-update -d 'Update the stored cryptographic hash with current ROM contents' # commands exclusively consuming device IDs -set -l deviceid_consumers activate clear-results downgrade get-releases get-results reinstall unlock update verify verify-update +set -l deviceid_consumers activate clear-results downgrade get-releases get-results get-updates reinstall switch-branch unlock update verify verify-update # complete device IDs complete -c fwupdmgr -n "__fish_seen_subcommand_from $deviceid_consumers" -x -a "(__fish_fwupdmgr_devices)" # complete files and device IDs diff --git a/data/fwupd.shutdown.in b/data/fwupd.shutdown.in index 53b334555..abde9394b 100755 --- a/data/fwupd.shutdown.in +++ b/data/fwupd.shutdown.in @@ -4,4 +4,8 @@ [ -f @localstatedir@/lib/fwupd/pending.db ] || exit 0 # activate firmware when we have a read-only filesysten -@bindir@/fwupdtool activate +if !@bindir@/fwupdtool activate; then + ret=$? + [ "$ret" -eq "2" ] && exit 0 + exit $ret +fi diff --git a/data/installed-tests/README.md b/data/installed-tests/README.md index d48d38dd6..8eb7b1aa0 100644 --- a/data/installed-tests/README.md +++ b/data/installed-tests/README.md @@ -9,7 +9,7 @@ By default this test suite is disabled. Enabling ======= To enable the test suite: -1. Modify `/etc/fwupd/daemon.conf` to remove the `test` plugin from `BlacklistPlugins` +1. Modify `/etc/fwupd/daemon.conf` to remove the `test` plugin from `DisabledPlugins` ``` # sed "s,^Enabled=false,Enabled=true," -i /etc/fwupd/remotes.d/fwupd-tests.conf ``` diff --git a/data/installed-tests/fakedevice123.bin b/data/installed-tests/fakedevice123.bin index a284bbbf9..885b0f234 100644 --- a/data/installed-tests/fakedevice123.bin +++ b/data/installed-tests/fakedevice123.bin @@ -1 +1 @@ -fakedevice123 -n +0x1020003 diff --git a/data/installed-tests/fakedevice123.bin.asc b/data/installed-tests/fakedevice123.bin.asc index 6cf14f45e..ed6d9b858 100644 --- a/data/installed-tests/fakedevice123.bin.asc +++ b/data/installed-tests/fakedevice123.bin.asc @@ -1,11 +1,11 @@ -----BEGIN PGP SIGNATURE----- -Version: GnuPG v2.0.14 (GNU/Linux) +Version: GnuPG v2.0.22 (GNU/Linux) -iQEcBAABAgAGBQJZQqynAAoJEEim2A5FOLrCVcsH/3Vn56wSeRCol0rOeXvoupg2 -qpTAmqUvlubv2vX1IDbcL/lHIIEAHAlN/4LRHUh+Om0T7bMKX1uSfmcgCyUTBxl0 -fm3TfXRhybi9VtZ5ZpwWxGsFsCNC9eOU0i8tB1zp9e9KjDPiYnluFkTRQ+Aw3u1u -tKBMTk6Z+VQlIUFrsveFYmPMGDkvn8AWbJCz6E4jc8can/lP/9djSi91mCqtEq/j -YTBz4OwfU80MRrSgoxykHgcB1RiT43ywfKlpHQzcO+rqCV7rv7LkXIEzBdWRZstk -XmboCnEKuMxtr+vXlGqU4n+upQkYur3Vs+07ut1OewQnJT3eeZbAH0mr42MVf7c= -=MQJe +iQEcBAABAgAGBQJfey1HAAoJEEim2A5FOLrCn2MIAK6BnVojGYSwHVpZm58b05Xs +rNqozg5pvfDfB0Bde1S0T/4TlDEnJNUku0Gz5IFNbR3ENT5VnJgBkE5xa8Rmv6cy +Gm30CmX+UE1E8qK4BVhUdbNN8bEmeMtzUMK2KfpwMXlIqcpSjpln76PQIxMHj+3P +600bkcppkLEKhiOo+THNhiHxPYJ+wjSSPm3paeMmUuApIvP4YFH8uQ5qkKLdLDVI +V5QOx3O5P3avmHu936GILG9EwV3TkR1eNOe33OqtrGvpoMTcsxUF0Wc/qmUD066d +c9hkTe01paQoN0HW/RMgrIaMnLFwK2mBcwySOo6TU9MIyQfDmLGN3u12nCrmRH8= +=Rq70 -----END PGP SIGNATURE----- diff --git a/data/installed-tests/fakedevice123.metainfo.xml b/data/installed-tests/fakedevice123.metainfo.xml index cef8c055e..52c9c71f4 100644 --- a/data/installed-tests/fakedevice123.metainfo.xml +++ b/data/installed-tests/fakedevice123.metainfo.xml @@ -1,8 +1,8 @@ - fakedevice.firmware - FakeDevice Firmware + org.fwupd.fakedevice.firmware + FakeDevice Firmware for the ACME Corp Integrated Webcam

diff --git a/data/installed-tests/fakedevice124.bin b/data/installed-tests/fakedevice124.bin index 913d074d1..f1b2c68db 100644 --- a/data/installed-tests/fakedevice124.bin +++ b/data/installed-tests/fakedevice124.bin @@ -1 +1 @@ -fakedevice124 -n +0x1020004 diff --git a/data/installed-tests/fakedevice124.bin.asc b/data/installed-tests/fakedevice124.bin.asc index d765b59d3..2b2192170 100644 --- a/data/installed-tests/fakedevice124.bin.asc +++ b/data/installed-tests/fakedevice124.bin.asc @@ -1,11 +1,11 @@ -----BEGIN PGP SIGNATURE----- -Version: GnuPG v2.0.14 (GNU/Linux) +Version: GnuPG v2.0.22 (GNU/Linux) -iQEcBAABAgAGBQJZQqy9AAoJEEim2A5FOLrCRLEH/27k0IfUtfGS8T5CPTvwW8kF -Cf6EIzw+2HgjbxLdeMNHwiHCBdIR58z44O1I9Xy6gY1vF3H4kKft6oBAUFDH0Ja5 -YpQHXMZVSNdnwdg57cyC67kLOycHTSDlLXKB74tU3R4J8xntA1cY+DSYmCs2uAjq -3T3ExfjrX6PGbRhbNr8vBUQckCxcGvEZNOws2081mTosEQNpIxFyJ2tbbKLR60d0 -5O/UDjNEYfUFCGy7MycXePEIOR+rO6KuEQ3vjJnv80UKE8msFxJTM1iKwct+B2HI -JNecCsx14BGDXCiE0Xc0heunfWiBHmNS2lymrHsU2Z82VrFqP0obD2cm64PBf0Y= -=Wsq/ +iQEcBAABAgAGBQJfey2FAAoJEEim2A5FOLrCl88IAKCggwAz3qBrBfrc91sYHCq5 +OthMyftOUTQ4JpfISY38k20pwFEhsSHKdKAYDKEVO2jopw+Cr9oTyFycWK20R2lz +tUn4e1EF8zQ29OLxGbvgGlP5/4vPJ2Cv5ujkub6LtNBrOMkNJ6+bB6G8nJZRTElU +e3wi9+E9oKPBgP40A/y79pzPiFMxXl1piYjU3JNeofd3nbtmyRqb6VAs9exQ94+p +CMWWZaJ9igxSAsQiE/NxZpO8qgG3KEmsW7yXRiaIe6xHxb49+JQdjxqS8Oc/C9sX +FSiVHDPzlUegZtcRWZy2zeSNTqmu8vzNSei0xEaLCaQ6PO+pQibxS2VZI/jDLdQ= +=Gha4 -----END PGP SIGNATURE----- diff --git a/data/installed-tests/fakedevice124.metainfo.xml b/data/installed-tests/fakedevice124.metainfo.xml index d9230f399..6b67f36cd 100644 --- a/data/installed-tests/fakedevice124.metainfo.xml +++ b/data/installed-tests/fakedevice124.metainfo.xml @@ -1,8 +1,8 @@ - fakedevice.firmware - FakeDevice Firmware + org.fwupd.fakedevice.firmware + FakeDevice

Firmware for the ACME Corp Integrated Webcam

diff --git a/data/installed-tests/fwupd.sh b/data/installed-tests/fwupd.sh new file mode 100755 index 000000000..0a6e7eb30 --- /dev/null +++ b/data/installed-tests/fwupd.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +exec 2>&1 +dirname=`dirname $0` + +run_test() +{ + $dirname/$1 + rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi +} + +run_test acpi-dmar-self-test +run_test acpi-facp-self-test +run_test ata-self-test +run_test nitrokey-self-test +run_test linux-swap-self-test +run_test nvme-self-test +run_test wacom-usb-self-test +run_test redfish-self-test +run_test optionrom-self-test +run_test vli-self-test +run_test uefi-dbx-self-test +run_test synaptics-prometheus-self-test +run_test dfu-self-test + +# success! +exit 0 diff --git a/data/installed-tests/fwupd.test.in b/data/installed-tests/fwupd.test.in new file mode 100644 index 000000000..eda783638 --- /dev/null +++ b/data/installed-tests/fwupd.test.in @@ -0,0 +1,3 @@ +[Test] +Type=session +Exec=sh -c "@installedtestsbindir@/fwupd.sh" diff --git a/data/installed-tests/fwupdmgr.sh b/data/installed-tests/fwupdmgr.sh index ed77e6d04..2d96bf4df 100755 --- a/data/installed-tests/fwupdmgr.sh +++ b/data/installed-tests/fwupdmgr.sh @@ -43,7 +43,7 @@ rc=$?; if [[ $rc != 0 ]]; then error $rc; fi # --- echo "Installing test firmware..." -fwupdmgr install ${dirname}/fakedevice124.cab +fwupdmgr update $device -y rc=$?; if [[ $rc != 0 ]]; then error $rc; fi # --- @@ -58,7 +58,7 @@ rc=$?; if [[ $rc != 0 ]]; then error $rc; fi # --- echo "Downgrading to older release (requires network access)" -fwupdmgr downgrade $device +fwupdmgr downgrade $device -y rc=$?; if [[ $rc != 0 ]]; then error $rc; fi # --- @@ -68,7 +68,7 @@ rc=$?; if [[ $rc != 2 ]]; then error $rc; fi # --- echo "Updating all devices to latest release (requires network access)" -fwupdmgr --no-unreported-check --no-metadata-check --no-reboot-check update +fwupdmgr --no-unreported-check --no-metadata-check --no-reboot-check update -y rc=$?; if [[ $rc != 0 ]]; then error $rc; fi # --- diff --git a/data/installed-tests/meson.build b/data/installed-tests/meson.build index 40918bcbc..adadbcdde 100644 --- a/data/installed-tests/meson.build +++ b/data/installed-tests/meson.build @@ -1,7 +1,6 @@ -installed_test_datadir = join_paths(datadir, 'installed-tests', 'fwupd') - con2 = configuration_data() con2.set('installedtestsdir', installed_test_datadir) +con2.set('installedtestsbindir', installed_test_bindir) con2.set('bindir', bindir) configure_file( @@ -12,6 +11,14 @@ configure_file( install_dir: installed_test_datadir, ) +configure_file( + input : 'fwupd.test.in', + output : 'fwupd.test', + configuration : con2, + install: true, + install_dir: installed_test_datadir, +) + install_data([ 'fwupdmgr.sh', 'fwupd-tests.xml', @@ -19,6 +26,12 @@ install_data([ install_dir : installed_test_datadir, ) +install_data([ + 'fwupd.sh', + ], + install_dir : installed_test_bindir, +) + custom_target('installed-cab123', input : [ 'fakedevice123.bin', diff --git a/data/org.freedesktop.fwupd.metainfo.xml b/data/org.freedesktop.fwupd.metainfo.xml index de007e34c..c912e26a5 100644 --- a/data/org.freedesktop.fwupd.metainfo.xml +++ b/data/org.freedesktop.fwupd.metainfo.xml @@ -30,97 +30,82 @@ fwupdmgr + fwupdtool + fwupdtpmevlog + fwupdagent - +

This release adds the following features:

    -
  • Add a re-implementation of the rhboot dbxtool
  • -
  • Add commands to fwupdtool for interacting with the ESP
  • -
  • Add support for the LabTop Mk IV
  • -
  • Add support for the Realtek RTD21XX I²C protocol
  • -
  • Add X-Configuration category to use for dbx updates
  • -
  • Allow blocking specific firmware releases by checksum
  • -
  • Allow plugins to set remove delay only on the child
  • -
  • Allow updating the dbx, validating it is safe to apply
  • -
  • Support download of large DFU firmware
  • -
  • Support polling the status from device in dfuManifest state
  • +
  • Include the amount of NVRAM size in use in the LVFS failure report

This release fixes the following bugs:

    -
  • Add missing Synaptics Prometheus GUIDs for ConfigId
  • -
  • Allow DFU device to attach to runtime without a bus reset
  • -
  • Be more careful doing multiple writes to the same device
  • -
  • Cancel the file monitor before disposal to avoid a potential deadlock
  • -
  • Correctly label the vebdor for more NVMe devices
  • -
  • Specify a remove delay for Poly USB Cameras
  • -
  • Use newer libxmlb features to properly display more AppStream markup
  • +
  • Delete unused EFI variables when deploying firmware
  • +
  • Fix probe warning for the Logitech Unifying device
  • +
  • Make bcm57xx hotplug more reliable
  • +
  • Recognize authorized thunderbolt value of 2
  • +
  • Remove the duplicate parent-child data in FwupdDevice and FuDevice
  • +
  • Show a less scary fwupdate output for devices without info
  • +
  • Show a link to discover more information about a specific plugin failure
  • +
  • Use a different Device ID for the OptionROM devices
  • +
  • Use UDisks to find out if swap devices are encrypted
- +

This release adds the following features:

    -
  • Add dual-image feature for VL103 backup firmware
  • -
  • Add more CCGX hybrid dock support
  • +
  • Add a compatible re-implementation of the rhboot dbxtool
  • +
  • Add async versions of the library for GUI tools
  • +
  • Add commands for interacting with the ESP to fwupdtool
  • +
  • Add firmware-extract subcommand to fwupdtool
  • +
  • Add FwupdPlugin so we can convey enumerated system errors to the end user
  • +
  • Add plugin for Goodix fingerprint sensors
  • +
  • Add plugin that can update the BCM5719 network adapter
  • +
  • Add plugin to update Elan Touchpads using HID
  • Add support for a delayed activation flow for Thunderbolt
  • +
  • Add support for ChromeOS Quiche and Gingerbread
  • +
  • Add support for Hyper hardware
  • +
  • Add support for the Host Security ID
  • +
  • Add support for ThunderBolt retimers
  • +
  • Add switch-branch command to fwupdtool and fwupdmgr
  • +
  • Allow blocking specific firmware releases by checksum
  • +
  • Allow constructing a firmware with multiple images
  • Allow firmware to require specific features from front-end clients
  • -
  • Modernize the thunderbolt plugin for future hardware
  • -
  • Support LVFS::UpdateImage in GUI clients
  • +
  • Allow updating the dbx using the LVFS, validating it is safe to apply
  • +
  • Include the HSI results and attributes in the uploaded report
  • +
  • Support loading DMI data from DT systems
  • +
  • Support LVFS::UpdateImage for GUI clients

This release fixes the following bugs:

    -
  • Be more defensive when remotes are missing required keys
  • -
  • Check all AppStream components when verifying
  • -
  • Check for free space after cleaning up ESP
  • -
  • Fix TPM PCR0 calculation
  • -
  • Only show UpdateMessage when state is success
  • -
  • Read the modem vendor ID correctly
  • -
  • Set the runtime version to 0.0.0 for pre-1.0.0 Thelio Io firmware
  • -
  • Support compiling libqmi-glib 1.26.0 and later
  • -
  • Use the GPIOB reset for the MiniDock VL103
  • -
  • Wait for the root device to be replugged when updating the MSP430
  • -
-
-
- - -

This release fixes the following regression:

-
    -
  • Fix refreshing when checking for downgraded metadata
  • -
-
-
- - -

This release adds the following features:

-
    -
  • Add support for HP DMC dock devices
  • -
-

This release fixes the following bugs:

-
    -
  • Always enforce the metadata signature has a valid timestamp
  • -
  • Capture the dock SKU in metadata
  • -
  • Check the device requirements when returning from GetDetails
  • -
  • Force the prometheus minor version from 0x02 to 0x01
  • -
  • Prevent Dell dock updates to occur via synaptics-mst plugin
  • -
-
-
- - -

This release fixes the following bugs:

-
    -
  • Add several more ATA OUI quirks
  • -
  • Avoid communicating with DFU devices when bitManifestationTolerant is off
  • -
  • Correct the display of final calculated PCRs
  • -
  • Delay activation for Dell Thunderbolt updates
  • -
  • Do not use synaptics-rmi on the Dell K12A
  • -
  • Fix switching wacom-raw to bootloader mode
  • -
  • Switch the default of EnumerateAllDevices to false
  • -
  • Use GPIOB to reset the VL817 found in two Lenovo products
  • +
  • Allow compiling the daemon without polkit support
  • +
  • Always look at all TPM eventlog supported algorithms
  • +
  • Change all instances of master/slave to initiator/target
  • +
  • Correctly order devices when using logical parents
  • +
  • Do not dedupe NVMe or VLI PD devices
  • +
  • Do not expose the VLI shared-SPI devices on the USB2 recovery device
  • +
  • Do not fix up the version on post-update mismatch
  • +
  • Download the metadata first when using 'fwupdtool refresh'
  • +
  • Drop efivar dependency
  • +
  • Drop support for ThunderBolt force power due to hardware issues
  • +
  • Fix setting BootNext correctly when multiple updates are scheduled
  • +
  • Fix the topology of the audio device on the Lenovo TR dock
  • +
  • Make return code different for get-updates with no updates
  • +
  • Make specific authorizations also imply others
  • +
  • Make TPM support more optional
  • +
  • Parse the HEX version before comparing for equality
  • +
  • Prevent dell-dock updates to occur via synaptics-mst plugin
  • +
  • Record the UEFI failure in more cases
  • +
  • Retry the HID SetReport to fix flashing the TB3 dock
  • +
  • Show an error when a plugin is missing dependencies
  • +
  • Use libxmlb bound parameters to speed up the device verification
  • +
  • Use pkttyagent to request user passwords if running without GUI
  • +
  • Use the JCat file to select the metadata file
@@ -195,6 +180,32 @@
+ + +

This release adds the following features:

+
    +
  • Added completion script for fish shell
  • +
  • Inihbit all power management actions using logind when updating
  • +
+

This release fixes the following bugs:

+
    +
  • Always check for PLAIN when doing vercmp() operations
  • +
  • Always return AppStream markup for remote agreements
  • +
  • Apply UEFI capsule update even with single valid capsule
  • +
  • Check the device protocol before de-duping devices
  • +
  • Copy the version and format from donor device in get-details
  • +
  • Correctly append the release to devices in `fwupdtool get-details`
  • +
  • Decrease minimum battery requirement to 10%
  • +
  • Discard the reason upgrades aren't available
  • +
  • Do not fail loading in /etc/machine-id is not available
  • +
  • Fix a critical warning when installing some firmware
  • +
  • For the `get-details` command make sure to always show devices
  • +
  • Set the MSP430 version format to pair
  • +
  • Switch off the ATA verbose logging by default
  • +
  • Use unknown for version format by default on get-details
  • +
+
+

This release adds the following features:

@@ -513,7 +524,7 @@
  • Do not fail to start the daemon if tpm2_pcrlist hangs
  • Do not fail when scheduling more than one update to be run offline
  • Do not let failing to find DBus prevent fwuptool from starting
  • -
  • Do not schedule an update on battery power if it requires AC power
  • +
  • Do not schedule an update on battery power if it requires an external power source
  • Include all device checksums in the LVFS report
  • Rename the shimx64.efi binary for known broken firmware
  • Upload the UPDATE_INFO entry for the UEFI UX capsule
  • diff --git a/data/remotes.d/lvfs.conf b/data/remotes.d/lvfs.conf index 1249ef746..f956bc97a 100644 --- a/data/remotes.d/lvfs.conf +++ b/data/remotes.d/lvfs.conf @@ -5,6 +5,8 @@ Enabled=true Title=Linux Vendor Firmware Service MetadataURI=https://cdn.fwupd.org/downloads/firmware.xml.gz ReportURI=https://fwupd.org/lvfs/firmware/report +SecurityReportURI=https://fwupd.org/lvfs/hsireports/upload OrderBefore=fwupd AutomaticReports=false +AutomaticSecurityReports=false ApprovalRequired=false diff --git a/data/tests/devicetree/base/ibm,firmware-versions/version b/data/tests/devicetree/base/ibm,firmware-versions/version new file mode 100644 index 000000000..94b2bd66d --- /dev/null +++ b/data/tests/devicetree/base/ibm,firmware-versions/version @@ -0,0 +1 @@ +1.2.3-4 \ No newline at end of file diff --git a/data/tests/devicetree/base/model b/data/tests/devicetree/base/model new file mode 100644 index 000000000..86c8a128b --- /dev/null +++ b/data/tests/devicetree/base/model @@ -0,0 +1 @@ +ColorHug \ No newline at end of file diff --git a/data/tests/devicetree/base/model-name b/data/tests/devicetree/base/model-name new file mode 100644 index 000000000..9d37fc1ac --- /dev/null +++ b/data/tests/devicetree/base/model-name @@ -0,0 +1 @@ +To Be Filled By O.E.M. \ No newline at end of file diff --git a/data/tests/devicetree/base/name b/data/tests/devicetree/base/name new file mode 100644 index 000000000..f76dd238a Binary files /dev/null and b/data/tests/devicetree/base/name differ diff --git a/data/tests/devicetree/base/vendor b/data/tests/devicetree/base/vendor new file mode 100644 index 000000000..1555d1a5c --- /dev/null +++ b/data/tests/devicetree/base/vendor @@ -0,0 +1 @@ +Hughski Limited \ No newline at end of file diff --git a/data/tests/devicetree/base/vpd/name b/data/tests/devicetree/base/vpd/name new file mode 100644 index 000000000..b8acbc9ee Binary files /dev/null and b/data/tests/devicetree/base/vpd/name differ diff --git a/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name new file mode 100644 index 000000000..1f22cb8d6 Binary files /dev/null and b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name differ diff --git a/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number new file mode 100644 index 000000000..e00c9e144 --- /dev/null +++ b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number @@ -0,0 +1 @@ +PCB-CH001 \ No newline at end of file diff --git a/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor new file mode 100644 index 000000000..ffbb9e015 --- /dev/null +++ b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor @@ -0,0 +1 @@ +Richard Hughes \ No newline at end of file diff --git a/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name new file mode 100644 index 000000000..88600030b Binary files /dev/null and b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name differ diff --git a/data/tests/devicetree/base/vpd/root-node-vpd@a000/name b/data/tests/devicetree/base/vpd/root-node-vpd@a000/name new file mode 100644 index 000000000..c6f7bd989 Binary files /dev/null and b/data/tests/devicetree/base/vpd/root-node-vpd@a000/name differ diff --git a/debian/changelog b/debian/changelog index 6e2b5429a..8ed35bb1c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +fwupd (1.5.1-1) UNRELEASED; urgency=medium + + * New upstream version (1.5.1) + + -- Mario Limonciello Fri, 13 Nov 2020 09:42:10 -0600 + fwupd (1.4.6-2) unstable; urgency=medium * Add udisks2 to recommends diff --git a/debian/control.in b/debian/control.in index 71be5aaf7..d700307f3 100644 --- a/debian/control.in +++ b/debian/control.in @@ -49,6 +49,7 @@ Depends: ${misc:Depends}, shared-mime-info Recommends: python3, bolt, + secureboot-db, udisks2, fwupd-signed Provides: fwupdate diff --git a/debian/fwupd-tests.install b/debian/fwupd-tests.install index 12dc15b43..5c837a649 100644 --- a/debian/fwupd-tests.install +++ b/debian/fwupd-tests.install @@ -3,6 +3,8 @@ #find them. for more information see: #https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=872458 usr/share/installed-tests/* +usr/libexec/installed-tests/fwupd/fwupd.sh +usr/libexec/installed-tests/fwupd/*-self-test usr/lib/*/fwupd-plugins-3/libfu_plugin_test.so usr/lib/*/fwupd-plugins-3/libfu_plugin_invalid.so debian/lintian/fwupd-tests usr/share/lintian/overrides diff --git a/debian/fwupd-tests.postinst b/debian/fwupd-tests.postinst index f3d686ba9..b8b61f7f5 100644 --- a/debian/fwupd-tests.postinst +++ b/debian/fwupd-tests.postinst @@ -7,7 +7,7 @@ set -e if [ "$1" = configure ] && [ -z "$2" ]; then if [ -f /etc/fwupd/daemon.conf ]; then if [ "$CI" = "true" ]; then - sed "s,^BlacklistPlugins=test;invalid,BlacklistPlugins=," -i /etc/fwupd/daemon.conf + sed "s,^DisabledPlugins=test;invalid,DisabledPlugins=," -i /etc/fwupd/daemon.conf else echo "To enable test suite, modify /etc/fwupd/daemon.conf" fi diff --git a/debian/fwupd-tests.postrm b/debian/fwupd-tests.postrm index c6fb6beaa..fc76ec2ea 100644 --- a/debian/fwupd-tests.postrm +++ b/debian/fwupd-tests.postrm @@ -6,7 +6,7 @@ set -e if [ "$1" = remove -o "$1" = purge ]; then if [ -f /etc/fwupd/daemon.conf ]; then if [ "$CI" = "true" ]; then - sed "s,^BlacklistPlugins=,BlacklistPlugins=test;invalid," -i /etc/fwupd/daemon.conf + sed "s,^DisabledPlugins=,DisabledPlugins=test;invalid," -i /etc/fwupd/daemon.conf else echo "To disable test suite, modify /etc/fwupd/daemon.conf" fi diff --git a/debian/fwupd.install b/debian/fwupd.install index ebef528bf..825909ff8 100644 --- a/debian/fwupd.install +++ b/debian/fwupd.install @@ -9,6 +9,7 @@ usr/share/polkit-1/* usr/share/locale usr/share/metainfo/* usr/libexec/fwupd/fwupd +usr/libexec/fwupd/fwupd-detect-cet usr/libexec/fwupd/fwupdoffline usr/share/man/man1/* lib/systemd/system/* diff --git a/debian/lintian/fwupd b/debian/lintian/fwupd index b262ba053..2cca20b92 100644 --- a/debian/lintian/fwupd +++ b/debian/lintian/fwupd @@ -9,3 +9,7 @@ fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-3/libfu_plugin_ue fwupd: executable-not-elf-or-script usr/libexec/fwupd/efi/*.efi fwupd: portable-executable-missing-security-features usr/libexec/fwupd/efi/*.efi SafeSEH fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-3/libfu_plugin_modem_manager.so +fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-3/libfu_plugin_pci_bcr.so +fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-3/libfu_plugin_pci_mei.so +fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-3/libfu_plugin_iommu.so +fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-3/libfu_plugin_msr.so diff --git a/debian/rules b/debian/rules index 34f0840fa..78519118c 100755 --- a/debian/rules +++ b/debian/rules @@ -14,9 +14,6 @@ ifneq ($(CI),) export CI=--werror --wrap-mode=default endif -regenerate_control: - OS=debian-x86_64 ./contrib/ci/generate_debian.py - SB_STYLE := debian deb_version := $(shell dpkg-parsechangelog --show-field Version) export FLASHROM=-Dplugin_flashrom=false @@ -47,9 +44,9 @@ override_dh_auto_configure: export DELL="-Dplugin_dell=false"; \ fi; \ if pkg-config --exists efivar; then \ - export UEFI="-Dplugin_uefi=true -Dplugin_redfish=true -Dplugin_nvme=true"; \ + export UEFI="-Dplugin_uefi=true -Dplugin_redfish=true -Dplugin_nvme=true -Dplugin_msr=true"; \ else \ - export UEFI="-Dplugin_uefi=false -Dplugin_redfish=false -Dplugin_nvme=false"; \ + export UEFI="-Dplugin_uefi=false -Dplugin_redfish=false -Dplugin_nvme=false -Dplugin_msr=false"; \ fi; \ dh_auto_configure -- $$UEFI $$DELL $$FLASHROM $$CI -Dplugin_dummy=true -Dgtkdoc=true @@ -61,9 +58,7 @@ override_dh_install: if [ -d debian/tmp/usr/libexec/fwupd/efi/ ]; then \ dh_install -pfwupd usr/libexec/fwupd/efi ;\ fi - if [ -z "$$CI" ]; then \ - dh_missing -a --fail-missing; \ - fi + dh_missing -a --fail-missing #this is placed in fwupd-tests rm -f debian/fwupd/usr/lib/*/fwupd-plugins-3/libfu_plugin_test.so diff --git a/debian/tests/ci b/debian/tests/ci index 4c630a604..ac847e768 100644 --- a/debian/tests/ci +++ b/debian/tests/ci @@ -1,5 +1,7 @@ #!/bin/sh set -e -sed "s,^BlacklistPlugins=.*,BlacklistPlugins=," -i /etc/fwupd/daemon.conf +sed "s,^DisabledPlugins=.*,DisabledPlugins=," -i /etc/fwupd/daemon.conf sed "s,^VerboseDomains=.*,VerboseDomains=*," -i /etc/fwupd/daemon.conf -gnome-desktop-testing-runner fwupd +git clone https://github.com/fwupd/fwupd-test-firmware +export G_TEST_SRCDIR=`pwd`/fwupd-test-firmware/installed-tests +CI_NETWORK=true gnome-desktop-testing-runner fwupd diff --git a/docs/fwupd-docs.xml b/docs/fwupd-docs.xml index 129aa40f2..15f6b4a35 100644 --- a/docs/fwupd-docs.xml +++ b/docs/fwupd-docs.xml @@ -26,8 +26,10 @@ + + @@ -67,405 +69,10 @@ - - Plugin Tutorial - -
    - Introduction - - At the heart of fwupd is a plugin loader that gets run at startup, - when devices get hotplugged and when updates are done. - The idea is we have lots of small plugins that each do one thing, and - are ordered by dependencies against each other at runtime. - Using plugins we can add support for new hardware or new policies - without making big changes all over the source tree. - - - There are broadly 3 types of plugin methods: - - - - - Mechanism: Upload binary data - into a specific hardware device. - - - - - Policy: Control the system when - updates are happening, e.g. preventing the user from powering-off. - - - - - Helpers: Providing more - metadata about devices, for instance handling device quirks. - - - - - In general, building things out-of-tree isn't something that we think is - a very good idea; the API and ABI internal to fwupd is still - changing and there's a huge benefit to getting plugins upstream where - they can undergo review and be ported as the API adapts. - For this reason we don't install the plugin headers onto the system, - although you can of course just install the .so binary file - manually. - - - - A plugin only needs to define the vfuncs that are required, and the - plugin name is taken automatically from the suffix of the - .so file. - - - A sample plugin - -/* - * Copyright (C) 2017 Richard Hughes - */ - -#include <fu-plugin.h> -#include <fu-plugin-vfuncs.h> - -struct FuPluginData { - gpointer proxy; -}; - -void -fu_plugin_initialize (FuPlugin *plugin) -{ - fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_BEFORE, "dfu"); - fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); -} - -void -fu_plugin_destroy (FuPlugin *plugin) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - destroy_proxy (data->proxy); -} - -gboolean -fu_plugin_startup (FuPlugin *plugin, GError **error) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - data->proxy = create_proxy (); - if (data->proxy == NULL) { - g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "failed to create proxy"); - return FALSE; - } - return TRUE; -} - - - - - We have to define when our plugin is run in reference to other plugins, - in this case, making sure we run before the dfu plugin. - For most plugins it does not matter in what order they are run and - this information is not required. - -
    - -
    - Creating an abstract device - - This section shows how you would create a device which is exported - to the daemon and thus can be queried and updated by the client software. - The example here is all hardcoded, and a true plugin would have to - derive the details about the FuDevice from the hardware, - for example reading data from sysfs or /dev. - - - Example adding a custom device - -#include <fu-plugin.h> - -gboolean -fu_plugin_coldplug (FuPlugin *plugin, GError **error) -{ - g_autoptr(FuDevice) dev = NULL; - fu_device_set_id (dev, "dummy-1:2:3"); - fu_device_add_guid (dev, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e"); - fu_device_set_version (dev, "1.2.3"); - fu_device_get_version_lowest (dev, "1.2.2"); - fu_device_get_version_bootloader (dev, "0.1.2"); - fu_device_add_icon (dev, "computer"); - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); - fu_plugin_device_add (plugin, dev); - return TRUE; -} - - - - This shows a lot of the plugin architecture in action. Some notable points: - - - - - The device ID (dummy-1:2:3) has to be unique on the - system between all plugins, so including the plugin name as a - prefix is probably a good idea. - - - - - The GUID value can be generated automatically using - fu_device_add_guid(dev,"some-identifier") but is quoted - here explicitly. - The GUID value has to match the provides value in the - .metainfo.xml file for the firmware update to succeed. - - - - - Setting a display name and an icon is a good idea in case the - GUI software needs to display the device to the user. - Icons can be specified using a full path, although icon theme names - should be preferred for most devices. - - - - - The FWUPD_DEVICE_FLAG_UPDATABLE flag tells the client - code that the device is in a state where it can be updated. - If the device needs to be in a special mode (e.g. a bootloader) then - the FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER flag can also be - used. - If the update should only be allowed when there is AC power available - to the computer (i.e. not on battery) then - FWUPD_DEVICE_FLAG_REQUIRE_AC should be used as well. - There are other flags and the API documentation should be used when - choosing what flags to use for each kind of device. - - - - - Setting the lowest allows client software to refuse downgrading - the device to specific versions. - This is required in case the upgrade migrates some kind of data-store - so as to be incompatible with previous versions. - Similarly, setting the version of the bootloader (if known) allows - the firmware to depend on a specific bootloader version, for instance - allowing signed firmware to only be installable on hardware with - a bootloader new enough to deploy it - - - -
    - -
    - Mechanism Plugins - - Although it would be a wonderful world if we could update all hardware - using a standard shared protocol this is not the universe we live in. - Using a mechanism like DFU or UpdateCapsule means that fwupd will just - work without requiring any special code, but for the real world we need - to support vendor-specific update protocols with layers of backwards - compatibility. - - - When a plugin has created a device that is FWUPD_DEVICE_FLAG_UPDATABLE - we can ask the daemon to update the device with a suitable - .cab file. - When this is done the daemon checks the update for compatibility with - the device, and then calls the vfuncs to update the device. - - - - Updating a device - -gboolean -fu_plugin_update (FuPlugin *plugin, - FuDevice *dev, - GBytes *blob_fw, - FwupdInstallFlags flags, - GError **error) -{ - gsize sz = 0; - guint8 *buf = g_bytes_get_data (blob_fw, &sz); - /* write 'buf' of size 'sz' to the hardware */ - return TRUE; -} - - - - It's important to note that the blob_fw is the binary - firmware file (e.g. .dfu) and not - the .cab binary data. - - - If FWUPD_INSTALL_FLAG_FORCE is used then the usual checks - done by the flashing process can be relaxed (e.g. checking for quirks), - but please don't brick the users hardware even if they ask you to. - -
    - -
    - Policy Helpers - - For some hardware, we might want to do an action before or after - the actual firmware is squirted into the device. - This could be something as simple as checking the system battery - level is over a certain threshold, or it could be as complicated as - ensuring a vendor-specific GPIO is asserted when specific types - of hardware are updated. - - - - Running before a device update - -gboolean -fu_plugin_update_prepare (FuPlugin *plugin, FuDevice *device, GError **error) -{ - if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC && !on_ac_power ()) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_AC_POWER_REQUIRED, - "Cannot install update " - "when not on AC power"); - return FALSE; - } - return TRUE; -} - - - - Running after a device update - -gboolean -fu_plugin_update_cleanup (FuPlugin *plugin, FuDevice *device, GError **error) -{ - return g_file_set_contents ("/var/lib/fwupd/something", - fu_device_get_id (device), -1, error); -} - - -
    - -
    - Detaching to bootloader mode - - Some hardware can only be updated in a special bootloader mode, which - for most devices can be switched to automatically. - In some cases the user to do something manually, for instance - re-inserting the hardware with a secret button pressed. - - - Before the device update is performed the fwupd daemon runs an optional - update_detach() vfunc which switches the device to - bootloader mode. - After the update (or if the update fails) an the daemon runs an - optional update_attach() vfunc which should switch the - hardware back to runtime mode. - Finally an optional update_reload() vfunc is run to - get the new firmware version from the hardware. - - - The optional vfuncs are only run - on the plugin currently registered to handle the device ID, although - the registered plugin can change during the attach and detach phases. - - - - Running before a device update - -gboolean -fu_plugin_update_detach (FuPlugin *plugin, FuDevice *device, GError **error) -{ - if (hardware_in_bootloader) - return TRUE; - return _device_detach(device, error); -} - - - - Running after a device update - -gboolean -fu_plugin_update_attach (FuPlugin *plugin, FuDevice *device, GError **error) -{ - if (!hardware_in_bootloader) - return TRUE; - return _device_attach(device, error); -} - - - - Running after a device update on success - -gboolean -fu_plugin_update_reload (FuPlugin *plugin, FuDevice *device, GError **error) -{ - g_autofree gchar *version = _get_version(plugin, device, error); - if (version == NULL) - return FALSE; - fu_device_set_version(device, version); - return TRUE; -} - - -
    - -
    - The Plugin Object Cache - - The fwupd daemon provides a per-plugin cache which allows objects - to be added, removed and queried using a specified key. - Objects added to the cache must be GObjects to enable the - cache objects to be properly refcounted. - -
    - -
    - Debugging a Plugin - - If the fwupd daemon is started with --plugin-verbose=$plugin - then the environment variable FWUPD_$PLUGIN_VERBOSE is - set process-wide. - This allows plugins to detect when they should output detailed debugging - information that would normally be too verbose to keep in the journal. - For example, using --plugin-verbose=logitech_hidpp would set - FWUPD_LOGITECH_HID_VERBOSE=1. - -
    - -
    - Using existing code to develop a plugin - - It is not usually possible to share a plugin codebase with - firmware update programs designed for other operating systems. - Matching the same rationale as the Linux kernel, trying to use one - code base between projects with a compatibility shim layer in-between - is real headache to maintain. - - - The general consensus is that trying to use a abstraction layer for - hardware is a very bad idea as you're not able to take advantage of the - platform specific helpers -- for instance quirk files and the custom - GType device creation. - The time the vendor saves by creating a shim layer and - importing existing source code into fwupd will be overtaken 100x by - upstream maintenance costs longer term, which isn't fair. - - - In a similar way, using C++ rather than GObject C means expanding the - test matrix to include clang in C++ mode and GNU g++ too. - It's also doubled the runtime requirements to now include both the C - standard library as well as the C++ standard library and increases the - dependency surface. - - - Most rewritten fwupd plugins at up to x10 smaller than the standalone - code as they can take advantage of helpers provided by fwupd rather - than re-implementing error handling, device quirking and data chunking. - -
    - -
    -
    + + API Index diff --git a/docs/hsi.xml b/docs/hsi.xml new file mode 100644 index 000000000..eb448d66c --- /dev/null +++ b/docs/hsi.xml @@ -0,0 +1,877 @@ + + + + Host Security ID Specification + + + This specification is still in active development: it is incomplete, + subject to change, and may have errors; use this at your own risk. + It is based on publicly available information. + + + + + + + + + Introduction + + Not all system vendors prioritize building a secure platform. + The truth is that security costs money. + Vendors have to choose between saving a few cents on a bill-of-materials + by sharing a SPI chip, or correctly implementing BootGuard. + Discovering security vulnerabilities often takes an external researcher + filing a disclosure. + These disclosures are often technical in nature and difficult for an + average consumer to decipher. + + + The Linux Vendor Firmware Service (LVFS) could provide some + easy-to-understand information to + people buying hardware. + The service already knows a huge amount of information about machines + from signed reports uploaded to the LVFS and from analyzing firmware binaries. + However this information alone does not explain firmware security to the + user in a way they can actually interpret. + + + + + Other Tools + + Traditionally, figuring out the true security of your hardware and firmware + requires sifting through the marketing documentation provided by the + OEM and in many cases just “trusting” they did it right. + Tools such as Chipsec can check the hardware configuration, but they do + not work out of the box and use technical jargon that an average user + cannot interpret. + Unfortunately, running a tool like Chipsec requires that you actively + turn off some security layers such as UEFI Secure Boot, and allow 3rd + party unsigned kernel modules to be loaded. + + + + + Verifying Host Firmware Security + + To start out some core protections must be assigned a relative importance. + Then an evaluation must be done to determine how each vendor is conforming + to the model. + For instance, a user might say that for home use any hardware the bare + minimum security level (HSI:1) is good enough. + For a work laptop the company IT department might restrict the choice of + models to anything meeting the criteria of level HSI:2 or + above. + A journalist or a security researcher would only buy level HSI:3 + and above. + The reality is that HSI:4 is going to be more expensive + than some unbranded hardware that is rated HSI:0. + + + To be trusted, this rating information should be distributed in a + centralized agnostic database such as the LVFS. + + + Of course, tools need to detect implementation errors, and to verify that + the model that is measured does indeed match the HSI level advertised by + the LVFS. + Some existing compliance solutions place the burden on the OEM to define + what firmware security has been implemented, which is easy to get wrong + and in some cases impossible to verify. + + + For this reason HSI will only measure security protections that can be + verified by the end user without requiring any extra hardware to be + connected, additional software to be installed, or disabling any existing + security layers to measure. + + + The HSI specification is primarily designed for laptop and desktop + hardware, although some tests may still make sense + on server or embedded hardware. + It is not expected that non-consumer hardware will publish an HSI number. + + + + + Runtime Behavior + + Orthogonal to the security features provided by the firmware there are + other security considerations related to the firmware which may require + internet access to discover or that runtime OS changes directly affect + the security of the firmware. + It would not make sense to have have updates on the LVFS + as a requirement for a specific security level as this would mean + offline the platform might be a higher level initially but as soon as + it is brought online it is downgraded which would be really confusing to + users. + The core security level will not change at + Operating System runtime, but the suffix may. + + + + + HSI:0 (Insecure) + + The lowest security level with little or no detected firmware protections. + This is the default security level if no tests can be run or some tests + in the next security level have failed. + + + + + HSI:1 (Critical) + + This security level corresponds to the most basic of security protections + considered essential by security professionals. + Any failures at this level would have critical security impact and could + likely be used to compromise the system firmware without physical access. + + + + + HSI:3 (Theoretical) + + This security level corresponds to firmware security issues that pose a + theoretical concern or where any exploit would be difficult or + impractical to use. + At this level various technologies may be employed to protect the boot + process from modification by an attacker with local access to the machine. + + + + + HSI:4 (System Protection) + + This security level corresponds to out-of-band protection of the system + firmware perhaps including recovery. + + + + + HSI:5 (System Attestation) + + This security level corresponds to out-of-band attestation of the system + firmware. + There are currently no tests implemented for HSI:5 and so this security + level cannot yet be obtained. + + + + + HSI Runtime Suffix <code>U</code> + + Updates available on the + Linux Vendor Firmware Service which are less than 12 months old. + + + + + HSI Runtime Suffix <code>A</code> + + Attestation data is available to verify the current system firmware. + For most UEFI platforms, this is usually the Trusted Platform Module (TPM) + PCR0 hash, but other attestation checksums could be used. + + + + + HSI Runtime Suffix <code>!</code> + + A runtime security issue detected. + + + + + UEFI + Secure Boot has been turned off. v1.5.0 + + + + + The kernel is + tainted due to a non-free module or critical firmware issue. v1.5.0 + + + + + The kernel is not + locked down. v1.5.0 + + + + + Unencrypted + swap partition. v1.5.0 + + + + + The installed fwupd is running with + custom or modified plugins. v1.5.0 + + + + + + + Tests included in fwupd + + The set of tests is currently x86 UEFI-centric, but will be expanded + in the future for various ARM or RISC-V firmware protections as required. + Where the requirement is architecture or processor specific it has been noted. + + + + + UEFI SecureBoot + + UEFI Secure boot is a verification mechanism for ensuring that code + launched by firmware is trusted. + + + Secure Boot requires that each binary loaded at boot is validated + against trusted certifictes. + + + + + For HSI-1 SecureBoot must be available for use on UEFI systems. + v1.5.0 + + + + + + See also: + + + + UEFI Wiki Entry + + + + + + + + + BIOS Write Enable (BWE) + + Intel hardware provides this mechanism to protect the SPI ROM chip + located on the motherboard from being overwritten by the operating system. + + + + + For HSI-1 the ``BIOSWE`` bit must be unset. v1.5.0 + + + + + + See also: + + + + Intel C200 Datasheet + + + + + + + + + BIOS Lock Enable (BLE) + + If the lock bit is set then System Management Interrupts (SMIs) are + raised when setting BIOS Write Enable. + The BLE` bit must be enabled in the PCH otherwise + BIOSWE can easily be unset. + + + + + For HSI-1 this should be set. v1.5.0 + + + + + + See also: + + + + Intel C200 Datasheet + + + + + + + + + SMM Bios Write Protect (SMM_BWP) + + This bit set defines when the BIOS region can be written by the host. + The SMM_BWP bit must be set to make the BIOS region + non-writable unless all processors are in system management mode. + + + + + For HSI-1 this should be set v1.5.0 + + + + + + See also: + + + + Intel C200 Datasheet + + + + + + + + + TPM 2.0 Present + + A TPM securely stores platform specific secrets that can only be divulged + to trusted consumers in a secure environment. + + + + + For HSI-1 this should be available for use by the OS or applications v1.5.0 + + + + + + See also: + + + + Wikipedia TPM Article + + + + + + + + + ME not in manufacturing mode + + There have been some unfortunate cases of the ME being distributed in + manufacturing mode. + In manufacturing mode many features from the ME can be interacted with + that decrease the platform’s security. + + + + + For HSI-1 this should be unset v1.5.0 + + + + + + See also: + + + + ME Manufacturing Mode: obscured dangers + + + + + Intel security advisory SA-00086 + + + + + + + + + ME Flash Descriptor Override + + The Flash Descriptor Security Override Strap is not accessible to end + users on consumer boards and Intel stresses that this is for debugging only. + + + + + For HSI-1 this should be unset v1.5.0 + + + + + + See also: + + + + Chromium documentation for Intel ME + + + + + + + + + CSME Version + + Converged Security and Manageability Engine is a standalone management + module that can manage and control some local devices without the host + CPU involvement. + The CSME lives in the PCH and can only be updated by the OEM vendor. + The version of the CSME module can be checked to detect the most common + and serious vulnerabilities. + + + + + For HSI-1 this should not be vulnerable to CVE-2017-5705, CVE-2017-5708, + CVE-2017-5711, CVE-2017-5712, CVE-2017-5711, CVE-2017-5712, CVE-2017-5706, + CVE-2017-5709, CVE-2017-5707 or CVE-2017-5710 v1.5.0 + + + + + + See also: + + + + Intel CSME Security Review Cumulative Update + + + + + + + + + Intel DCI + + Newer Intel CPUs support debugging over USB3 via a proprietary Direct + Connection Interface (DCI) with the use of off-the-shelf hardware. + DCI should always be disabled and locked on production hardware. + + + + + For HSI-1 this should be disabled. v1.5.0 + + + + + For HSI-2 this should be locked. v1.5.0 + + + + + + See also: + + + + Intel Direct Connect Interface + + + + + Chipsec 4xxlp register definitions + + + + + RISC-V EDK PCH register definitions + + + + + + + + + PCR0 TPM Event Log Reconstruction + + The TPM event log records which events are registered for the PCR0 hash. + When reconstructed the event log values should always match the TPM PCR0. + If extra events are included in the event log, or some are missing, + the reconstitution will fail. + + + + + For HSI-2 this should match the TPM-provided PCR0 v1.5.0 + + + + + + See also: + + + + Linux Kernel TPM Documentation + + + + + + + + Pre-boot DMA protection + + The IOMMU on modern systems is used to mitigate against DMA attacks. + All I/O for devices capable of DMA is mapped into a private virtual + memory region. + The ACPI DMAR table is used to set up pre-boot DMA protection which + eliminates some firmware attacks. + + + + + For HSI-2 this should be available v1.5.0 + + + + + + See also: + + + + Wikipedia IOMMU article + + + + + + + + + Intel BootGuard + + BootGuard is a processor feature that prevents the machine from running + firmware images not released by the system manufacturer. + It forms a root-of-trust by fusing in cryptographic keys into the processor + itself that are used to verify the Authenticated Code Modules found in + the SPI flash. + + + + + For HSI-1 verified boot must be enabled with ACM protection. v1.5.0 + + + + + For HSI-2 the error enforcement policy must be set to “immediate shutdown”. v1.5.0 + + + + + + See also: + + + + Coreboot documentation + + + + + + + + + Suspend to RAM disabled + + Suspend to Ram (S3) keeps the raw contents of the DRAM refreshed when + the system is asleep. + This means that the memory modules can be physically removed and the + contents recovered, or a cold boot attack can be performed with a USB device. + + + + + For HSI-3 the firmware should be configured to prefer using suspend + to idle instead of suspend to ram or to not offer suspend to + RAM. v1.5.0 + + + + + + See also: + + + + Wikipedia article on cold boot attacks + + + + + + + + + Intel CET Available + + Control enforcement technology is available on new Intel platforms and + prevents exploits from hijacking the control-flow transfer instructions + for both forward-edge (indirect call/jmp) and back-edge transfer (ret). + + + + + For HSI-3 this should be available and enabled v1.5.0 + + + + + + See also: + + + + Intel CET Technology Preview + + + + + + + + + DRAM total memory encryption (TME) + + Total memory encryption technology is used by the firmware on supported + SOCs to encrypt all data on external memory buses. + It mitigates against an attacker being able to capture memory data while + the system is running or to capture memory by removing a DRAM chip. + + + + + For HSI-4 this should be supported and enabled v1.5.0 + + + + + + See also: + + + + Intel TME Press Release + + 0 + + + + + + Supervisor Mode Access Prevention + + Without Supervisor Mode Access Prevention, the supervisor code usually + has full read and write access to user-space memory mappings. + This can make exploits easier to write, as it allows the kernel to + access user-space memory when it did not intend to. + + + + + For HSI-4 the SMAP and SMEP features should be available on the CPU. v1.5.0 + + + + + + See also: + + + + Wikipedia SMAP Article + + + + + + + + + Kernel DMA protection + + The IOMMU on modern systems is used to mitigate against DMA attacks. + All I/O for devices capable of DMA is mapped into a private virtual + memory region. + Common implementations are Intel VT-d and AMD-Vi. + + + + + For HSI-2 this should be available for use. v1.5.0 + + + + + + See also: + + + + Wikipedia IOMMU article + + + + + + + + + Suspend-to-Idle + + The platform should be set up with Suspend-to-Idle as the default S3 + sleep state. + + + + + For HSI-3 this should be set v1.5.0 + + + + + + + Conclusion + + Any system with a Host Security ID of 0 can easily be + modified from userspace. + PCs with confidential documents should have a HSI:3 or + higher level of protection. + In a graphical tool that would show details about the computer (such as + GNOME Control Center’s details tab) the OS could display a field + indicating Host Security ID. + The ID should be shown with an alert color if the security is not at + least HSI:1 or the suffix is !. + + + On Linux fwupd is used to enumerate and update firmware. + It exports a property HostSecurityId and a + GetHostSecurityAttrs() method. + The attributes are supposed to represent the system as a whole + but individual (internal) devices are able to make a claim that they + worsened the state of the security of the system. + Certain attributes can “obsolete” other attributes. + An example is BIOSGuard will set obsoletes to org.intel.prx. + + + A plugin method gets called on each plugin which adds attributes directly + from the hardware or kernel. + Several attributes may be dependent upon the kernel performing measurements + and it will take time for these to be upstreamed. + In some cases security level measurements will only be possible on systems + with a newer kernel. + + + The long term goal is to increase the HSI:x level of systems + being sold to consumers. + By making some of the HSI:x attributes part of the LVFS + uploaded report we can allow users to compare vendors and models before + purchasing hardware. + + + + + Intentional Omissions + + + Intel SGX + + This is not widely used as it has several high severity security issues. + + + + Intel MPX + + MPX support was removed from GCC and the Linux kernel in 2019 and it is + now considered obsolete. + + + + + Further Work + + More internal and external devices should be factored into the security + equation. + For now the focus for further tests should be around internal device + firmware as it is what can be most directly controlled by fwupd and the + hardware manufacturer. + + + Security conscious manufacturers are actively participating in the + development of future initiatives in the Trusted Computing Group (TCG). + As those become ratified standards that are available in hardware, + there are opportunities for synergy with this specification. + + + + + diff --git a/docs/meson.build b/docs/meson.build index 7169a5480..de07c3bf8 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -5,6 +5,11 @@ gnome.gtkdoc( join_paths(meson.source_root(), 'libfwupdplugin'), join_paths(meson.build_root(), 'libfwupd'), join_paths(meson.build_root(), 'libfwupdplugin'), + join_paths(meson.current_source_dir()), + ], + content_files : [ + 'tutorial.xml', + 'hsi.xml', ], main_xml : 'fwupd-docs.xml', install : true diff --git a/docs/tutorial.xml b/docs/tutorial.xml new file mode 100644 index 000000000..e1f306351 --- /dev/null +++ b/docs/tutorial.xml @@ -0,0 +1,402 @@ + + + + Plugin Tutorial + +
    + Introduction + + At the heart of fwupd is a plugin loader that gets run at startup, + when devices get hotplugged and when updates are done. + The idea is we have lots of small plugins that each do one thing, and + are ordered by dependencies against each other at runtime. + Using plugins we can add support for new hardware or new policies + without making big changes all over the source tree. + + + There are broadly 3 types of plugin methods: + + + + + Mechanism: Upload binary data + into a specific hardware device. + + + + + Policy: Control the system when + updates are happening, e.g. preventing the user from powering-off. + + + + + Helpers: Providing more + metadata about devices, for instance handling device quirks. + + + + + In general, building things out-of-tree isn't something that we think is + a very good idea; the API and ABI internal to fwupd is still + changing and there's a huge benefit to getting plugins upstream where + they can undergo review and be ported as the API adapts. + For this reason we don't install the plugin headers onto the system, + although you can of course just install the .so binary file + manually. + + + + A plugin only needs to define the vfuncs that are required, and the + plugin name is taken automatically from the suffix of the + .so file. + + + A sample plugin + +/* +* Copyright (C) 2017 Richard Hughes +*/ + +#include <fu-plugin.h> +#include <fu-plugin-vfuncs.h> + +struct FuPluginData { +gpointer proxy; +}; + +void +fu_plugin_initialize (FuPlugin *plugin) +{ +fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_BEFORE, "dfu"); +fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); +} + +void +fu_plugin_destroy (FuPlugin *plugin) +{ +FuPluginData *data = fu_plugin_get_data (plugin); +destroy_proxy (data->proxy); +} + +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ +FuPluginData *data = fu_plugin_get_data (plugin); +data->proxy = create_proxy (); +if (data->proxy == NULL) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, + "failed to create proxy"); + return FALSE; +} +return TRUE; +} + + + + + We have to define when our plugin is run in reference to other plugins, + in this case, making sure we run before the dfu plugin. + For most plugins it does not matter in what order they are run and + this information is not required. + +
    + +
    + Creating an abstract device + + This section shows how you would create a device which is exported + to the daemon and thus can be queried and updated by the client software. + The example here is all hardcoded, and a true plugin would have to + derive the details about the FuDevice from the hardware, + for example reading data from sysfs or /dev. + + + Example adding a custom device + +#include <fu-plugin.h> + +gboolean +fu_plugin_coldplug (FuPlugin *plugin, GError **error) +{ +g_autoptr(FuDevice) dev = NULL; +fu_device_set_id (dev, "dummy-1:2:3"); +fu_device_add_guid (dev, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e"); +fu_device_set_version (dev, "1.2.3"); +fu_device_get_version_lowest (dev, "1.2.2"); +fu_device_get_version_bootloader (dev, "0.1.2"); +fu_device_add_icon (dev, "computer"); +fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); +fu_plugin_device_add (plugin, dev); +return TRUE; +} + + + + This shows a lot of the plugin architecture in action. Some notable points: + + + + + The device ID (dummy-1:2:3) has to be unique on the + system between all plugins, so including the plugin name as a + prefix is probably a good idea. + + + + + The GUID value can be generated automatically using + fu_device_add_guid(dev,"some-identifier") but is quoted + here explicitly. + The GUID value has to match the provides value in the + .metainfo.xml file for the firmware update to succeed. + + + + + Setting a display name and an icon is a good idea in case the + GUI software needs to display the device to the user. + Icons can be specified using a full path, although icon theme names + should be preferred for most devices. + + + + + The FWUPD_DEVICE_FLAG_UPDATABLE flag tells the client + code that the device is in a state where it can be updated. + If the device needs to be in a special mode (e.g. a bootloader) then + the FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER flag can also be + used. + If the update should only be allowed when there is AC power available + to the computer (i.e. not on battery) then + FWUPD_DEVICE_FLAG_REQUIRE_AC should be used as well. + There are other flags and the API documentation should be used when + choosing what flags to use for each kind of device. + + + + + Setting the lowest allows client software to refuse downgrading + the device to specific versions. + This is required in case the upgrade migrates some kind of data-store + so as to be incompatible with previous versions. + Similarly, setting the version of the bootloader (if known) allows + the firmware to depend on a specific bootloader version, for instance + allowing signed firmware to only be installable on hardware with + a bootloader new enough to deploy it + + + +
    + +
    + Mechanism Plugins + + Although it would be a wonderful world if we could update all hardware + using a standard shared protocol this is not the universe we live in. + Using a mechanism like DFU or UpdateCapsule means that fwupd will just + work without requiring any special code, but for the real world we need + to support vendor-specific update protocols with layers of backwards + compatibility. + + + When a plugin has created a device that is FWUPD_DEVICE_FLAG_UPDATABLE + we can ask the daemon to update the device with a suitable + .cab file. + When this is done the daemon checks the update for compatibility with + the device, and then calls the vfuncs to update the device. + + + + Updating a device + +gboolean +fu_plugin_update (FuPlugin *plugin, + FuDevice *dev, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ +gsize sz = 0; +guint8 *buf = g_bytes_get_data (blob_fw, &sz); +/* write 'buf' of size 'sz' to the hardware */ +return TRUE; +} + + + + It's important to note that the blob_fw is the binary + firmware file (e.g. .dfu) and not + the .cab binary data. + + + If FWUPD_INSTALL_FLAG_FORCE is used then the usual checks + done by the flashing process can be relaxed (e.g. checking for quirks), + but please don't brick the users hardware even if they ask you to. + +
    + +
    + Policy Helpers + + For some hardware, we might want to do an action before or after + the actual firmware is squirted into the device. + This could be something as simple as checking the system battery + level is over a certain threshold, or it could be as complicated as + ensuring a vendor-specific GPIO is asserted when specific types + of hardware are updated. + + + + Running before a device update + +gboolean +fu_plugin_update_prepare (FuPlugin *plugin, FuDevice *device, GError **error) +{ +if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC && !on_ac_power ()) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_AC_POWER_REQUIRED, + "Cannot install update " + "when not on AC power"); + return FALSE; +} +return TRUE; +} + + + + Running after a device update + +gboolean +fu_plugin_update_cleanup (FuPlugin *plugin, FuDevice *device, GError **error) +{ +return g_file_set_contents ("/var/lib/fwupd/something", + fu_device_get_id (device), -1, error); +} + + +
    + +
    + Detaching to bootloader mode + + Some hardware can only be updated in a special bootloader mode, which + for most devices can be switched to automatically. + In some cases the user to do something manually, for instance + re-inserting the hardware with a secret button pressed. + + + Before the device update is performed the fwupd daemon runs an optional + update_detach() vfunc which switches the device to + bootloader mode. + After the update (or if the update fails) an the daemon runs an + optional update_attach() vfunc which should switch the + hardware back to runtime mode. + Finally an optional update_reload() vfunc is run to + get the new firmware version from the hardware. + + + The optional vfuncs are only run + on the plugin currently registered to handle the device ID, although + the registered plugin can change during the attach and detach phases. + + + + Running before a device update + +gboolean +fu_plugin_update_detach (FuPlugin *plugin, FuDevice *device, GError **error) +{ +if (hardware_in_bootloader) + return TRUE; +return _device_detach(device, error); +} + + + + Running after a device update + +gboolean +fu_plugin_update_attach (FuPlugin *plugin, FuDevice *device, GError **error) +{ +if (!hardware_in_bootloader) + return TRUE; +return _device_attach(device, error); +} + + + + Running after a device update on success + +gboolean +fu_plugin_update_reload (FuPlugin *plugin, FuDevice *device, GError **error) +{ +g_autofree gchar *version = _get_version(plugin, device, error); +if (version == NULL) + return FALSE; +fu_device_set_version(device, version); +return TRUE; +} + + +
    + +
    + The Plugin Object Cache + + The fwupd daemon provides a per-plugin cache which allows objects + to be added, removed and queried using a specified key. + Objects added to the cache must be GObjects to enable the + cache objects to be properly refcounted. + +
    + +
    + Debugging a Plugin + + If the fwupd daemon is started with --plugin-verbose=$plugin + then the environment variable FWUPD_$PLUGIN_VERBOSE is + set process-wide. + This allows plugins to detect when they should output detailed debugging + information that would normally be too verbose to keep in the journal. + For example, using --plugin-verbose=logitech_hidpp would set + FWUPD_LOGITECH_HID_VERBOSE=1. + +
    + +
    + Using existing code to develop a plugin + + It is not usually possible to share a plugin codebase with + firmware update programs designed for other operating systems. + Matching the same rationale as the Linux kernel, trying to use one + code base between projects with a compatibility shim layer in-between + is real headache to maintain. + + + The general consensus is that trying to use a abstraction layer for + hardware is a very bad idea as you're not able to take advantage of the + platform specific helpers -- for instance quirk files and the custom + GType device creation. + The time the vendor saves by creating a shim layer and + importing existing source code into fwupd will be overtaken 100x by + upstream maintenance costs longer term, which isn't fair. + + + In a similar way, using C++ rather than GObject C means expanding the + test matrix to include clang in C++ mode and GNU g++ too. + It's also doubled the runtime requirements to now include both the C + standard library as well as the C++ standard library and increases the + dependency surface. + + + Most rewritten fwupd plugins at up to x10 smaller than the standalone + code as they can take advantage of helpers provided by fwupd rather + than re-implementing error handling, device quirking and data chunking. + +
    + +
    +
    diff --git a/libfwupd/fwupd-client-private.h b/libfwupd/fwupd-client-private.h new file mode 100644 index 000000000..9ed55a449 --- /dev/null +++ b/libfwupd/fwupd-client-private.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016-2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fwupd-client.h" + +#ifdef HAVE_GIO_UNIX +#include +#endif + +#ifdef HAVE_GIO_UNIX +void fwupd_client_get_details_stream_async (FwupdClient *self, + GUnixInputStream *istr, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +void fwupd_client_install_stream_async (FwupdClient *self, + const gchar *device_id, + GUnixInputStream *istr, + const gchar *filename_hint, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +void fwupd_client_update_metadata_stream_async(FwupdClient *self, + const gchar *remote_id, + GUnixInputStream *istr, + GUnixInputStream *istr_sig, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +#endif diff --git a/libfwupd/fwupd-client-sync.c b/libfwupd/fwupd-client-sync.c new file mode 100644 index 000000000..cb6b3c23b --- /dev/null +++ b/libfwupd/fwupd-client-sync.c @@ -0,0 +1,2054 @@ +/* + * Copyright (C) 2016-2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#ifdef HAVE_GIO_UNIX +#include +#endif + +#include "fwupd-client.h" +#include "fwupd-client-private.h" +#include "fwupd-client-sync.h" +#include "fwupd-common-private.h" +#include "fwupd-error.h" + +typedef struct { + gboolean ret; + gchar *str; + GError *error; + GPtrArray *array; + GMainLoop *loop; + GVariant *val; + GHashTable *hash; + GBytes *bytes; + FwupdDevice *device; +} FwupdClientHelper; + +static void +fwupd_client_helper_free (FwupdClientHelper *helper) +{ + if (helper->val != NULL) + g_variant_unref (helper->val); + if (helper->error != NULL) + g_error_free (helper->error); + if (helper->array != NULL) + g_ptr_array_unref (helper->array); + if (helper->hash != NULL) + g_hash_table_unref (helper->hash); + if (helper->bytes != NULL) + g_bytes_unref (helper->bytes); + if (helper->device != NULL) + g_object_unref (helper->device); + g_free (helper->str); + g_main_loop_unref (helper->loop); + g_free (helper); +} + +static FwupdClientHelper * +fwupd_client_helper_new (void) +{ + FwupdClientHelper *helper; + helper = g_new0 (FwupdClientHelper, 1); + helper->loop = g_main_loop_new (NULL, FALSE); + return helper; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FwupdClientHelper, fwupd_client_helper_free) +#pragma clang diagnostic pop + +static void +fwupd_client_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_connect_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_connect: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Sets up the client ready for use. Most other methods call this + * for you, and do you only need to call this if you are just watching + * the client. + * + * Returns: %TRUE for success + * + * Since: 0.7.1 + **/ +gboolean +fwupd_client_connect (FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* call async version and run loop until complete */ + fwupd_client_connect_async (self, cancellable, fwupd_client_connect_cb, helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_get_devices_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_devices_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_devices: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets all the devices registered with the daemon. + * + * Returns: (element-type FwupdDevice) (transfer container): results + * + * Since: 0.9.2 + **/ +GPtrArray * +fwupd_client_get_devices (FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_devices_async (self, cancellable, + fwupd_client_get_devices_cb, helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->array); +} + +static void +fwupd_client_get_plugins_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_plugins_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_plugins: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets all the plugins being used the daemon. + * + * Returns: (element-type FwupdPlugin) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_plugins (FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_plugins_async (self, cancellable, + fwupd_client_get_plugins_cb, helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->array); +} + +static void +fwupd_client_get_history_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_history_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_history: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets all the history. + * + * Returns: (element-type FwupdDevice) (transfer container): results + * + * Since: 1.0.4 + **/ +GPtrArray * +fwupd_client_get_history (FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_history_async (self, cancellable, + fwupd_client_get_history_cb, helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->array); +} + +static void +fwupd_client_get_releases_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_releases_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_releases: + * @self: A #FwupdClient + * @device_id: the device ID + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets all the releases for a specific device + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 0.9.3 + **/ +GPtrArray * +fwupd_client_get_releases (FwupdClient *self, const gchar *device_id, + GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (device_id != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_releases_async (self, device_id, cancellable, + fwupd_client_get_releases_cb, helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->array); +} + +static void +fwupd_client_get_downgrades_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_downgrades_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_downgrades: + * @self: A #FwupdClient + * @device_id: the device ID + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets all the downgrades for a specific device. + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 0.9.8 + **/ +GPtrArray * +fwupd_client_get_downgrades (FwupdClient *self, const gchar *device_id, + GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (device_id != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_downgrades_async (self, device_id, cancellable, + fwupd_client_get_downgrades_cb, helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->array); +} + +static void +fwupd_client_get_upgrades_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_upgrades_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_upgrades: + * @self: A #FwupdClient + * @device_id: the device ID + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets all the upgrades for a specific device. + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 0.9.8 + **/ +GPtrArray * +fwupd_client_get_upgrades (FwupdClient *self, const gchar *device_id, + GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (device_id != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_upgrades_async (self, device_id, cancellable, + fwupd_client_get_upgrades_cb, helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->array); +} + +static void +fwupd_client_get_details_bytes_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_details_bytes_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_details_bytes: + * @self: A #FwupdClient + * @bytes: the firmware blob, e.g. the contents of `firmware.cab` + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets details about a specific firmware file. + * + * Returns: (transfer container) (element-type FwupdDevice): an array of results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_details_bytes (FwupdClient *self, + GBytes *bytes, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (bytes != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_details_bytes_async (self, bytes, + cancellable, + fwupd_client_get_details_bytes_cb, + helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->array); +} + +#ifdef HAVE_GIO_UNIX +static void +fwupd_client_get_details_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_details_bytes_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} +#endif + +/** + * fwupd_client_get_details: + * @self: A #FwupdClient + * @filename: the firmware filename, e.g. `firmware.cab` + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets details about a specific firmware file. + * + * Returns: (transfer container) (element-type FwupdDevice): an array of results + * + * Since: 1.0.0 + **/ +GPtrArray * +fwupd_client_get_details (FwupdClient *self, + const gchar *filename, + GCancellable *cancellable, + GError **error) +{ +#ifdef HAVE_GIO_UNIX + g_autoptr(GUnixInputStream) istr = NULL; + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (filename != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + istr = fwupd_unix_input_stream_from_fn (filename, error); + if (istr == NULL) + return NULL; + fwupd_client_get_details_stream_async (self, istr, cancellable, + fwupd_client_get_details_cb, + helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->array); +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); + return NULL; +#endif +} + +static void +fwupd_client_verify_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_verify_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_verify: + * @self: A #FwupdClient + * @device_id: the device ID + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Verify a specific device. + * + * Returns: %TRUE for verification success + * + * Since: 0.7.0 + **/ +gboolean +fwupd_client_verify (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_verify_async (self, device_id, cancellable, + fwupd_client_verify_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_verify_update_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_verify_update_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_verify_update: + * @self: A #FwupdClient + * @device_id: the device ID + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Update the verification record for a specific device. + * + * Returns: %TRUE for verification success + * + * Since: 0.8.0 + **/ +gboolean +fwupd_client_verify_update (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_verify_update_async (self, device_id, cancellable, + fwupd_client_verify_update_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_unlock_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_unlock_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_unlock: + * @self: A #FwupdClient + * @device_id: the device ID + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Unlocks a specific device so firmware can be read or wrote. + * + * Returns: %TRUE for success + * + * Since: 0.7.0 + **/ +gboolean +fwupd_client_unlock (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_unlock_async (self, device_id, cancellable, + fwupd_client_unlock_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} +static void +fwupd_client_modify_config_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_modify_config_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_modify_config + * @self: A #FwupdClient + * @key: key, e.g. `DisabledPlugins` + * @value: value, e.g. `*` + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Modifies a daemon config option. + * The daemon will only respond to this request with proper permissions + * + * Returns: %TRUE for success + * + * Since: 1.2.8 + **/ +gboolean +fwupd_client_modify_config (FwupdClient *self, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_modify_config_async (self, key, value, + cancellable, + fwupd_client_modify_config_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_activate_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_activate_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_activate: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @device_id: a device + * @error: the #GError, or %NULL + * + * Activates up a device, which normally means the device switches to a new + * firmware version. This should only be called when data loss cannot occur. + * + * Returns: %TRUE for success + * + * Since: 1.2.6 + **/ +gboolean +fwupd_client_activate (FwupdClient *self, + GCancellable *cancellable, + const gchar *device_id, /* yes, this is the wrong way around :/ */ + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_activate_async (self, device_id, + cancellable, + fwupd_client_activate_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_clear_results_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_clear_results_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_clear_results: + * @self: A #FwupdClient + * @device_id: the device ID + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Clears the results for a specific device. + * + * Returns: %TRUE for success + * + * Since: 0.7.0 + **/ +gboolean +fwupd_client_clear_results (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_clear_results_async (self, device_id, + cancellable, + fwupd_client_clear_results_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_get_results_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->device = fwupd_client_get_results_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_results: + * @self: A #FwupdClient + * @device_id: the device ID + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets the results of a previous firmware update for a specific device. + * + * Returns: (transfer full): a #FwupdDevice, or %NULL for failure + * + * Since: 0.7.0 + **/ +FwupdDevice * +fwupd_client_get_results (FwupdClient *self, const gchar *device_id, + GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (device_id != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_results_async (self, device_id, cancellable, + fwupd_client_get_results_cb, helper); + g_main_loop_run (helper->loop); + if (helper->device == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->device); +} + +static void +fwupd_client_get_host_security_attrs_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_host_security_attrs_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_host_security_attrs: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets all the host security attributes from the daemon. + * + * Returns: (element-type FwupdSecurityAttr) (transfer container): attributes + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_host_security_attrs (FwupdClient *self, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_host_security_attrs_async (self, cancellable, + fwupd_client_get_host_security_attrs_cb, + helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->array); +} + +static void +fwupd_client_get_device_by_id_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->device = fwupd_client_get_device_by_id_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_device_by_id: + * @self: A #FwupdClient + * @device_id: the device ID, e.g. `usb:00:01:03:03` + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets a device by it's device ID. + * + * Returns: (transfer full): a #FwupdDevice or %NULL + * + * Since: 0.9.3 + **/ +FwupdDevice * +fwupd_client_get_device_by_id (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (device_id != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_device_by_id_async (self, device_id, cancellable, + fwupd_client_get_device_by_id_cb, + helper); + g_main_loop_run (helper->loop); + if (helper->device == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->device); +} + +static void +fwupd_client_get_devices_by_guid_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_devices_by_guid_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_devices_by_guid: + * @self: A #FwupdClient + * @guid: the GUID, e.g. `e22c4520-43dc-5bb3-8245-5787fead9b63` + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets any devices that provide a specific GUID. An error is returned if no + * devices contains this GUID. + * + * Returns: (element-type FwupdDevice) (transfer container): devices or %NULL + * + * Since: 1.4.1 + **/ +GPtrArray * +fwupd_client_get_devices_by_guid (FwupdClient *self, const gchar *guid, + GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (guid != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_devices_by_guid_async (self, guid, cancellable, + fwupd_client_get_devices_by_guid_cb, + helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->array); +} + +#ifdef HAVE_GIO_UNIX +static void +fwupd_client_install_fd_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_install_bytes_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} +#endif + +/** + * fwupd_client_install: + * @self: A #FwupdClient + * @device_id: the device ID + * @filename: the filename to install + * @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Install a file onto a specific device. + * + * Returns: %TRUE for success + * + * Since: 0.7.0 + **/ +gboolean +fwupd_client_install (FwupdClient *self, + const gchar *device_id, + const gchar *filename, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error) +{ +#ifdef HAVE_GIO_UNIX + g_autoptr(GUnixInputStream) istr = NULL; + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* move to a thread if this ever takes more than a few ms */ + istr = fwupd_unix_input_stream_from_fn (filename, error); + if (istr == NULL) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_install_stream_async (self, device_id, istr, filename, + install_flags, cancellable, + fwupd_client_install_fd_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); + return FALSE; +#endif +} + +static void +fwupd_client_install_bytes_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_install_bytes_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_install_bytes: + * @self: A #FwupdClient + * @device_id: the device ID + * @bytes: #GBytes + * @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Install firmware onto a specific device. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fwupd_client_install_bytes (FwupdClient *self, + const gchar *device_id, + GBytes *bytes, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (bytes != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_install_bytes_async (self, device_id, bytes, install_flags, + cancellable, + fwupd_client_install_bytes_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} +static void +fwupd_client_install_release_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_install_release_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_install_release: + * @self: A #FwupdClient + * @device: A #FwupdDevice + * @release: A #FwupdRelease + * @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Installs a new release on a device, downloading the firmware if required. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fwupd_client_install_release (FwupdClient *self, + FwupdDevice *device, + FwupdRelease *release, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (FWUPD_IS_DEVICE (device), FALSE); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_install_release_async (self, device, release, + install_flags, cancellable, + fwupd_client_install_release_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +#ifdef HAVE_GIO_UNIX +static void +fwupd_client_update_metadata_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_update_metadata_bytes_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} +#endif + +/** + * fwupd_client_update_metadata: + * @self: A #FwupdClient + * @remote_id: the remote ID, e.g. `lvfs-testing` + * @metadata_fn: the XML metadata filename + * @signature_fn: the GPG signature file + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Updates the metadata. This allows a session process to download the metadata + * and metadata signing file to be passed into the daemon to be checked and + * parsed. + * + * The @remote_id allows the firmware to be tagged so that the remote can be + * matched when the firmware is downloaded. + * + * Returns: %TRUE for success + * + * Since: 1.0.0 + **/ +gboolean +fwupd_client_update_metadata (FwupdClient *self, + const gchar *remote_id, + const gchar *metadata_fn, + const gchar *signature_fn, + GCancellable *cancellable, + GError **error) +{ +#ifdef HAVE_GIO_UNIX + g_autoptr(GUnixInputStream) istr = NULL; + g_autoptr(GUnixInputStream) istr_sig = NULL; + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (remote_id != NULL, FALSE); + g_return_val_if_fail (metadata_fn != NULL, FALSE); + g_return_val_if_fail (signature_fn != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + istr = fwupd_unix_input_stream_from_fn (metadata_fn, error); + if (istr == NULL) + return FALSE; + istr_sig = fwupd_unix_input_stream_from_fn (signature_fn, error); + if (istr_sig == NULL) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_update_metadata_stream_async (self, remote_id, istr, istr_sig, cancellable, + fwupd_client_update_metadata_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); + return FALSE; +#endif +} + +static void +fwupd_client_update_metadata_bytes_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_update_metadata_bytes_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_update_metadata_bytes: + * @self: A #FwupdClient + * @remote_id: remote ID, e.g. `lvfs-testing` + * @metadata: XML metadata data + * @signature: signature data + * @cancellable: #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Updates the metadata. This allows a session process to download the metadata + * and metadata signing file to be passed into the daemon to be checked and + * parsed. + * + * The @remote_id allows the firmware to be tagged so that the remote can be + * matched when the firmware is downloaded. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fwupd_client_update_metadata_bytes (FwupdClient *self, + const gchar *remote_id, + GBytes *metadata, + GBytes *signature, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (remote_id != NULL, FALSE); + g_return_val_if_fail (metadata != NULL, FALSE); + g_return_val_if_fail (signature != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_update_metadata_bytes_async (self, remote_id, metadata, signature, + cancellable, + fwupd_client_update_metadata_bytes_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_refresh_remote_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_refresh_remote_finish (FWUPD_CLIENT (source), + res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_refresh_remote: + * @self: A #FwupdClient + * @remote: A #FwupdRemote + * @cancellable: A #GCancellable, or %NULL + * @error: A #GError, or %NULL + * + * Refreshes a remote by downloading new metadata. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fwupd_client_refresh_remote (FwupdClient *self, + FwupdRemote *remote, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (FWUPD_IS_REMOTE (remote), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + fwupd_client_refresh_remote_async (self, remote, cancellable, + fwupd_client_refresh_remote_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_modify_remote_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_modify_remote_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_modify_remote: + * @self: A #FwupdClient + * @remote_id: the remote ID, e.g. `lvfs-testing` + * @key: the key, e.g. `Enabled` + * @value: the key, e.g. `true` + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Modifies a system remote in a specific way. + * + * NOTE: User authentication may be required to complete this action. + * + * Returns: %TRUE for success + * + * Since: 0.9.8 + **/ +gboolean +fwupd_client_modify_remote (FwupdClient *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (remote_id != NULL, FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_modify_remote_async (self, remote_id, key, value, + cancellable, + fwupd_client_modify_remote_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_get_report_metadata_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->hash = fwupd_client_get_report_metadata_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_report_metadata: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets all the report metadata from the daemon. + * + * Returns: (transfer container): attributes + * + * Since: 1.5.0 + **/ +GHashTable * +fwupd_client_get_report_metadata (FwupdClient *self, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_report_metadata_async (self, cancellable, + fwupd_client_get_report_metadata_cb, + helper); + g_main_loop_run (helper->loop); + if (helper->hash == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->hash); +} + +static void +fwupd_client_modify_device_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_modify_device_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_modify_device: + * @self: A #FwupdClient + * @device_id: the device ID + * @key: the key, e.g. `Flags` + * @value: the key, e.g. `reported` + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Modifies a device in a specific way. Not all properties on the #FwupdDevice + * are settable by the client, and some may have other restrictions on @value. + * + * NOTE: User authentication may be required to complete this action. + * + * Returns: %TRUE for success + * + * Since: 1.0.4 + **/ +gboolean +fwupd_client_modify_device (FwupdClient *self, + const gchar *device_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_modify_device_async (self, device_id, key, value, + cancellable, + fwupd_client_modify_device_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_get_remotes_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_remotes_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_remotes: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets the list of remotes that have been configured for the system. + * + * Returns: (element-type FwupdRemote) (transfer container): list of remotes, or %NULL + * + * Since: 0.9.3 + **/ +GPtrArray * +fwupd_client_get_remotes (FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_remotes_async (self, cancellable, + fwupd_client_get_remotes_cb, helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->array); +} + +static FwupdRemote * +fwupd_client_get_remote_by_id_noref (GPtrArray *remotes, const gchar *remote_id) +{ + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index (remotes, i); + if (g_strcmp0 (remote_id, fwupd_remote_get_id (remote)) == 0) + return remote; + } + return NULL; +} + +/** + * fwupd_client_get_remote_by_id: + * @self: A #FwupdClient + * @remote_id: the remote ID, e.g. `lvfs-testing` + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets a specific remote that has been configured for the system. + * + * Returns: (transfer full): a #FwupdRemote, or %NULL if not found + * + * Since: 0.9.3 + **/ +FwupdRemote * +fwupd_client_get_remote_by_id (FwupdClient *self, + const gchar *remote_id, + GCancellable *cancellable, + GError **error) +{ + FwupdRemote *remote; + g_autoptr(GPtrArray) remotes = NULL; + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (remote_id != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* find remote in list */ + remotes = fwupd_client_get_remotes (self, cancellable, error); + if (remotes == NULL) + return NULL; + remote = fwupd_client_get_remote_by_id_noref (remotes, remote_id); + if (remote == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No remote '%s' found in search paths", + remote_id); + return NULL; + } + + /* success */ + return g_object_ref (remote); +} + +static void +fwupd_client_get_approved_firmware_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_approved_firmware_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_approved_firmware: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets the list of approved firmware. + * + * Returns: (transfer full): checksums, or %NULL for error + * + * Since: 1.2.6 + **/ +gchar ** +fwupd_client_get_approved_firmware (FwupdClient *self, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + gchar **argv; + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_approved_firmware_async (self, cancellable, + fwupd_client_get_approved_firmware_cb, + helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + argv = g_new0 (gchar *, helper->array->len + 1); + for (guint i = 0; i < helper->array->len; i++) { + const gchar *tmp = g_ptr_array_index (helper->array, i); + argv[i] = g_strdup (tmp); + } + return argv; +} + +static void +fwupd_client_set_approved_firmware_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_set_approved_firmware_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_set_approved_firmware: + * @self: A #FwupdClient + * @checksums: Array of checksums + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Sets the list of approved firmware. + * + * Returns: %TRUE for success + * + * Since: 1.2.6 + **/ +gboolean +fwupd_client_set_approved_firmware (FwupdClient *self, + gchar **checksums, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* convert */ + for (guint i = 0; checksums[i] != NULL; i++) + g_ptr_array_add (array, g_strdup (checksums[i])); + + /* call async version and run loop until complete */ + fwupd_client_set_approved_firmware_async (self, array, cancellable, + fwupd_client_set_approved_firmware_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_get_blocked_firmware_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->array = fwupd_client_get_blocked_firmware_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_get_blocked_firmware: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Gets the list of blocked firmware. + * + * Returns: (transfer full): checksums, or %NULL for error + * + * Since: 1.4.6 + **/ +gchar ** +fwupd_client_get_blocked_firmware (FwupdClient *self, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + gchar **argv; + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_get_blocked_firmware_async (self, cancellable, + fwupd_client_get_blocked_firmware_cb, + helper); + g_main_loop_run (helper->loop); + if (helper->array == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + argv = g_new0 (gchar *, helper->array->len + 1); + for (guint i = 0; i < helper->array->len; i++) { + const gchar *tmp = g_ptr_array_index (helper->array, i); + argv[i] = g_strdup (tmp); + } + return argv; +} + +static void +fwupd_client_set_blocked_firmware_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_set_blocked_firmware_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_set_blocked_firmware: + * @self: A #FwupdClient + * @checksums: Array of checksums + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Sets the list of approved firmware. + * + * Returns: %TRUE for success + * + * Since: 1.4.6 + **/ +gboolean +fwupd_client_set_blocked_firmware (FwupdClient *self, + gchar **checksums, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (checksums != NULL, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + for (guint i = 0; checksums[i] != NULL; i++) + g_ptr_array_add (array, g_strdup (checksums[i])); + + /* call async version and run loop until complete */ + fwupd_client_set_blocked_firmware_async (self, array, cancellable, + fwupd_client_set_blocked_firmware_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_set_feature_flags_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->ret = fwupd_client_set_feature_flags_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_set_feature_flags: + * @self: A #FwupdClient + * @feature_flags: #FwupdFeatureFlags, e.g. %FWUPD_FEATURE_FLAG_UPDATE_TEXT + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Sets the features the client supports. This allows firmware to depend on + * specific front-end features, for instance showing the user an image on + * how to detach the hardware. + * + * Clients can call this none or multiple times. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fwupd_client_set_feature_flags (FwupdClient *self, + FwupdFeatureFlags feature_flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + fwupd_client_set_feature_flags_async (self, feature_flags, cancellable, + fwupd_client_set_feature_flags_cb, + helper); + g_main_loop_run (helper->loop); + if (!helper->ret) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_self_sign_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->str = fwupd_client_self_sign_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_self_sign: + * @self: A #FwupdClient + * @value: A string to sign, typically a JSON blob + * @flags: #FwupdSelfSignFlags, e.g. %FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Signs the data using the client self-signed certificate. + * + * Returns: a signature, or %NULL for failure + * + * Since: 1.2.6 + **/ +gchar * +fwupd_client_self_sign (FwupdClient *self, + const gchar *value, + FwupdSelfSignFlags flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (value != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_self_sign_async (self, value, flags, cancellable, + fwupd_client_self_sign_cb, + helper); + g_main_loop_run (helper->loop); + if (helper->str == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->str); +} + +static void +fwupd_client_download_bytes_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->bytes = fwupd_client_download_bytes_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_download_bytes: + * @self: A #FwupdClient + * @url: the remote URL + * @flags: #FwupdClientDownloadFlags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Downloads data from a remote server. The fwupd_client_set_user_agent() function + * should be called before this method is used. + * + * Returns: (transfer full): downloaded data, or %NULL for error + * + * Since: 1.4.5 + **/ +GBytes * +fwupd_client_download_bytes (FwupdClient *self, + const gchar *url, + FwupdClientDownloadFlags flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (url != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_download_bytes_async (self, url, flags, cancellable, + fwupd_client_download_bytes_cb, helper); + g_main_loop_run (helper->loop); + if (helper->bytes == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->bytes); +} + +static void +fwupd_client_upload_bytes_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *) user_data; + helper->bytes = fwupd_client_upload_bytes_finish (FWUPD_CLIENT (source), res, &helper->error); + g_main_loop_quit (helper->loop); +} + +/** + * fwupd_client_upload_bytes: + * @self: A #FwupdClient + * @url: the remote URL + * @payload: payload string + * @signature: (nullable): signature string + * @flags: #FwupdClientDownloadFlags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE + * @cancellable: the #GCancellable, or %NULL + * @error: the #GError, or %NULL + * + * Uploads data to a remote server. The fwupd_client_set_user_agent() function + * should be called before this method is used. + * + * Returns: (transfer full): response data, or %NULL for error + * + * Since: 1.4.5 + **/ +GBytes * +fwupd_client_upload_bytes (FwupdClient *self, + const gchar *url, + const gchar *payload, + const gchar *signature, + FwupdClientUploadFlags flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = fwupd_client_helper_new (); + + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (url != NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect (self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + fwupd_client_upload_bytes_async (self, url, payload, signature, flags, cancellable, + fwupd_client_upload_bytes_cb, helper); + g_main_loop_run (helper->loop); + if (helper->bytes == NULL) { + g_propagate_error (error, g_steal_pointer (&helper->error)); + return NULL; + } + return g_steal_pointer (&helper->bytes); +} diff --git a/libfwupd/fwupd-client-sync.h b/libfwupd/fwupd-client-sync.h new file mode 100644 index 000000000..974b5a44f --- /dev/null +++ b/libfwupd/fwupd-client-sync.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2016-2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fwupd-client.h" + +gboolean fwupd_client_connect (FwupdClient *self, + GCancellable *cancellable, + GError **error); +GPtrArray *fwupd_client_get_devices (FwupdClient *self, + GCancellable *cancellable, + GError **error); +GPtrArray *fwupd_client_get_plugins (FwupdClient *self, + GCancellable *cancellable, + GError **error); +GPtrArray *fwupd_client_get_history (FwupdClient *self, + GCancellable *cancellable, + GError **error); +GPtrArray *fwupd_client_get_releases (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error); +GPtrArray *fwupd_client_get_downgrades (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error); +GPtrArray *fwupd_client_get_upgrades (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error); +GPtrArray *fwupd_client_get_details (FwupdClient *self, + const gchar *filename, + GCancellable *cancellable, + GError **error); +GPtrArray *fwupd_client_get_details_bytes (FwupdClient *self, + GBytes *bytes, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_verify (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_verify_update (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_unlock (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_modify_config (FwupdClient *self, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_activate (FwupdClient *self, + GCancellable *cancellable, + const gchar *device_id, + GError **error); +gboolean fwupd_client_clear_results (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error); +FwupdDevice *fwupd_client_get_results (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error); +GPtrArray *fwupd_client_get_host_security_attrs (FwupdClient *self, + GCancellable *cancellable, + GError **error); +FwupdDevice *fwupd_client_get_device_by_id (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error); +GPtrArray *fwupd_client_get_devices_by_guid (FwupdClient *self, + const gchar *guid, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_install (FwupdClient *self, + const gchar *device_id, + const gchar *filename, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_install_bytes (FwupdClient *self, + const gchar *device_id, + GBytes *bytes, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_install_release (FwupdClient *self, + FwupdDevice *device, + FwupdRelease *release, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_update_metadata (FwupdClient *self, + const gchar *remote_id, + const gchar *metadata_fn, + const gchar *signature_fn, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_update_metadata_bytes (FwupdClient *self, + const gchar *remote_id, + GBytes *metadata, + GBytes *signature, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_refresh_remote (FwupdClient *self, + FwupdRemote *remote, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_modify_remote (FwupdClient *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_modify_device (FwupdClient *self, + const gchar *device_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error); +GHashTable *fwupd_client_get_report_metadata (FwupdClient *self, + GCancellable *cancellable, + GError **error); +GPtrArray *fwupd_client_get_remotes (FwupdClient *self, + GCancellable *cancellable, + GError **error); +FwupdRemote *fwupd_client_get_remote_by_id (FwupdClient *self, + const gchar *remote_id, + GCancellable *cancellable, + GError **error); +gchar **fwupd_client_get_approved_firmware (FwupdClient *self, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_set_approved_firmware (FwupdClient *self, + gchar **checksums, + GCancellable *cancellable, + GError **error); +gchar **fwupd_client_get_blocked_firmware (FwupdClient *self, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_set_blocked_firmware (FwupdClient *self, + gchar **checksums, + GCancellable *cancellable, + GError **error); +gchar *fwupd_client_self_sign (FwupdClient *self, + const gchar *value, + FwupdSelfSignFlags flags, + GCancellable *cancellable, + GError **error); +gboolean fwupd_client_set_feature_flags (FwupdClient *self, + FwupdFeatureFlags feature_flags, + GCancellable *cancellable, + GError **error); +GBytes *fwupd_client_download_bytes (FwupdClient *self, + const gchar *url, + FwupdClientDownloadFlags flags, + GCancellable *cancellable, + GError **error); +GBytes *fwupd_client_upload_bytes (FwupdClient *self, + const gchar *url, + const gchar *payload, + const gchar *signature, + FwupdClientUploadFlags flags, + GCancellable *cancellable, + GError **error); diff --git a/libfwupd/fwupd-client.c b/libfwupd/fwupd-client.c index 23e30c863..e5e360a20 100644 --- a/libfwupd/fwupd-client.c +++ b/libfwupd/fwupd-client.c @@ -18,12 +18,15 @@ #include #include -#include "fwupd-client.h" +#include "fwupd-client-private.h" +#include "fwupd-client-sync.h" #include "fwupd-common-private.h" #include "fwupd-deprecated.h" #include "fwupd-enums.h" #include "fwupd-error.h" #include "fwupd-device-private.h" +#include "fwupd-plugin-private.h" +#include "fwupd-security-attr-private.h" #include "fwupd-release-private.h" #include "fwupd-remote-private.h" @@ -46,6 +49,7 @@ typedef struct { gchar *daemon_version; gchar *host_product; gchar *host_machine_id; + gchar *host_security_id; GDBusConnection *conn; GDBusProxy *proxy; SoupSession *soup_session; @@ -70,6 +74,7 @@ enum { PROP_SOUP_SESSION, PROP_HOST_PRODUCT, PROP_HOST_MACHINE_ID, + PROP_HOST_SECURITY_ID, PROP_INTERACTIVE, PROP_LAST }; @@ -79,98 +84,72 @@ static guint signals [SIGNAL_LAST] = { 0 }; G_DEFINE_TYPE_WITH_PRIVATE (FwupdClient, fwupd_client, G_TYPE_OBJECT) #define GET_PRIVATE(o) (fwupd_client_get_instance_private (o)) -typedef struct { - gboolean ret; - GError *error; - GMainLoop *loop; - GVariant *val; - GDBusMessage *message; -} FwupdClientHelper; - static void -fwupd_client_helper_free (FwupdClientHelper *helper) +fwupd_client_set_host_product (FwupdClient *self, const gchar *host_product) { - if (helper->message != NULL) - g_object_unref (helper->message); - if (helper->val != NULL) - g_variant_unref (helper->val); - if (helper->error != NULL) - g_error_free (helper->error); - g_main_loop_unref (helper->loop); - g_free (helper); -} - -static FwupdClientHelper * -fwupd_client_helper_new (void) -{ - FwupdClientHelper *helper; - helper = g_new0 (FwupdClientHelper, 1); - helper->loop = g_main_loop_new (NULL, FALSE); - return helper; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(FwupdClientHelper, fwupd_client_helper_free) -#pragma clang diagnostic pop - -static void -fwupd_client_set_host_product (FwupdClient *client, const gchar *host_product) -{ - FwupdClientPrivate *priv = GET_PRIVATE (client); + FwupdClientPrivate *priv = GET_PRIVATE (self); g_free (priv->host_product); priv->host_product = g_strdup (host_product); - g_object_notify (G_OBJECT (client), "host-product"); + g_object_notify (G_OBJECT (self), "host-product"); } static void -fwupd_client_set_host_machine_id (FwupdClient *client, const gchar *host_machine_id) +fwupd_client_set_host_machine_id (FwupdClient *self, const gchar *host_machine_id) { - FwupdClientPrivate *priv = GET_PRIVATE (client); + FwupdClientPrivate *priv = GET_PRIVATE (self); g_free (priv->host_machine_id); priv->host_machine_id = g_strdup (host_machine_id); - g_object_notify (G_OBJECT (client), "host-machine-id"); + g_object_notify (G_OBJECT (self), "host-machine-id"); } static void -fwupd_client_set_daemon_version (FwupdClient *client, const gchar *daemon_version) +fwupd_client_set_host_security_id (FwupdClient *self, const gchar *host_security_id) { - FwupdClientPrivate *priv = GET_PRIVATE (client); + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_free (priv->host_security_id); + priv->host_security_id = g_strdup (host_security_id); + g_object_notify (G_OBJECT (self), "host-security-id"); +} + +static void +fwupd_client_set_daemon_version (FwupdClient *self, const gchar *daemon_version) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); g_free (priv->daemon_version); priv->daemon_version = g_strdup (daemon_version); - g_object_notify (G_OBJECT (client), "daemon-version"); + g_object_notify (G_OBJECT (self), "daemon-version"); } static void -fwupd_client_set_status (FwupdClient *client, FwupdStatus status) +fwupd_client_set_status (FwupdClient *self, FwupdStatus status) { - FwupdClientPrivate *priv = GET_PRIVATE (client); + FwupdClientPrivate *priv = GET_PRIVATE (self); if (priv->status == status) return; priv->status = status; g_debug ("Emitting ::status-changed() [%s]", fwupd_status_to_string (priv->status)); - g_signal_emit (client, signals[SIGNAL_STATUS_CHANGED], 0, priv->status); - g_object_notify (G_OBJECT (client), "status"); + g_signal_emit (self, signals[SIGNAL_STATUS_CHANGED], 0, priv->status); + g_object_notify (G_OBJECT (self), "status"); } static void -fwupd_client_set_percentage (FwupdClient *client, guint percentage) +fwupd_client_set_percentage (FwupdClient *self, guint percentage) { - FwupdClientPrivate *priv = GET_PRIVATE (client); + FwupdClientPrivate *priv = GET_PRIVATE (self); if (priv->percentage == percentage) return; priv->percentage = percentage; - g_object_notify (G_OBJECT (client), "percentage"); + g_object_notify (G_OBJECT (self), "percentage"); } static void fwupd_client_properties_changed_cb (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidated_properties, - FwupdClient *client) + FwupdClient *self) { - FwupdClientPrivate *priv = GET_PRIVATE (client); + FwupdClientPrivate *priv = GET_PRIVATE (self); g_autoptr(GVariantDict) dict = NULL; /* print to the console */ @@ -179,14 +158,14 @@ fwupd_client_properties_changed_cb (GDBusProxy *proxy, g_autoptr(GVariant) val = NULL; val = g_dbus_proxy_get_cached_property (proxy, "Status"); if (val != NULL) - fwupd_client_set_status (client, g_variant_get_uint32 (val)); + fwupd_client_set_status (self, g_variant_get_uint32 (val)); } if (g_variant_dict_contains (dict, "Tainted")) { g_autoptr(GVariant) val = NULL; val = g_dbus_proxy_get_cached_property (proxy, "Tainted"); if (val != NULL) { priv->tainted = g_variant_get_boolean (val); - g_object_notify (G_OBJECT (client), "tainted"); + g_object_notify (G_OBJECT (self), "tainted"); } } if (g_variant_dict_contains (dict, "Interactive")) { @@ -194,32 +173,38 @@ fwupd_client_properties_changed_cb (GDBusProxy *proxy, val = g_dbus_proxy_get_cached_property (proxy, "Interactive"); if (val != NULL) { priv->interactive = g_variant_get_boolean (val); - g_object_notify (G_OBJECT (client), "interactive"); + g_object_notify (G_OBJECT (self), "interactive"); } } if (g_variant_dict_contains (dict, "Percentage")) { g_autoptr(GVariant) val = NULL; val = g_dbus_proxy_get_cached_property (proxy, "Percentage"); if (val != NULL) - fwupd_client_set_percentage (client, g_variant_get_uint32 (val)); + fwupd_client_set_percentage (self, g_variant_get_uint32 (val)); } if (g_variant_dict_contains (dict, "DaemonVersion")) { g_autoptr(GVariant) val = NULL; val = g_dbus_proxy_get_cached_property (proxy, "DaemonVersion"); if (val != NULL) - fwupd_client_set_daemon_version (client, g_variant_get_string (val, NULL)); + fwupd_client_set_daemon_version (self, g_variant_get_string (val, NULL)); } if (g_variant_dict_contains (dict, "HostProduct")) { g_autoptr(GVariant) val = NULL; val = g_dbus_proxy_get_cached_property (proxy, "HostProduct"); if (val != NULL) - fwupd_client_set_host_product (client, g_variant_get_string (val, NULL)); + fwupd_client_set_host_product (self, g_variant_get_string (val, NULL)); } if (g_variant_dict_contains (dict, "HostMachineId")) { g_autoptr(GVariant) val = NULL; val = g_dbus_proxy_get_cached_property (proxy, "HostMachineId"); if (val != NULL) - fwupd_client_set_host_machine_id (client, g_variant_get_string (val, NULL)); + fwupd_client_set_host_machine_id (self, g_variant_get_string (val, NULL)); + } + if (g_variant_dict_contains (dict, "HostSecurityId")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property (proxy, "HostSecurityId"); + if (val != NULL) + fwupd_client_set_host_security_id (self, g_variant_get_string (val, NULL)); } } @@ -228,31 +213,31 @@ fwupd_client_signal_cb (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters, - FwupdClient *client) + FwupdClient *self) { g_autoptr(FwupdDevice) dev = NULL; if (g_strcmp0 (signal_name, "Changed") == 0) { g_debug ("Emitting ::changed()"); - g_signal_emit (client, signals[SIGNAL_CHANGED], 0); + g_signal_emit (self, signals[SIGNAL_CHANGED], 0); return; } if (g_strcmp0 (signal_name, "DeviceAdded") == 0) { dev = fwupd_device_from_variant (parameters); g_debug ("Emitting ::device-added(%s)", fwupd_device_get_id (dev)); - g_signal_emit (client, signals[SIGNAL_DEVICE_ADDED], 0, dev); + g_signal_emit (self, signals[SIGNAL_DEVICE_ADDED], 0, dev); return; } if (g_strcmp0 (signal_name, "DeviceRemoved") == 0) { dev = fwupd_device_from_variant (parameters); - g_signal_emit (client, signals[SIGNAL_DEVICE_REMOVED], 0, dev); + g_signal_emit (self, signals[SIGNAL_DEVICE_REMOVED], 0, dev); g_debug ("Emitting ::device-removed(%s)", fwupd_device_get_id (dev)); return; } if (g_strcmp0 (signal_name, "DeviceChanged") == 0) { dev = fwupd_device_from_variant (parameters); - g_signal_emit (client, signals[SIGNAL_DEVICE_CHANGED], 0, dev); + g_signal_emit (self, signals[SIGNAL_DEVICE_CHANGED], 0, dev); g_debug ("Emitting ::device-changed(%s)", fwupd_device_get_id (dev)); return; @@ -262,7 +247,7 @@ fwupd_client_signal_cb (GDBusProxy *proxy, /** * fwupd_client_ensure_networking: - * @client: A #FwupdClient + * @self: A #FwupdClient * @error: the #GError, or %NULL * * Sets up the client networking support ready for use. Most other download and @@ -274,9 +259,9 @@ fwupd_client_signal_cb (GDBusProxy *proxy, * Since: 1.4.5 **/ gboolean -fwupd_client_ensure_networking (FwupdClient *client, GError **error) +fwupd_client_ensure_networking (FwupdClient *self, GError **error) { - FwupdClientPrivate *priv = GET_PRIVATE (client); + FwupdClientPrivate *priv = GET_PRIVATE (self); const gchar *http_proxy; g_autoptr(SoupSession) session = NULL; @@ -342,58 +327,30 @@ fwupd_client_ensure_networking (FwupdClient *client, GError **error) return TRUE; } -/** - * fwupd_client_connect: - * @client: A #FwupdClient - * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL - * - * Sets up the client ready for use. Most other methods call this - * for you, and do you only need to call this if you are just watching - * the client. - * - * Returns: %TRUE for success - * - * Since: 0.7.1 - **/ -gboolean -fwupd_client_connect (FwupdClient *client, GCancellable *cancellable, GError **error) +static void +fwupd_client_connect_get_proxy_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); + g_autoptr(GTask) task = G_TASK (user_data); + FwupdClient *self = g_task_get_source_object (task); + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GError) error = NULL; g_autoptr(GVariant) val = NULL; g_autoptr(GVariant) val2 = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* nothing to do */ - if (priv->proxy != NULL) - return TRUE; - - /* connect to the daemon */ - priv->conn = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); - if (priv->conn == NULL) { - g_prefix_error (error, "Failed to connect to system D-Bus: "); - return FALSE; + priv->proxy = g_dbus_proxy_new_finish (res, &error); + if (priv->proxy == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; } - priv->proxy = g_dbus_proxy_new_sync (priv->conn, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - FWUPD_DBUS_SERVICE, - FWUPD_DBUS_PATH, - FWUPD_DBUS_INTERFACE, - NULL, - error); - if (priv->proxy == NULL) - return FALSE; g_signal_connect (priv->proxy, "g-properties-changed", - G_CALLBACK (fwupd_client_properties_changed_cb), client); + G_CALLBACK (fwupd_client_properties_changed_cb), self); g_signal_connect (priv->proxy, "g-signal", - G_CALLBACK (fwupd_client_signal_cb), client); + G_CALLBACK (fwupd_client_signal_cb), self); val = g_dbus_proxy_get_cached_property (priv->proxy, "DaemonVersion"); if (val != NULL) - fwupd_client_set_daemon_version (client, g_variant_get_string (val, NULL)); + fwupd_client_set_daemon_version (self, g_variant_get_string (val, NULL)); val2 = g_dbus_proxy_get_cached_property (priv->proxy, "Tainted"); if (val2 != NULL) priv->tainted = g_variant_get_boolean (val2); @@ -402,12 +359,101 @@ fwupd_client_connect (FwupdClient *client, GCancellable *cancellable, GError **e priv->interactive = g_variant_get_boolean (val2); val = g_dbus_proxy_get_cached_property (priv->proxy, "HostProduct"); if (val != NULL) - fwupd_client_set_host_product (client, g_variant_get_string (val, NULL)); + fwupd_client_set_host_product (self, g_variant_get_string (val, NULL)); val = g_dbus_proxy_get_cached_property (priv->proxy, "HostMachineId"); if (val != NULL) - fwupd_client_set_host_machine_id (client, g_variant_get_string (val, NULL)); + fwupd_client_set_host_machine_id (self, g_variant_get_string (val, NULL)); + val = g_dbus_proxy_get_cached_property (priv->proxy, "HostSecurityId"); + if (val != NULL) + fwupd_client_set_host_security_id (self, g_variant_get_string (val, NULL)); - return TRUE; + /* success */ + g_task_return_boolean (task, TRUE); +} + +static void +fwupd_client_connect_get_bus_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + FwupdClient *self = g_task_get_source_object (task); + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GError) error = NULL; + + priv->conn = g_bus_get_finish (res, &error); + if (priv->conn == NULL) { + g_prefix_error (&error, "Failed to connect to system D-Bus: "); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + g_dbus_proxy_new (priv->conn, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + FWUPD_DBUS_SERVICE, + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + g_task_get_cancellable (task), + fwupd_client_connect_get_proxy_cb, + g_object_ref (task)); +} + +/** + * fwupd_client_connect_async: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Sets up the client ready for use. This is probably the first method you call + * when wanting to use libfwupd in an asynchronous manner. + * + * Other methods such as fwupd_client_get_devices_async() should only be called + * after fwupd_client_connect_finish() has been called without an error. + * + * Since: 1.5.0 + **/ +void +fwupd_client_connect_async (FwupdClient *self, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + /* nothing to do */ + if (priv->proxy != NULL) { + g_task_return_boolean (task, TRUE); + return; + } + + g_bus_get (G_BUS_TYPE_SYSTEM, cancellable, + fwupd_client_connect_get_bus_cb, + g_steal_pointer (&task)); + +} + +/** + * fwupd_client_connect_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_connect_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_connect_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); } static void @@ -443,166 +489,528 @@ fwupd_client_fixup_dbus_error (GError *error) g_dbus_error_strip_remote_error (error); } +static void +fwupd_client_get_host_security_attrs_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + fwupd_security_attr_array_from_variant (val), + (GDestroyNotify) g_ptr_array_unref); +} + /** - * fwupd_client_get_devices: - * @client: A #FwupdClient + * fwupd_client_get_host_security_attrs_async: + * @self: A #FwupdClient * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the host security attributes from the daemon. + * + * You must have called fwupd_client_connect_async() on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_host_security_attrs_async (FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetHostSecurityAttrs", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_get_host_security_attrs_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_get_host_security_attrs_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * + * Gets the result of fwupd_client_get_host_security_attrs_async(). + * + * Returns: (element-type FwupdSecurityAttr) (transfer container): attributes + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_host_security_attrs_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static GHashTable * +fwupd_report_metadata_hash_from_variant (GVariant *value) +{ + GHashTable *hash; + gsize sz; + g_autoptr(GVariant) untuple = NULL; + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + untuple = g_variant_get_child_value (value, 0); + sz = g_variant_n_children (untuple); + for (guint i = 0; i < sz; i++) { + g_autoptr(GVariant) data = NULL; + const gchar *key = NULL; + const gchar *val = NULL; + data = g_variant_get_child_value (untuple, i); + g_variant_get (data, "{&s&s}", &key, &val); + g_hash_table_insert (hash, g_strdup (key), g_strdup (val)); + } + return hash; +} + +static void +fwupd_client_get_report_metadata_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + fwupd_report_metadata_hash_from_variant (val), + (GDestroyNotify) g_hash_table_unref); +} + +/** + * fwupd_client_get_report_metadata_async: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the report metadata from the daemon. + * + * You must have called fwupd_client_connect_async() on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_report_metadata_async (FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetReportMetadata", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_get_report_metadata_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_get_report_metadata_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_get_report_metadata_async(). + * + * Returns: (transfer container): attributes + * + * Since: 1.5.0 + **/ +GHashTable * +fwupd_client_get_report_metadata_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_get_devices_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + fwupd_device_array_from_variant (val), + (GDestroyNotify) g_ptr_array_unref); +} + +/** + * fwupd_client_get_devices_async: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * * Gets all the devices registered with the daemon. * - * Returns: (element-type FwupdDevice) (transfer container): results + * You must have called fwupd_client_connect_async() on @self before using + * this method. * - * Since: 0.9.2 + * Since: 1.5.0 **/ -GPtrArray * -fwupd_client_get_devices (FwupdClient *client, GCancellable *cancellable, GError **error) +void +fwupd_client_get_devices_async (FwupdClient *self, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return NULL; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "GetDevices", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return NULL; - } - return fwupd_device_array_from_variant (val); + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetDevices", + NULL, G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_get_devices_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_get_history: - * @client: A #FwupdClient - * @cancellable: the #GCancellable, or %NULL + * fwupd_client_get_devices_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * + * Gets the result of fwupd_client_get_devices_async(). + * + * Returns: (element-type FwupdDevice) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_devices_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_get_plugins_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + fwupd_plugin_array_from_variant (val), + (GDestroyNotify) g_ptr_array_unref); +} + +/** + * fwupd_client_get_plugins_async: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the plugins being used by the daemon. + * + * You must have called fwupd_client_connect_async() on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_plugins_async (FwupdClient *self, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetPlugins", + NULL, G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_get_plugins_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_get_plugins_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_get_plugins_async(). + * + * Returns: (element-type FwupdDevice) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_plugins_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_get_history_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + fwupd_device_array_from_variant (val), + (GDestroyNotify) g_ptr_array_unref); +} + +/** + * fwupd_client_get_history_async: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * * Gets all the history. * - * Returns: (element-type FwupdDevice) (transfer container): results + * You must have called fwupd_client_connect_async() on @self before using + * this method. * - * Since: 1.0.4 + * Since: 1.5.0 **/ -GPtrArray * -fwupd_client_get_history (FwupdClient *client, GCancellable *cancellable, GError **error) +void +fwupd_client_get_history_async (FwupdClient *self, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return NULL; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "GetHistory", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return NULL; - } - return fwupd_device_array_from_variant (val); + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetHistory", + NULL, G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_get_history_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_get_device_by_id: - * @client: A #FwupdClient - * @device_id: the device ID, e.g. `usb:00:01:03:03` - * @cancellable: the #GCancellable, or %NULL + * fwupd_client_get_history_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * - * Gets a device by it's device ID. + * Gets the result of fwupd_client_get_history_async(). * - * Returns: (transfer full): a #FwupdDevice or %NULL + * Returns: (element-type FwupdDevice) (transfer container): results * - * Since: 0.9.3 + * Since: 1.5.0 **/ -FwupdDevice * -fwupd_client_get_device_by_id (FwupdClient *client, - const gchar *device_id, - GCancellable *cancellable, - GError **error) +GPtrArray * +fwupd_client_get_history_finish (FwupdClient *self, GAsyncResult *res, GError **error) { - g_autoptr(GPtrArray) devices = NULL; - - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (device_id != NULL, NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} - /* get all the devices */ - devices = fwupd_client_get_devices (client, cancellable, error); - if (devices == NULL) - return NULL; +static void +fwupd_client_get_device_by_id_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(FwupdDevice) device = NULL; + const gchar *device_id = g_task_get_task_data (task); + + devices = fwupd_client_get_devices_finish (FWUPD_CLIENT (source), res, &error); + if (devices == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } /* find the device by ID (client side) */ for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); - if (g_strcmp0 (fwupd_device_get_id (dev), device_id) == 0) - return g_object_ref (dev); + if (g_strcmp0 (fwupd_device_get_id (dev), device_id) == 0) { + g_task_return_pointer (task, + g_object_ref (dev), + (GDestroyNotify) g_object_unref); + return; + } } - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "failed to find %s", device_id); - return NULL; + + /* failed */ + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "failed to find %s", device_id); } /** - * fwupd_client_get_devices_by_guid: - * @client: A #FwupdClient - * @guid: the GUID, e.g. `e22c4520-43dc-5bb3-8245-5787fead9b63` + * fwupd_client_get_device_by_id_async: + * @self: A #FwupdClient + * @device_id: the device ID * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets a device by it's device ID. + * + * You must have called fwupd_client_connect_async() on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_device_by_id_async (FwupdClient *self, const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new (self, cancellable, callback, callback_data); + g_task_set_task_data (task, g_strdup (device_id), g_free); + fwupd_client_get_devices_async (self, cancellable, + fwupd_client_get_device_by_id_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_get_device_by_id_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * - * Gets any devices that provide a specific GUID. An error is returned if no - * devices contains this GUID. + * Gets the result of fwupd_client_get_device_by_id_async(). * - * Returns: (element-type FwupdDevice) (transfer container): devices or %NULL + * Returns: (transfer full): a #FwupdDevice, or %NULL for failure * - * Since: 1.4.1 + * Since: 1.5.0 **/ -GPtrArray * -fwupd_client_get_devices_by_guid (FwupdClient *client, - const gchar *guid, - GCancellable *cancellable, - GError **error) +FwupdDevice * +fwupd_client_get_device_by_id_finish (FwupdClient *self, GAsyncResult *res, GError **error) { + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_get_devices_by_guid_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GPtrArray) devices_tmp = NULL; - - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (guid != NULL, NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); + const gchar *guid = g_task_get_task_data (task); /* get all the devices */ - devices_tmp = fwupd_client_get_devices (client, cancellable, error); - if (devices_tmp == NULL) - return NULL; + devices_tmp = fwupd_client_get_devices_finish (FWUPD_CLIENT (source), res, &error); + if (devices_tmp == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } /* find the devices by GUID (client side) */ devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); @@ -614,541 +1022,920 @@ fwupd_client_get_devices_by_guid (FwupdClient *client, /* nothing */ if (devices->len == 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "failed to find any device providing %s", guid); - return NULL; + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "failed to find any device providing %s", guid); + return; } /* success */ - return g_steal_pointer (&devices); + g_task_return_pointer (task, + g_steal_pointer (&devices), + (GDestroyNotify) g_ptr_array_unref); } /** - * fwupd_client_get_releases: - * @client: A #FwupdClient - * @device_id: the device ID + * fwupd_client_get_devices_by_guid_async: + * @self: A #FwupdClient + * @guid: the GUID, e.g. `e22c4520-43dc-5bb3-8245-5787fead9b63` * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * - * Gets all the releases for a specific device + * Gets any devices that provide a specific GUID. An error is returned if no + * devices contains this GUID. * - * Returns: (element-type FwupdRelease) (transfer container): results + * You must have called fwupd_client_connect_async() on @self before using + * this method. * - * Since: 0.9.3 + * Since: 1.5.0 **/ -GPtrArray * -fwupd_client_get_releases (FwupdClient *client, const gchar *device_id, - GCancellable *cancellable, GError **error) +void +fwupd_client_get_devices_by_guid_async (FwupdClient *self, const gchar *guid, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (device_id != NULL, NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return NULL; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (guid != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "GetReleases", - g_variant_new ("(s)", device_id), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return NULL; - } - return fwupd_release_array_from_variant (val); + task = g_task_new (self, cancellable, callback, callback_data); + g_task_set_task_data (task, g_strdup (guid), g_free); + fwupd_client_get_devices_async (self, cancellable, + fwupd_client_get_devices_by_guid_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_get_downgrades: - * @client: A #FwupdClient - * @device_id: the device ID - * @cancellable: the #GCancellable, or %NULL + * fwupd_client_get_devices_by_guid_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * - * Gets all the downgrades for a specific device. + * Gets the result of fwupd_client_get_devices_by_guid_async(). * * Returns: (element-type FwupdRelease) (transfer container): results * - * Since: 0.9.8 + * Since: 1.5.0 **/ GPtrArray * -fwupd_client_get_downgrades (FwupdClient *client, const gchar *device_id, - GCancellable *cancellable, GError **error) +fwupd_client_get_devices_by_guid_finish (FwupdClient *self, GAsyncResult *res, GError **error) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; - - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (device_id != NULL, NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return NULL; - - /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "GetDowngrades", - g_variant_new ("(s)", device_id), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return NULL; - } - return fwupd_release_array_from_variant (val); -} - -/** - * fwupd_client_get_upgrades: - * @client: A #FwupdClient - * @device_id: the device ID - * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL - * - * Gets all the upgrades for a specific device. - * - * Returns: (element-type FwupdRelease) (transfer container): results - * - * Since: 0.9.8 - **/ -GPtrArray * -fwupd_client_get_upgrades (FwupdClient *client, const gchar *device_id, - GCancellable *cancellable, GError **error) -{ - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; - - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (device_id != NULL, NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return NULL; - - /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "GetUpgrades", - g_variant_new ("(s)", device_id), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return NULL; - } - return fwupd_release_array_from_variant (val); + return g_task_propagate_pointer (G_TASK(res), error); } static void -fwupd_client_proxy_call_cb (GObject *source, GAsyncResult *res, gpointer user_data) +fwupd_client_get_releases_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) { - FwupdClientHelper *helper = (FwupdClientHelper *) user_data; - helper->val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), - res, &helper->error); - if (helper->val != NULL) - helper->ret = TRUE; - if (helper->error != NULL) - fwupd_client_fixup_dbus_error (helper->error); - g_main_loop_quit (helper->loop); + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + fwupd_release_array_from_variant (val), + (GDestroyNotify) g_ptr_array_unref); } /** - * fwupd_client_modify_config - * @client: A #FwupdClient - * @key: key, e.g. `BlacklistPlugins` + * fwupd_client_get_releases_async: + * @self: A #FwupdClient + * @device_id: the device ID + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the releases for a specific device + * + * You must have called fwupd_client_connect_async() on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_releases_async (FwupdClient *self, const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetReleases", + g_variant_new ("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_get_releases_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_get_releases_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_get_releases_async(). + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_releases_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_get_downgrades_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + fwupd_release_array_from_variant (val), + (GDestroyNotify) g_ptr_array_unref); +} + +/** + * fwupd_client_get_downgrades_async: + * @self: A #FwupdClient + * @device_id: the device ID + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the downgrades for a specific device. + * + * You must have called fwupd_client_connect_async() on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_downgrades_async (FwupdClient *self, const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetDowngrades", + g_variant_new ("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_get_downgrades_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_get_downgrades_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_get_downgrades_async(). + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_downgrades_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_get_upgrades_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + fwupd_release_array_from_variant (val), + (GDestroyNotify) g_ptr_array_unref); +} + +/** + * fwupd_client_get_upgrades_async: + * @self: A #FwupdClient + * @device_id: the device ID + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the upgrades for a specific device. + * + * You must have called fwupd_client_connect_async() on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_upgrades_async (FwupdClient *self, const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetUpgrades", + g_variant_new ("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_get_upgrades_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_get_upgrades_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_get_upgrades_async(). + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_upgrades_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_modify_config_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +/** + * fwupd_client_modify_config_async: + * @self: A #FwupdClient + * @key: key, e.g. `DisabledPlugins` * @value: value, e.g. `*` * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Modifies a daemon config option. * The daemon will only respond to this request with proper permissions * - * Returns: %TRUE for success - * - * Since: 1.2.8 + * Since: 1.5.0 **/ -gboolean -fwupd_client_modify_config (FwupdClient *client, const gchar *key, const gchar *value, - GCancellable *cancellable, GError **error) +void +fwupd_client_modify_config_async (FwupdClient *self, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(FwupdClientHelper) helper = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (key != NULL); + g_return_if_fail (value != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - helper = fwupd_client_helper_new (); - g_dbus_proxy_call (priv->proxy, - "ModifyConfig", + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "ModifyConfig", g_variant_new ("(ss)", key, value), - G_DBUS_CALL_FLAGS_NONE, - -1, + G_DBUS_CALL_FLAGS_NONE, -1, cancellable, - fwupd_client_proxy_call_cb, - helper); - g_main_loop_run (helper->loop); - if (!helper->ret) { - g_propagate_error (error, helper->error); - helper->error = NULL; - return FALSE; - } - return TRUE; + fwupd_client_modify_config_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_activate: - * @client: A #FwupdClient - * @cancellable: the #GCancellable, or %NULL - * @device_id: a device + * fwupd_client_modify_config_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * + * Gets the result of fwupd_client_modify_config_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_modify_config_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +static void +fwupd_client_activate_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +/** + * fwupd_client_activate_async: + * @self: A #FwupdClient + * @device_id: a device + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * * Activates up a device, which normally means the device switches to a new * firmware version. This should only be called when data loss cannot occur. * - * Returns: %TRUE for success - * - * Since: 1.2.6 + * Since: 1.5.0 **/ -gboolean -fwupd_client_activate (FwupdClient *client, GCancellable *cancellable, - const gchar *device_id, GError **error) +void +fwupd_client_activate_async (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(FwupdClientHelper) helper = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (device_id != NULL, FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - helper = fwupd_client_helper_new (); - g_dbus_proxy_call (priv->proxy, - "Activate", + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "Activate", g_variant_new ("(s)", device_id), - G_DBUS_CALL_FLAGS_NONE, - -1, + G_DBUS_CALL_FLAGS_NONE, -1, cancellable, - fwupd_client_proxy_call_cb, - helper); - g_main_loop_run (helper->loop); - if (!helper->ret) { - g_propagate_error (error, helper->error); - helper->error = NULL; - return FALSE; - } - return TRUE; + fwupd_client_activate_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_verify: - * @client: A #FwupdClient + * fwupd_client_activate_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_activate_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_activate_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +static void +fwupd_client_verify_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +/** + * fwupd_client_verify_async: + * @self: A #FwupdClient * @device_id: the device ID * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Verify a specific device. * - * Returns: %TRUE for verification success - * - * Since: 0.7.0 + * Since: 1.5.0 **/ -gboolean -fwupd_client_verify (FwupdClient *client, const gchar *device_id, - GCancellable *cancellable, GError **error) +void +fwupd_client_verify_async (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(FwupdClientHelper) helper = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (device_id != NULL, FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - helper = fwupd_client_helper_new (); - g_dbus_proxy_call (priv->proxy, - "Verify", + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "Verify", g_variant_new ("(s)", device_id), - G_DBUS_CALL_FLAGS_NONE, - -1, + G_DBUS_CALL_FLAGS_NONE, -1, cancellable, - fwupd_client_proxy_call_cb, - helper); - g_main_loop_run (helper->loop); - if (!helper->ret) { - g_propagate_error (error, helper->error); - helper->error = NULL; - return FALSE; - } - return TRUE; + fwupd_client_verify_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_verify_update: - * @client: A #FwupdClient + * fwupd_client_verify_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_verify_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_verify_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +static void +fwupd_client_verify_update_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +/** + * fwupd_client_verify_update_async: + * @self: A #FwupdClient * @device_id: the device ID * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Update the verification record for a specific device. * - * Returns: %TRUE for verification success - * - * Since: 0.8.0 + * Since: 1.5.0 **/ -gboolean -fwupd_client_verify_update (FwupdClient *client, const gchar *device_id, - GCancellable *cancellable, GError **error) +void +fwupd_client_verify_update_async (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(FwupdClientHelper) helper = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (device_id != NULL, FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - helper = fwupd_client_helper_new (); - g_dbus_proxy_call (priv->proxy, - "VerifyUpdate", + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "VerifyUpdate", g_variant_new ("(s)", device_id), - G_DBUS_CALL_FLAGS_NONE, - -1, + G_DBUS_CALL_FLAGS_NONE, -1, cancellable, - fwupd_client_proxy_call_cb, - helper); - g_main_loop_run (helper->loop); - if (!helper->ret) { - g_propagate_error (error, helper->error); - helper->error = NULL; - return FALSE; - } - return TRUE; + fwupd_client_verify_update_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_unlock: - * @client: A #FwupdClient + * fwupd_client_verify_update_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_verify_update_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_verify_update_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +static void +fwupd_client_unlock_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +/** + * fwupd_client_unlock_async: + * @self: A #FwupdClient * @device_id: the device ID * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Unlocks a specific device so firmware can be read or wrote. * - * Returns: %TRUE for success - * - * Since: 0.7.0 + * Since: 1.5.0 **/ -gboolean -fwupd_client_unlock (FwupdClient *client, const gchar *device_id, - GCancellable *cancellable, GError **error) +void +fwupd_client_unlock_async (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(FwupdClientHelper) helper = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (device_id != NULL, FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - helper = fwupd_client_helper_new (); - g_dbus_proxy_call (priv->proxy, - "Unlock", + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "Unlock", g_variant_new ("(s)", device_id), - G_DBUS_CALL_FLAGS_NONE, - -1, + G_DBUS_CALL_FLAGS_NONE, -1, cancellable, - fwupd_client_proxy_call_cb, - helper); - g_main_loop_run (helper->loop); - if (!helper->ret) { - g_propagate_error (error, helper->error); - helper->error = NULL; - return FALSE; - } - return TRUE; + fwupd_client_unlock_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_clear_results: - * @client: A #FwupdClient - * @device_id: the device ID - * @cancellable: the #GCancellable, or %NULL + * fwupd_client_unlock_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * + * Gets the result of fwupd_client_unlock_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_unlock_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +static void +fwupd_client_clear_results_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +/** + * fwupd_client_clear_results_async: + * @self: A #FwupdClient + * @device_id: a device + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * * Clears the results for a specific device. * - * Returns: %TRUE for success - * - * Since: 0.7.0 + * Since: 1.5.0 **/ -gboolean -fwupd_client_clear_results (FwupdClient *client, const gchar *device_id, - GCancellable *cancellable, GError **error) +void +fwupd_client_clear_results_async (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(FwupdClientHelper) helper = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (device_id != NULL, FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - helper = fwupd_client_helper_new (); - g_dbus_proxy_call (priv->proxy, - "ClearResults", + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "ClearResults", g_variant_new ("(s)", device_id), - G_DBUS_CALL_FLAGS_NONE, - -1, + G_DBUS_CALL_FLAGS_NONE, -1, cancellable, - fwupd_client_proxy_call_cb, - helper); - g_main_loop_run (helper->loop); - if (!helper->ret) { - g_propagate_error (error, helper->error); - helper->error = NULL; - return FALSE; - } - return TRUE; + fwupd_client_clear_results_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_get_results: - * @client: A #FwupdClient + * fwupd_client_clear_results_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_clear_results_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_clear_results_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +static void +fwupd_client_get_results_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + fwupd_device_from_variant (val), + (GDestroyNotify) g_ptr_array_unref); +} + +/** + * fwupd_client_get_results_async: + * @self: A #FwupdClient * @device_id: the device ID * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Gets the results of a previous firmware update for a specific device. * - * Returns: (transfer full): a #FwupdDevice, or %NULL for failure + * You must have called fwupd_client_connect_async() on @self before using + * this method. * - * Since: 0.7.0 + * Since: 1.5.0 **/ -FwupdDevice * -fwupd_client_get_results (FwupdClient *client, const gchar *device_id, - GCancellable *cancellable, GError **error) +void +fwupd_client_get_results_async (FwupdClient *self, const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(FwupdClientHelper) helper = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (device_id != NULL, NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return NULL; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - helper = fwupd_client_helper_new (); - g_dbus_proxy_call (priv->proxy, - "GetResults", + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetResults", g_variant_new ("(s)", device_id), G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - fwupd_client_proxy_call_cb, - helper); - g_main_loop_run (helper->loop); - if (!helper->ret) { - g_propagate_error (error, helper->error); - helper->error = NULL; - return NULL; - } - return fwupd_device_from_variant (helper->val); + -1, cancellable, + fwupd_client_get_results_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_get_results_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_get_results_async(). + * + * Returns: (transfer full): a #FwupdDevice, or %NULL for failure + * + * Since: 1.5.0 + **/ +FwupdDevice * +fwupd_client_get_results_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); } #ifdef HAVE_GIO_UNIX + static void -fwupd_client_send_message_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +fwupd_client_install_stream_cb (GObject *source, GAsyncResult *res, gpointer user_data) { - FwupdClientHelper *helper = (FwupdClientHelper *) user_data; - GDBusConnection *con = G_DBUS_CONNECTION (source_object); - helper->message = g_dbus_connection_send_message_with_reply_finish (con, res, - &helper->error); - if (helper->message && - !g_dbus_message_to_gerror (helper->message, &helper->error)) { - helper->ret = TRUE; - helper->val = g_dbus_message_get_body (helper->message); - if (helper->val != NULL) - g_variant_ref (helper->val); - } - if (helper->error != NULL) - fwupd_client_fixup_dbus_error (helper->error); - g_main_loop_quit (helper->loop); -} -#endif + g_autoptr(GDBusMessage) msg = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); -#ifdef HAVE_GIO_UNIX -static gboolean -fwupd_client_install_fd (FwupdClient *client, - const gchar *device_id, - GUnixInputStream *istr, - const gchar *filename_hint, - FwupdInstallFlags install_flags, - GCancellable *cancellable, - GError **error) + msg = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source), + res, &error); + if (msg == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + if (g_dbus_message_to_gerror (msg, &error)) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +void +fwupd_client_install_stream_async (FwupdClient *self, + const gchar *device_id, + GUnixInputStream *istr, + const gchar *filename_hint, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - GVariant *body; + FwupdClientPrivate *priv = GET_PRIVATE (self); GVariantBuilder builder; - gint retval; - g_autoptr(FwupdClientHelper) helper = NULL; g_autoptr(GDBusMessage) request = NULL; g_autoptr(GUnixFDList) fd_list = NULL; + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); /* set options */ g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); @@ -1170,10 +1957,18 @@ fwupd_client_install_fd (FwupdClient *client, g_variant_builder_add (&builder, "{sv}", "allow-reinstall", g_variant_new_boolean (TRUE)); } + if (install_flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) { + g_variant_builder_add (&builder, "{sv}", + "allow-branch-switch", g_variant_new_boolean (TRUE)); + } if (install_flags & FWUPD_INSTALL_FLAG_FORCE) { g_variant_builder_add (&builder, "{sv}", "force", g_variant_new_boolean (TRUE)); } + if (install_flags & FWUPD_INSTALL_FLAG_IGNORE_POWER) { + g_variant_builder_add (&builder, "{sv}", + "ignore-power", g_variant_new_boolean (TRUE)); + } if (install_flags & FWUPD_INSTALL_FLAG_NO_HISTORY) { g_variant_builder_add (&builder, "{sv}", "no-history", g_variant_new_boolean (TRUE)); @@ -1181,8 +1976,7 @@ fwupd_client_install_fd (FwupdClient *client, /* set out of band file descriptor */ fd_list = g_unix_fd_list_new (); - retval = g_unix_fd_list_append (fd_list, g_unix_input_stream_get_fd (istr), NULL); - g_assert (retval != -1); + g_unix_fd_list_append (fd_list, g_unix_input_stream_get_fd (istr), NULL); request = g_dbus_message_new_method_call (FWUPD_DBUS_SERVICE, FWUPD_DBUS_PATH, FWUPD_DBUS_INTERFACE, @@ -1190,317 +1984,530 @@ fwupd_client_install_fd (FwupdClient *client, g_dbus_message_set_unix_fd_list (request, fd_list); /* call into daemon */ - helper = fwupd_client_helper_new (); - body = g_variant_new ("(sha{sv})", device_id, g_unix_input_stream_get_fd (istr), &builder); - g_dbus_message_set_body (request, body); + g_dbus_message_set_body (request, g_variant_new ("(sha{sv})", + device_id, + g_unix_input_stream_get_fd (istr), + &builder)); g_dbus_connection_send_message_with_reply (priv->conn, request, G_DBUS_SEND_MESSAGE_FLAGS_NONE, G_MAXINT, NULL, cancellable, - fwupd_client_send_message_cb, - helper); - g_main_loop_run (helper->loop); - if (!helper->ret) { - g_propagate_error (error, helper->error); - helper->error = NULL; - return FALSE; - } - return TRUE; + fwupd_client_install_stream_cb, + g_steal_pointer (&task)); } #endif /** - * fwupd_client_install_bytes: - * @client: A #FwupdClient + * fwupd_client_install_bytes_async: + * @self: A #FwupdClient * @device_id: the device ID * @bytes: #GBytes * @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Install firmware onto a specific device. * - * Returns: %TRUE for success - * - * Since: 1.4.5 + * Since: 1.5.0 **/ -gboolean -fwupd_client_install_bytes (FwupdClient *client, - const gchar *device_id, - GBytes *bytes, - FwupdInstallFlags install_flags, - GCancellable *cancellable, - GError **error) +void +fwupd_client_install_bytes_async (FwupdClient *self, + const gchar *device_id, + GBytes *bytes, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { #ifdef HAVE_GIO_UNIX + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GError) error = NULL; g_autoptr(GUnixInputStream) istr = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (device_id != NULL, FALSE); - g_return_val_if_fail (bytes != NULL, FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + /* move to a thread if this ever takes more than a few ms */ + istr = fwupd_unix_input_stream_from_bytes (bytes, &error); + if (istr == NULL) { + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } - istr = fwupd_unix_input_stream_from_bytes (bytes, error); - if (istr == NULL) - return FALSE; - return fwupd_client_install_fd (client, device_id, istr, NULL, - install_flags, cancellable, error); + /* call into daemon */ + fwupd_client_install_stream_async (self, device_id, istr, NULL, + install_flags, cancellable, + callback, callback_data); #else - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Not supported as is unavailable"); - return FALSE; + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); #endif } /** - * fwupd_client_install: - * @client: A #FwupdClient + * fwupd_client_install_bytes_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_install_bytes_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_install_bytes_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +/** + * fwupd_client_install_async: + * @self: A #FwupdClient * @device_id: the device ID * @filename: the filename to install * @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * - * Install a file onto a specific device. + * Install firmware onto a specific device. * - * Returns: %TRUE for success - * - * Since: 0.7.0 + * Since: 1.5.0 **/ -gboolean -fwupd_client_install (FwupdClient *client, - const gchar *device_id, - const gchar *filename, - FwupdInstallFlags install_flags, - GCancellable *cancellable, - GError **error) +void +fwupd_client_install_async (FwupdClient *self, + const gchar *device_id, + const gchar *filename, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { #ifdef HAVE_GIO_UNIX + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GError) error = NULL; g_autoptr(GUnixInputStream) istr = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (device_id != NULL, FALSE); - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (filename != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + /* move to a thread if this ever takes more than a few ms */ + istr = fwupd_unix_input_stream_from_fn (filename, &error); + if (istr == NULL) { + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } - istr = fwupd_unix_input_stream_from_fn (filename, error); - if (istr == NULL) - return FALSE; - return fwupd_client_install_fd (client, device_id, istr, filename, - install_flags, cancellable, error); + /* call into daemon */ + fwupd_client_install_stream_async (self, device_id, istr, NULL, + install_flags, cancellable, + callback, callback_data); #else - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Not supported as is unavailable"); - return FALSE; + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); #endif } /** - * fwupd_client_install_release: - * @client: A #FwupdClient - * @device: A #FwupdDevice - * @release: A #FwupdRelease - * @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL - * @cancellable: A #GCancellable, or %NULL - * @error: A #GError, or %NULL + * fwupd_client_install_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL * - * Installs a new release on a device, downloading the firmware if required. + * Gets the result of fwupd_client_install_async(). * * Returns: %TRUE for success * - * Since: 1.4.5 + * Since: 1.5.0 **/ gboolean -fwupd_client_install_release (FwupdClient *client, - FwupdDevice *device, - FwupdRelease *release, - FwupdInstallFlags install_flags, - GCancellable *cancellable, - GError **error) +fwupd_client_install_finish (FwupdClient *self, GAsyncResult *res, GError **error) { - GChecksumType checksum_type; - const gchar *checksum_expected; - const gchar *remote_id; - const gchar *uri_tmp; - g_autofree gchar *checksum_actual = NULL; - g_autofree gchar *uri_str = NULL; - g_autoptr(GBytes) blob = NULL; - g_autoptr(SoupURI) uri = NULL; + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} - /* work out what remote-specific URI fields this should use */ - uri_tmp = fwupd_release_get_uri (release); - uri = soup_uri_new (uri_tmp); - remote_id = fwupd_release_get_remote_id (release); - if (remote_id != NULL) { - g_autoptr(FwupdRemote) remote = NULL; - g_autofree gchar *fn = NULL; +typedef struct { + FwupdDevice *device; + FwupdRelease *release; + FwupdInstallFlags install_flags; +} FwupdClientInstallReleaseData; - /* if a remote-id was specified, the remote has to exist */ - remote = fwupd_client_get_remote_by_id (client, remote_id, cancellable, error); - if (remote == NULL) - return FALSE; +static void +fwupd_client_install_release_data_free (FwupdClientInstallReleaseData *data) +{ + g_object_unref (data->device); + g_object_unref (data->release); + g_free (data); +} - /* local and directory remotes may have the firmware already */ - if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL && uri == NULL) { - const gchar *fn_cache = fwupd_remote_get_filename_cache (remote); - g_autofree gchar *path = g_path_get_dirname (fn_cache); +static void +fwupd_client_install_release_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); - fn = g_build_filename (path, uri_tmp, NULL); - } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) { - fn = g_strdup (uri_tmp + 7); - } - - /* install with flags chosen by the user */ - if (fn != NULL) { - return fwupd_client_install (client, fwupd_device_get_id (device), - fn, install_flags, cancellable, error); - } - - /* remote file */ - uri_str = fwupd_remote_build_firmware_uri (remote, uri_tmp, error); - if (uri_str == NULL) - return FALSE; - } else { - uri_str = g_strdup (uri_tmp); + if (!fwupd_client_install_release_finish (FWUPD_CLIENT (source), res, &error)) { + g_task_return_error (task, g_steal_pointer (&error)); + return; } - /* download file */ - blob = fwupd_client_download_bytes (client, uri_str, - FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, - cancellable, error); - if (blob == NULL) - return FALSE; + /* success */ + g_task_return_boolean (task, TRUE); +} + +static void +fwupd_client_install_release_bytes_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + + if (!fwupd_client_install_bytes_finish (FWUPD_CLIENT (source), res, &error)) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +static void +fwupd_client_install_release_download_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + FwupdClientInstallReleaseData *data = g_task_get_task_data (task); + GChecksumType checksum_type; + GCancellable *cancellable = g_task_get_cancellable (task); + const gchar *checksum_expected; + g_autofree gchar *checksum_actual = NULL; + + blob = fwupd_client_download_bytes_finish (FWUPD_CLIENT (source), res, &error); + if (blob == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } /* verify checksum */ - checksum_expected = fwupd_checksum_get_best (fwupd_release_get_checksums (release)); + checksum_expected = fwupd_checksum_get_best (fwupd_release_get_checksums (data->release)); checksum_type = fwupd_checksum_guess_kind (checksum_expected); checksum_actual = g_compute_checksum_for_bytes (checksum_type, blob); if (g_strcmp0 (checksum_expected, checksum_actual) != 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Checksum invalid, expected %s got %s", - checksum_expected, checksum_actual); - return FALSE; + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum invalid, expected %s got %s", + checksum_expected, checksum_actual); + return; } /* if the device specifies ONLY_OFFLINE automatically set this flag */ - if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_ONLY_OFFLINE)) - install_flags |= FWUPD_INSTALL_FLAG_OFFLINE; - return fwupd_client_install_bytes (client, - fwupd_device_get_id (device), blob, - install_flags, NULL, error); + if (fwupd_device_has_flag (data->device, FWUPD_DEVICE_FLAG_ONLY_OFFLINE)) + data->install_flags |= FWUPD_INSTALL_FLAG_OFFLINE; + fwupd_client_install_bytes_async (FWUPD_CLIENT (source), + fwupd_device_get_id (data->device), blob, + data->install_flags, cancellable, + fwupd_client_install_release_bytes_cb, + g_steal_pointer (&task)); +} + +static void +fwupd_client_install_release_remote_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autofree gchar *fn = NULL; + g_autofree gchar *uri_str = NULL; + g_autoptr(FwupdRemote) remote = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(SoupURI) uri = NULL; + FwupdClientInstallReleaseData *data = g_task_get_task_data (task); + GCancellable *cancellable = g_task_get_cancellable (task); + + /* if a remote-id was specified, the remote has to exist */ + remote = fwupd_client_get_remote_by_id_finish (FWUPD_CLIENT (source), res, &error); + if (remote == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* local and directory remotes may have the firmware already */ + uri = soup_uri_new (fwupd_release_get_uri (data->release)); + if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL && uri == NULL) { + const gchar *fn_cache = fwupd_remote_get_filename_cache (remote); + g_autofree gchar *path = g_path_get_dirname (fn_cache); + fn = g_build_filename (path, fwupd_release_get_uri (data->release), NULL); + } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) { + fn = g_strdup (fwupd_release_get_uri (data->release) + 7); + } + + /* install with flags chosen by the user */ + if (fn != NULL) { + fwupd_client_install_async (FWUPD_CLIENT (source), + fwupd_device_get_id (data->device), + fn, data->install_flags, + cancellable, + fwupd_client_install_release_cb, + g_steal_pointer (&task)); + return; + } + + /* remote file */ + uri_str = fwupd_remote_build_firmware_uri (remote, + fwupd_release_get_uri (data->release), + &error); + if (uri_str == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* download file */ + fwupd_client_download_bytes_async (FWUPD_CLIENT (source), uri_str, + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, + cancellable, + fwupd_client_install_release_download_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_get_details: - * @client: A #FwupdClient - * @filename: the firmware filename, e.g. `firmware.cab` + * fwupd_client_install_release_async: + * @self: A #FwupdClient + * @device: A #FwupdDevice + * @release: A #FwupdRelease + * @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Installs a new release on a device, downloading the firmware if required. + * + * Since: 1.5.0 + **/ +void +fwupd_client_install_release_async (FwupdClient *self, + FwupdDevice *device, + FwupdRelease *release, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = NULL; + FwupdClientInstallReleaseData *data; + const gchar *remote_id; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (FWUPD_IS_DEVICE (device)); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new (self, cancellable, callback, callback_data); + data = g_new0 (FwupdClientInstallReleaseData, 1); + data->device = g_object_ref (device); + data->release = g_object_ref (release); + data->install_flags = install_flags; + g_task_set_task_data (task, data, (GDestroyNotify) fwupd_client_install_release_data_free); + + /* work out what remote-specific URI fields this should use */ + remote_id = fwupd_release_get_remote_id (release); + if (remote_id == NULL) { + fwupd_client_download_bytes_async (self, + fwupd_release_get_uri (release), + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, + cancellable, + fwupd_client_install_release_download_cb, + g_steal_pointer (&task)); + return; + } + + /* if a remote-id was specified, the remote has to exist */ + fwupd_client_get_remote_by_id_async (self, remote_id, cancellable, + fwupd_client_install_release_remote_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_install_release_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * - * Gets details about a specific firmware file. + * Gets the result of fwupd_client_install_release_async(). * - * Returns: (transfer container) (element-type FwupdDevice): an array of results + * Returns: %TRUE for success * - * Since: 1.0.0 + * Since: 1.5.0 **/ -GPtrArray * -fwupd_client_get_details (FwupdClient *client, const gchar *filename, - GCancellable *cancellable, GError **error) +gboolean +fwupd_client_install_release_finish (FwupdClient *self, GAsyncResult *res, GError **error) { + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + #ifdef HAVE_GIO_UNIX - FwupdClientPrivate *priv = GET_PRIVATE (client); - GVariant *body; - gint fd; - gint retval; - g_autoptr(FwupdClientHelper) helper = NULL; + +static void +fwupd_client_get_details_stream_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GDBusMessage) msg = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + + msg = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source), + res, &error); + if (msg == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + if (g_dbus_message_to_gerror (msg, &error)) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + fwupd_device_array_from_variant (g_dbus_message_get_body (msg)), + (GDestroyNotify) g_ptr_array_unref); +} + +void +fwupd_client_get_details_stream_async (FwupdClient *self, + GUnixInputStream *istr, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + gint fd = g_unix_input_stream_get_fd (istr); g_autoptr(GDBusMessage) request = NULL; g_autoptr(GUnixFDList) fd_list = NULL; - - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (filename != NULL, NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return NULL; - - /* open file */ - fd = open (filename, O_RDONLY); - if (fd < 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "failed to open %s", - filename); - return NULL; - } + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); /* set out of band file descriptor */ fd_list = g_unix_fd_list_new (); - retval = g_unix_fd_list_append (fd_list, fd, NULL); - g_assert (retval != -1); + g_unix_fd_list_append (fd_list, fd, NULL); request = g_dbus_message_new_method_call (FWUPD_DBUS_SERVICE, FWUPD_DBUS_PATH, FWUPD_DBUS_INTERFACE, "GetDetails"); g_dbus_message_set_unix_fd_list (request, fd_list); - /* g_unix_fd_list_append did a dup() already */ - close (fd); - /* call into daemon */ - helper = fwupd_client_helper_new (); - body = g_variant_new ("(h)", fd); - g_dbus_message_set_body (request, body); - + g_dbus_message_set_body (request, g_variant_new ("(h)", fd)); g_dbus_connection_send_message_with_reply (priv->conn, request, G_DBUS_SEND_MESSAGE_FLAGS_NONE, - -1, + G_MAXINT, NULL, cancellable, - fwupd_client_send_message_cb, - helper); - g_main_loop_run (helper->loop); - if (!helper->ret) { - g_propagate_error (error, helper->error); - helper->error = NULL; - return NULL; + fwupd_client_get_details_stream_cb, + g_steal_pointer (&task)); +} +#endif + +/** + * fwupd_client_get_details_bytes_async: + * @self: A #FwupdClient + * @bytes: a #GBytes for the firmware, e.g. `firmware.cab` + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets details about a specific firmware file. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_details_bytes_async (FwupdClient *self, + GBytes *bytes, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ +#ifdef HAVE_GIO_UNIX + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GError) error = NULL; + g_autoptr(GUnixInputStream) istr = NULL; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); + + /* move to a thread if this ever takes more than a few ms */ + istr = fwupd_unix_input_stream_from_bytes (bytes, &error); + if (istr == NULL) { + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); + g_task_return_error (task, g_steal_pointer (&error)); + return; } - /* return results */ - return fwupd_device_array_from_variant (helper->val); + /* call into daemon */ + fwupd_client_get_details_stream_async (self, istr, cancellable, + callback, callback_data); #else - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Not supported as is unavailable"); - return NULL; + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); #endif } +/** + * fwupd_client_get_details_bytes_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_get_details_bytes_async(). + * + * Returns: (transfer container) (element-type FwupdDevice): an array of results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_details_bytes_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + /** * fwupd_client_get_percentage: - * @client: A #FwupdClient + * @self: A #FwupdClient * * Gets the last returned percentage value. * @@ -1509,16 +2516,16 @@ fwupd_client_get_details (FwupdClient *client, const gchar *filename, * Since: 0.7.3 **/ guint -fwupd_client_get_percentage (FwupdClient *client) +fwupd_client_get_percentage (FwupdClient *self) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_return_val_if_fail (FWUPD_IS_CLIENT (client), 0); + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_CLIENT (self), 0); return priv->percentage; } /** * fwupd_client_get_daemon_version: - * @client: A #FwupdClient + * @self: A #FwupdClient * * Gets the daemon version number. * @@ -1527,16 +2534,16 @@ fwupd_client_get_percentage (FwupdClient *client) * Since: 0.9.6 **/ const gchar * -fwupd_client_get_daemon_version (FwupdClient *client) +fwupd_client_get_daemon_version (FwupdClient *self) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); return priv->daemon_version; } /** * fwupd_client_get_host_product: - * @client: A #FwupdClient + * @self: A #FwupdClient * * Gets the string that represents the host running fwupd * @@ -1545,16 +2552,16 @@ fwupd_client_get_daemon_version (FwupdClient *client) * Since: 1.3.1 **/ const gchar * -fwupd_client_get_host_product (FwupdClient *client) +fwupd_client_get_host_product (FwupdClient *self) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); return priv->host_product; } /** * fwupd_client_get_host_machine_id: - * @client: A #FwupdClient + * @self: A #FwupdClient * * Gets the string that represents the host machine ID * @@ -1563,16 +2570,34 @@ fwupd_client_get_host_product (FwupdClient *client) * Since: 1.3.2 **/ const gchar * -fwupd_client_get_host_machine_id (FwupdClient *client) +fwupd_client_get_host_machine_id (FwupdClient *self) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); return priv->host_machine_id; } +/** + * fwupd_client_get_host_security_id: + * @self: A #FwupdClient + * + * Gets the string that represents the host machine ID + * + * Returns: a string, or %NULL for unknown. + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_client_get_host_security_id (FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + return priv->host_security_id; +} + /** * fwupd_client_get_status: - * @client: A #FwupdClient + * @self: A #FwupdClient * * Gets the last returned status value. * @@ -1581,16 +2606,16 @@ fwupd_client_get_host_machine_id (FwupdClient *client) * Since: 0.7.3 **/ FwupdStatus -fwupd_client_get_status (FwupdClient *client) +fwupd_client_get_status (FwupdClient *self) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FWUPD_STATUS_UNKNOWN); + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FWUPD_STATUS_UNKNOWN); return priv->status; } /** * fwupd_client_get_tainted: - * @client: A #FwupdClient + * @self: A #FwupdClient * * Gets if the daemon has been tainted by 3rd party code. * @@ -1599,17 +2624,16 @@ fwupd_client_get_status (FwupdClient *client) * Since: 1.2.4 **/ gboolean -fwupd_client_get_tainted (FwupdClient *client) +fwupd_client_get_tainted (FwupdClient *self) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); return priv->tainted; } - /** * fwupd_client_get_daemon_interactive: - * @client: A #FwupdClient + * @self: A #FwupdClient * * Gets if the daemon is running in an interactive terminal. * @@ -1618,32 +2642,57 @@ fwupd_client_get_tainted (FwupdClient *client) * Since: 1.3.4 **/ gboolean -fwupd_client_get_daemon_interactive (FwupdClient *client) +fwupd_client_get_daemon_interactive (FwupdClient *self) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); return priv->interactive; } #ifdef HAVE_GIO_UNIX -static gboolean -fwupd_client_update_metadata_fds (FwupdClient *client, - const gchar *remote_id, - GUnixInputStream *metadata, - GUnixInputStream *signature, - GCancellable *cancellable, - GError **error) + +static void +fwupd_client_update_metadata_stream_cb (GObject *source, GAsyncResult *res, gpointer user_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - GVariant *body; - g_autoptr(FwupdClientHelper) helper = NULL; + g_autoptr(GDBusMessage) msg = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + + msg = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source), + res, &error); + if (msg == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + if (g_dbus_message_to_gerror (msg, &error)) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +void +fwupd_client_update_metadata_stream_async (FwupdClient *self, + const gchar *remote_id, + GUnixInputStream *istr, + GUnixInputStream *istr_sig, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); g_autoptr(GDBusMessage) request = NULL; g_autoptr(GUnixFDList) fd_list = NULL; + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); /* set out of band file descriptor */ fd_list = g_unix_fd_list_new (); - g_unix_fd_list_append (fd_list, g_unix_input_stream_get_fd (metadata), NULL); - g_unix_fd_list_append (fd_list, g_unix_input_stream_get_fd (signature), NULL); + g_unix_fd_list_append (fd_list, g_unix_input_stream_get_fd (istr), NULL); + g_unix_fd_list_append (fd_list, g_unix_input_stream_get_fd (istr_sig), NULL); request = g_dbus_message_new_method_call (FWUPD_DBUS_SERVICE, FWUPD_DBUS_PATH, FWUPD_DBUS_INTERFACE, @@ -1651,101 +2700,30 @@ fwupd_client_update_metadata_fds (FwupdClient *client, g_dbus_message_set_unix_fd_list (request, fd_list); /* call into daemon */ - body = g_variant_new ("(shh)", - remote_id, - g_unix_input_stream_get_fd (metadata), - g_unix_input_stream_get_fd (signature)); - g_dbus_message_set_body (request, body); - helper = fwupd_client_helper_new (); + g_dbus_message_set_body (request, g_variant_new ("(shh)", + remote_id, + g_unix_input_stream_get_fd (istr), + g_unix_input_stream_get_fd (istr_sig))); g_dbus_connection_send_message_with_reply (priv->conn, request, G_DBUS_SEND_MESSAGE_FLAGS_NONE, - -1, + G_MAXINT, NULL, cancellable, - fwupd_client_send_message_cb, - helper); - g_main_loop_run (helper->loop); - if (!helper->ret) { - g_propagate_error (error, helper->error); - helper->error = NULL; - return FALSE; - } - return TRUE; + fwupd_client_update_metadata_stream_cb, + g_steal_pointer (&task)); } - #endif /** - * fwupd_client_update_metadata: - * @client: A #FwupdClient - * @remote_id: the remote ID, e.g. `lvfs-testing` - * @metadata_fn: the XML metadata filename - * @signature_fn: the GPG signature file - * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL - * - * Updates the metadata. This allows a session process to download the metadata - * and metadata signing file to be passed into the daemon to be checked and - * parsed. - * - * The @remote_id allows the firmware to be tagged so that the remote can be - * matched when the firmware is downloaded. - * - * Returns: %TRUE for success - * - * Since: 1.0.0 - **/ -gboolean -fwupd_client_update_metadata (FwupdClient *client, - const gchar *remote_id, - const gchar *metadata_fn, - const gchar *signature_fn, - GCancellable *cancellable, - GError **error) -{ -#ifdef HAVE_GIO_UNIX - GUnixInputStream *istr; - GUnixInputStream *istr_sig; - - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (remote_id != NULL, FALSE); - g_return_val_if_fail (metadata_fn != NULL, FALSE); - g_return_val_if_fail (signature_fn != NULL, FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; - - /* open files */ - istr = fwupd_unix_input_stream_from_fn (metadata_fn, error); - if (istr == NULL) - return FALSE; - istr_sig = fwupd_unix_input_stream_from_fn (signature_fn, error); - if (istr_sig == NULL) - return FALSE; - return fwupd_client_update_metadata_fds (client, remote_id, - istr, istr_sig, - cancellable, error); -#else - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Not supported as is unavailable"); - return FALSE; -#endif -} - -/** - * fwupd_client_update_metadata_bytes: - * @client: A #FwupdClient + * fwupd_client_update_metadata_bytes_async: + * @self: A #FwupdClient * @remote_id: remote ID, e.g. `lvfs-testing` * @metadata: XML metadata data * @signature: signature data * @cancellable: #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Updates the metadata. This allows a session process to download the metadata * and metadata signing file to be passed into the daemon to be checked and @@ -1754,411 +2732,788 @@ fwupd_client_update_metadata (FwupdClient *client, * The @remote_id allows the firmware to be tagged so that the remote can be * matched when the firmware is downloaded. * - * Returns: %TRUE for success - * - * Since: 1.4.5 + * Since: 1.5.0 **/ -gboolean -fwupd_client_update_metadata_bytes (FwupdClient *client, - const gchar *remote_id, - GBytes *metadata, - GBytes *signature, - GCancellable *cancellable, - GError **error) +void +fwupd_client_update_metadata_bytes_async (FwupdClient *self, + const gchar *remote_id, + GBytes *metadata, + GBytes *signature, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { #ifdef HAVE_GIO_UNIX - GUnixInputStream *istr; - GUnixInputStream *istr_sig; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GError) error = NULL; + g_autoptr(GUnixInputStream) istr = NULL; + g_autoptr(GUnixInputStream) istr_sig = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (remote_id != NULL, FALSE); - g_return_val_if_fail (metadata != NULL, FALSE); - g_return_val_if_fail (signature != NULL, FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (remote_id != NULL); + g_return_if_fail (metadata != NULL); + g_return_if_fail (signature != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); - /* convert bytes to a readable fd */ - istr = fwupd_unix_input_stream_from_bytes (metadata, error); - if (istr == NULL) - return FALSE; - istr_sig = fwupd_unix_input_stream_from_bytes (signature, error); - if (istr_sig == NULL) - return FALSE; - return fwupd_client_update_metadata_fds (client, remote_id, - istr, istr_sig, - cancellable, error); + /* move to a thread if this ever takes more than a few ms */ + istr = fwupd_unix_input_stream_from_bytes (metadata, &error); + if (istr == NULL) { + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + istr_sig = fwupd_unix_input_stream_from_bytes (signature, &error); + if (istr_sig == NULL) { + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* call into daemon */ + fwupd_client_update_metadata_stream_async (self, remote_id, istr, istr_sig, + cancellable, + callback, callback_data); #else - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Not supported as is unavailable"); - return FALSE; + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data); + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); #endif } /** - * fwupd_client_refresh_remote: - * @client: A #FwupdClient + * fwupd_client_update_metadata_bytes_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_update_metadata_bytes_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_update_metadata_bytes_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +typedef struct { + FwupdRemote *remote; + GBytes *signature; + GBytes *metadata; +} FwupdClientRefreshRemoteData; + +static void +fwupd_client_refresh_remote_data_free (FwupdClientRefreshRemoteData *data) +{ + if (data->signature != NULL) + g_bytes_unref (data->signature); + if (data->metadata != NULL) + g_bytes_unref (data->metadata); + g_object_unref (data->remote); + g_free (data); +} + +static void +fwupd_client_refresh_remote_update_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + + /* save metadata */ + if (!fwupd_client_update_metadata_bytes_finish (FWUPD_CLIENT (source), res, &error)) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +static void +fwupd_client_refresh_remote_metadata_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + FwupdClientRefreshRemoteData *data = g_task_get_task_data (task); + FwupdClient *self = g_task_get_source_object (task); + GCancellable *cancellable = g_task_get_cancellable (task); + + /* save metadata */ + bytes = fwupd_client_download_bytes_finish (FWUPD_CLIENT (source), res, &error); + if (bytes == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + data->metadata = g_steal_pointer (&bytes); + + /* send all this to fwupd */ + fwupd_client_update_metadata_bytes_async (self, + fwupd_remote_get_id (data->remote), + data->metadata, + data->signature, + cancellable, + fwupd_client_refresh_remote_update_cb, + g_steal_pointer (&task)); +} + +static void +fwupd_client_refresh_remote_signature_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + FwupdClientRefreshRemoteData *data = g_task_get_task_data (task); + FwupdClient *self = g_task_get_source_object (task); + GCancellable *cancellable = g_task_get_cancellable (task); + + /* save signature */ + bytes = fwupd_client_download_bytes_finish (FWUPD_CLIENT (source), res, &error); + if (bytes == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + data->signature = g_steal_pointer (&bytes); + if (!fwupd_remote_load_signature_bytes (data->remote, data->signature, &error)) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* download metadata */ + fwupd_client_download_bytes_async (self, + fwupd_remote_get_metadata_uri (data->remote), + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, + cancellable, + fwupd_client_refresh_remote_metadata_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_refresh_remote_async: + * @self: A #FwupdClient * @remote: A #FwupdRemote - * @cancellable: A #GCancellable, or %NULL - * @error: A #GError, or %NULL + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Refreshes a remote by downloading new metadata. * - * Returns: %TRUE for success - * - * Since: 1.4.5 + * Since: 1.5.0 **/ -gboolean -fwupd_client_refresh_remote (FwupdClient *client, - FwupdRemote *remote, - GCancellable *cancellable, - GError **error) +void +fwupd_client_refresh_remote_async (FwupdClient *self, + FwupdRemote *remote, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - g_autoptr(GBytes) metadata = NULL; - g_autoptr(GBytes) signature = NULL; + FwupdClientRefreshRemoteData *data; + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (FWUPD_IS_REMOTE (remote), FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (FWUPD_IS_REMOTE (remote)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); - /* download the signature */ - signature = fwupd_client_download_bytes (client, - fwupd_remote_get_metadata_uri_sig (remote), - FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, - cancellable, error); - if (signature == NULL) - return FALSE; + task = g_task_new (self, cancellable, callback, callback_data); + data = g_new0 (FwupdClientRefreshRemoteData, 1); + data->remote = g_object_ref (remote); + g_task_set_task_data (task, + g_steal_pointer (&data), + (GDestroyNotify) fwupd_client_refresh_remote_data_free); - /* find the download URI of the metadata from the JCat file */ - if (!fwupd_remote_load_signature_bytes (remote, signature, error)) - return FALSE; + /* download signature */ + fwupd_client_download_bytes_async (self, + fwupd_remote_get_metadata_uri_sig (remote), + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, + cancellable, + fwupd_client_refresh_remote_signature_cb, + g_steal_pointer (&task)); - /* download the metadata */ - metadata = fwupd_client_download_bytes (client, - fwupd_remote_get_metadata_uri (remote), - FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, - cancellable, error); - if (metadata == NULL) - return FALSE; - - /* send all this to fwupd */ - return fwupd_client_update_metadata_bytes (client, - fwupd_remote_get_id (remote), - metadata, signature, - cancellable, error); } /** - * fwupd_client_get_remotes: - * @client: A #FwupdClient - * @cancellable: the #GCancellable, or %NULL + * fwupd_client_refresh_remote_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * + * Gets the result of fwupd_client_refresh_remote_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_refresh_remote_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +static void +fwupd_client_get_remotes_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + fwupd_remote_array_from_variant (val), + (GDestroyNotify) g_ptr_array_unref); +} + +/** + * fwupd_client_get_remotes_async: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * * Gets the list of remotes that have been configured for the system. * - * Returns: (element-type FwupdRemote) (transfer container): list of remotes, or %NULL + * You must have called fwupd_client_connect_async() on @self before using + * this method. * - * Since: 0.9.3 + * Since: 1.5.0 **/ -GPtrArray * -fwupd_client_get_remotes (FwupdClient *client, GCancellable *cancellable, GError **error) +void +fwupd_client_get_remotes_async (FwupdClient *self, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return NULL; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "GetRemotes", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return NULL; - } - return fwupd_remote_array_from_variant (val); + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetRemotes", + NULL, G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_get_remotes_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_get_approved_firmware: - * @client: A #FwupdClient - * @cancellable: the #GCancellable, or %NULL + * fwupd_client_get_remotes_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * + * Gets the result of fwupd_client_get_remotes_async(). + * + * Returns: (element-type FwupdRemote) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_remotes_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_get_approved_firmware_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_auto(GStrv) strv = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free); + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + g_variant_get (val, "(^as)", &strv); + for (guint i = 0; strv[i] != NULL; i++) + g_ptr_array_add (array, g_strdup (strv[i])); + + /* success */ + g_task_return_pointer (task, + g_steal_pointer (&array), + (GDestroyNotify) g_ptr_array_unref); +} + +/** + * fwupd_client_get_approved_firmware_async: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * * Gets the list of approved firmware. * - * Returns: (transfer full): list of remotes, or %NULL + * You must have called fwupd_client_connect_async() on @self before using + * this method. * - * Since: 1.2.6 + * Since: 1.5.0 **/ -gchar ** -fwupd_client_get_approved_firmware (FwupdClient *client, - GCancellable *cancellable, - GError **error) +void +fwupd_client_get_approved_firmware_async (FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; - gchar **retval = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return NULL; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "GetApprovedFirmware", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return NULL; - } - g_variant_get (val, "(^as)", &retval); - return retval; + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetApprovedFirmware", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_get_approved_firmware_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_set_approved_firmware: - * @client: A #FwupdClient - * @checksums: Array of checksums - * @cancellable: the #GCancellable, or %NULL + * fwupd_client_get_approved_firmware_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * + * Gets the result of fwupd_client_get_approved_firmware_async(). + * + * Returns: (element-type utf8) (transfer container): checksums, or %NULL for error + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_approved_firmware_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_set_approved_firmware_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +/** + * fwupd_client_set_approved_firmware_async: + * @self: A #FwupdClient + * @checksums: (element-type utf8): firmware checksums + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * * Sets the list of approved firmware. * - * Returns: %TRUE for success - * - * Since: 1.2.6 + * Since: 1.5.0 **/ -gboolean -fwupd_client_set_approved_firmware (FwupdClient *client, - gchar **checksums, - GCancellable *cancellable, - GError **error) +void +fwupd_client_set_approved_firmware_async (FwupdClient *self, + GPtrArray *checksums, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; + g_auto(GStrv) strv = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "SetApprovedFirmware", - g_variant_new ("(^as)", checksums), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return FALSE; + task = g_task_new (self, cancellable, callback, callback_data); + strv = g_new0 (gchar *, checksums->len + 1); + for (guint i = 0; i < checksums->len; i++) { + const gchar *tmp = g_ptr_array_index (checksums, i); + strv[i] = g_strdup (tmp); } - return TRUE; + g_dbus_proxy_call (priv->proxy, "SetApprovedFirmware", + g_variant_new ("(^as)", strv), + G_DBUS_CALL_FLAGS_NONE, -1, + cancellable, + fwupd_client_set_approved_firmware_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_get_blocked_firmware: - * @client: A #FwupdClient - * @cancellable: the #GCancellable, or %NULL + * fwupd_client_set_approved_firmware_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * + * Gets the result of fwupd_client_set_approved_firmware_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_set_approved_firmware_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +static void +fwupd_client_get_blocked_firmware_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_auto(GStrv) strv = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free); + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + g_variant_get (val, "(^as)", &strv); + for (guint i = 0; strv[i] != NULL; i++) + g_ptr_array_add (array, g_strdup (strv[i])); + + /* success */ + g_task_return_pointer (task, + g_steal_pointer (&array), + (GDestroyNotify) g_ptr_array_unref); +} + +/** + * fwupd_client_get_blocked_firmware_async: + * @self: A #FwupdClient + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * * Gets the list of blocked firmware. * - * Returns: (transfer full): list of checksums, or %NULL + * You must have called fwupd_client_connect_async() on @self before using + * this method. * - * Since: 1.4.6 + * Since: 1.5.0 **/ -gchar ** -fwupd_client_get_blocked_firmware (FwupdClient *client, - GCancellable *cancellable, - GError **error) +void +fwupd_client_get_blocked_firmware_async (FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; - gchar **retval = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return NULL; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "GetBlockedFirmware", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return NULL; - } - g_variant_get (val, "(^as)", &retval); - return retval; + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "GetBlockedFirmware", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_get_blocked_firmware_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_set_blocked_firmware: - * @client: A #FwupdClient - * @checksums: Array of checksums - * @cancellable: the #GCancellable, or %NULL + * fwupd_client_get_blocked_firmware_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * + * Gets the result of fwupd_client_get_blocked_firmware_async(). + * + * Returns: (element-type utf8) (transfer container): checksums, or %NULL for error + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_blocked_firmware_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_set_blocked_firmware_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +/** + * fwupd_client_set_blocked_firmware_async: + * @self: A #FwupdClient + * @checksums: (element-type utf8): firmware checksums + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * * Sets the list of blocked firmware. * - * Returns: %TRUE for success - * - * Since: 1.4.6 + * Since: 1.5.0 **/ -gboolean -fwupd_client_set_blocked_firmware (FwupdClient *client, - gchar **checksums, - GCancellable *cancellable, - GError **error) +void +fwupd_client_set_blocked_firmware_async (FwupdClient *self, + GPtrArray *checksums, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; + g_auto(GStrv) strv = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "SetBlockedFirmware", - g_variant_new ("(^as)", checksums), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return FALSE; + task = g_task_new (self, cancellable, callback, callback_data); + strv = g_new0 (gchar *, checksums->len + 1); + for (guint i = 0; i < checksums->len; i++) { + const gchar *tmp = g_ptr_array_index (checksums, i); + strv[i] = g_strdup (tmp); } - return TRUE; + g_dbus_proxy_call (priv->proxy, "SetBlockedFirmware", + g_variant_new ("(^as)", strv), + G_DBUS_CALL_FLAGS_NONE, -1, + cancellable, + fwupd_client_set_blocked_firmware_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_set_feature_flags: - * @client: A #FwupdClient + * fwupd_client_set_blocked_firmware_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_set_blocked_firmware_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_set_blocked_firmware_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +static void +fwupd_client_set_feature_flags_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +/** + * fwupd_client_set_feature_flags_async: + * @self: A #FwupdClient * @feature_flags: #FwupdFeatureFlags, e.g. %FWUPD_FEATURE_FLAG_UPDATE_TEXT * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Sets the features the client supports. This allows firmware to depend on * specific front-end features, for instance showing the user an image on * how to detach the hardware. * - * Clients can call this none or multiple times. - * - * Returns: %TRUE for success - * - * Since: 1.4.5 + * Since: 1.5.0 **/ -gboolean -fwupd_client_set_feature_flags (FwupdClient *client, - FwupdFeatureFlags feature_flags, - GCancellable *cancellable, - GError **error) +void +fwupd_client_set_feature_flags_async (FwupdClient *self, + FwupdFeatureFlags feature_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "SetFeatureFlags", - g_variant_new ("(t)", (guint64) feature_flags), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return FALSE; - } - return TRUE; + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "SetFeatureFlags", + g_variant_new ("(t)", (guint64) feature_flags), + G_DBUS_CALL_FLAGS_NONE, -1, + cancellable, + fwupd_client_set_feature_flags_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_self_sign: - * @client: A #FwupdClient - * @value: A string to sign, typically a JSON blob - * @flags: #FwupdSelfSignFlags, e.g. %FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP - * @cancellable: the #GCancellable, or %NULL + * fwupd_client_set_feature_flags_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult * @error: the #GError, or %NULL * - * Signs the data using the client self-signed certificate. + * Gets the result of fwupd_client_set_feature_flags_async(). * * Returns: %TRUE for success * - * Since: 1.2.6 + * Since: 1.5.0 **/ -gchar * -fwupd_client_self_sign (FwupdClient *client, - const gchar *value, - FwupdSelfSignFlags flags, - GCancellable *cancellable, - GError **error) +gboolean +fwupd_client_set_feature_flags_finish (FwupdClient *self, GAsyncResult *res, GError **error) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - GVariantBuilder builder; + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +static void +fwupd_client_self_sign_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + gchar *str = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; g_autoptr(GVariant) val = NULL; - gchar *retval = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return NULL; + /* success */ + g_variant_get (val, "(s)", &str); + g_task_return_pointer (task, + g_steal_pointer (&str), + (GDestroyNotify) g_free); +} + +/** + * fwupd_client_self_sign_async: + * @self: A #FwupdClient + * @value: A string to sign, typically a JSON blob + * @flags: #FwupdSelfSignFlags, e.g. %FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Signs the data using the client self-signed certificate. + * + * You must have called fwupd_client_connect_async() on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_self_sign_async (FwupdClient *self, + const gchar *value, + FwupdSelfSignFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + GVariantBuilder builder; + g_autoptr(GTask) task = NULL; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (value != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* set options */ g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); @@ -2172,131 +3527,204 @@ fwupd_client_self_sign (FwupdClient *client, } /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "SelfSign", - g_variant_new ("(sa{sv})", value, &builder), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return NULL; - } - g_variant_get (val, "(s)", &retval); - return retval; + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, + "SelfSign", + g_variant_new ("(sa{sv})", value, &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, cancellable, + fwupd_client_self_sign_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_modify_remote: - * @client: A #FwupdClient + * fwupd_client_self_sign_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_self_sign_async(). + * + * Returns: a signature, or %NULL for failure + * + * Since: 1.5.0 + **/ +gchar * +fwupd_client_self_sign_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_modify_remote_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +/** + * fwupd_client_modify_remote_async: + * @self: A #FwupdClient * @remote_id: the remote ID, e.g. `lvfs-testing` * @key: the key, e.g. `Enabled` * @value: the key, e.g. `true` * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Modifies a system remote in a specific way. * - * NOTE: User authentication may be required to complete this action. - * - * Returns: %TRUE for success - * - * Since: 0.9.8 + * Since: 1.5.0 **/ -gboolean -fwupd_client_modify_remote (FwupdClient *client, - const gchar *remote_id, - const gchar *key, - const gchar *value, - GCancellable *cancellable, - GError **error) +void +fwupd_client_modify_remote_async (FwupdClient *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (remote_id != NULL, FALSE); - g_return_val_if_fail (key != NULL, FALSE); - g_return_val_if_fail (value != NULL, FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (remote_id != NULL); + g_return_if_fail (key != NULL); + g_return_if_fail (value != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "ModifyRemote", - g_variant_new ("(sss)", remote_id, key, value), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return FALSE; - } - return TRUE; + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "ModifyRemote", + g_variant_new ("(sss)", remote_id, key, value), + G_DBUS_CALL_FLAGS_NONE, -1, + cancellable, + fwupd_client_modify_remote_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_modify_device: - * @client: A #FwupdClient + * fwupd_client_modify_remote_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_modify_remote_async(). + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_modify_remote_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean (G_TASK(res), error); +} + +static void +fwupd_client_modify_device_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error (error); + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_boolean (task, TRUE); +} + +/** + * fwupd_client_modify_device_async: + * @self: A #FwupdClient * @device_id: the device ID * @key: the key, e.g. `Flags` * @value: the key, e.g. `reported` * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Modifies a device in a specific way. Not all properties on the #FwupdDevice * are settable by the client, and some may have other restrictions on @value. * - * NOTE: User authentication may be required to complete this action. + * Since: 1.5.0 + **/ +void +fwupd_client_modify_device_async (FwupdClient *self, + const gchar *device_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (device_id != NULL); + g_return_if_fail (key != NULL); + g_return_if_fail (value != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new (self, cancellable, callback, callback_data); + g_dbus_proxy_call (priv->proxy, "ModifyDevice", + g_variant_new ("(sss)", device_id, key, value), + G_DBUS_CALL_FLAGS_NONE, -1, + cancellable, + fwupd_client_modify_device_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_modify_device_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_modify_device_async(). * * Returns: %TRUE for success * - * Since: 1.0.4 + * Since: 1.5.0 **/ gboolean -fwupd_client_modify_device (FwupdClient *client, - const gchar *remote_id, - const gchar *key, - const gchar *value, - GCancellable *cancellable, - GError **error) +fwupd_client_modify_device_finish (FwupdClient *self, GAsyncResult *res, GError **error) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_autoptr(GVariant) val = NULL; - - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); - g_return_val_if_fail (remote_id != NULL, FALSE); - g_return_val_if_fail (key != NULL, FALSE); - g_return_val_if_fail (value != NULL, FALSE); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - /* connect */ - if (!fwupd_client_connect (client, cancellable, error)) - return FALSE; - - /* call into daemon */ - val = g_dbus_proxy_call_sync (priv->proxy, - "ModifyDevice", - g_variant_new ("(sss)", remote_id, key, value), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (val == NULL) { - if (error != NULL) - fwupd_client_fixup_dbus_error (*error); - return FALSE; - } - return TRUE; + return g_task_propagate_boolean (G_TASK(res), error); } static FwupdRemote * @@ -2310,54 +3738,98 @@ fwupd_client_get_remote_by_id_noref (GPtrArray *remotes, const gchar *remote_id) return NULL; } -/** - * fwupd_client_get_remote_by_id: - * @client: A #FwupdClient - * @remote_id: the remote ID, e.g. `lvfs-testing` - * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL - * - * Gets a specific remote that has been configured for the system. - * - * Returns: (transfer full): a #FwupdRemote, or %NULL if not found - * - * Since: 0.9.3 - **/ -FwupdRemote * -fwupd_client_get_remote_by_id (FwupdClient *client, - const gchar *remote_id, - GCancellable *cancellable, - GError **error) +static void +fwupd_client_get_remote_by_id_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) { - FwupdRemote *remote; + FwupdRemote *remote_tmp; + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) remotes = NULL; + const gchar *remote_id = g_task_get_task_data (task); - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (remote_id != NULL, NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); + remotes = fwupd_client_get_remotes_finish (FWUPD_CLIENT (source), res, &error); + if (remotes == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } - /* find remote in list */ - remotes = fwupd_client_get_remotes (client, cancellable, error); - if (remotes == NULL) - return NULL; - remote = fwupd_client_get_remote_by_id_noref (remotes, remote_id); - if (remote == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No remote '%s' found in search paths", - remote_id); - return NULL; + remote_tmp = fwupd_client_get_remote_by_id_noref (remotes, remote_id); + if (remote_tmp == NULL) { + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no remote '%s' found in search paths", + remote_id); + return; } /* success */ - return g_object_ref (remote); + g_task_return_pointer (task, + g_object_ref (remote_tmp), + (GDestroyNotify) g_object_unref); +} + +/** + * fwupd_client_get_remote_by_id_async: + * @self: A #FwupdClient + * @remote_id: the remote ID, e.g. `lvfs-testing` + * @cancellable: the #GCancellable, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets a specific remote that has been configured for the system. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_remote_by_id_async (FwupdClient *self, + const gchar *remote_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (remote_id != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new (self, cancellable, callback, callback_data); + g_task_set_task_data (task, g_strdup (remote_id), g_free); + fwupd_client_get_remotes_async (self, cancellable, + fwupd_client_get_remote_by_id_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_get_remote_by_id_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_get_remote_by_id_async(). + * + * Returns: (transfer full): a #FwupdRemote, or %NULL if not found + * + * Since: 1.5.0 + **/ +FwupdRemote * +fwupd_client_get_remote_by_id_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); } /** * fwupd_client_set_user_agent: - * @client: A #FwupdClient + * @self: A #FwupdClient * @user_agent: the user agent ID, e.g. `gnome-software/3.34.1` * * Manually sets the user agent that is used for downloading. The user agent @@ -2366,10 +3838,10 @@ fwupd_client_get_remote_by_id (FwupdClient *client, * Since: 1.4.5 **/ void -fwupd_client_set_user_agent (FwupdClient *client, const gchar *user_agent) +fwupd_client_set_user_agent (FwupdClient *self, const gchar *user_agent) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - g_return_if_fail (FWUPD_IS_CLIENT (client)); + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FWUPD_IS_CLIENT (self)); g_return_if_fail (user_agent != NULL); g_free (priv->user_agent); priv->user_agent = g_strdup (user_agent); @@ -2377,7 +3849,7 @@ fwupd_client_set_user_agent (FwupdClient *client, const gchar *user_agent) /** * fwupd_client_set_user_agent_for_package: - * @client: A #FwupdClient + * @self: A #FwupdClient * @package_name: client program name, e.g. "gnome-software" * @package_version: client program version, e.g. "3.28.1" * @@ -2394,15 +3866,15 @@ fwupd_client_set_user_agent (FwupdClient *client, const gchar *user_agent) * Since: 1.4.5 **/ void -fwupd_client_set_user_agent_for_package (FwupdClient *client, +fwupd_client_set_user_agent_for_package (FwupdClient *self, const gchar *package_name, const gchar *package_version) { - FwupdClientPrivate *priv = GET_PRIVATE (client); + FwupdClientPrivate *priv = GET_PRIVATE (self); GString *str = g_string_new (NULL); g_autofree gchar *system = NULL; - g_return_if_fail (FWUPD_IS_CLIENT (client)); + g_return_if_fail (FWUPD_IS_CLIENT (self)); g_return_if_fail (package_name != NULL); g_return_if_fail (package_version != NULL); @@ -2423,14 +3895,13 @@ fwupd_client_set_user_agent_for_package (FwupdClient *client, priv->user_agent = g_string_free (str, FALSE); } - static void fwupd_client_download_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data) { guint percentage; goffset header_size; goffset body_length; - FwupdClient *client = FWUPD_CLIENT (user_data); + FwupdClient *self = FWUPD_CLIENT (user_data); /* if it's returning "Found" or an error, ignore the percentage */ if (msg->status_code != SOUP_STATUS_OK) { @@ -2448,130 +3919,250 @@ fwupd_client_download_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, gpointer us /* calculate percentage */ percentage = (guint) ((100 * body_length) / header_size); g_debug ("progress: %u%%", percentage); - fwupd_client_set_status (client, FWUPD_STATUS_DOWNLOADING); - fwupd_client_set_percentage (client, percentage); + fwupd_client_set_percentage (self, percentage); +} + +static void +fwupd_client_read_bytes_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); + + bytes = fwupd_input_stream_read_bytes_finish (G_INPUT_STREAM (source), res, &error); + if (bytes == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* success */ + g_task_return_pointer (task, + g_steal_pointer (&bytes), + (GDestroyNotify) g_bytes_unref); +} + +static void +fwupd_client_download_bytes_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + FwupdClient *self = g_task_get_source_object (task); + FwupdClientPrivate *priv = GET_PRIVATE (self); + GCancellable *cancellable = g_task_get_cancellable (task); + g_autoptr(GError) error = NULL; + g_autoptr(GInputStream) istr = NULL; + guint status_code = 0; + SoupMessage *msg = g_task_get_task_data (task); + + /* get the result */ + fwupd_client_set_status (self, FWUPD_STATUS_IDLE); + istr = soup_session_send_finish (priv->soup_session, res, &error); + if (istr == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* check the input stream before reading the data */ + g_object_get (msg, "status-code", &status_code, NULL); + g_debug ("status-code was %u", status_code); + if (status_code == 429) { + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to download due to server limit"); + return; + } + if (status_code != SOUP_STATUS_OK) { + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to download: %s", + soup_status_get_phrase (status_code)); + return; + } + + /* read the input stream into a GBytes, async */ + fwupd_input_stream_read_bytes_async (istr, cancellable, + fwupd_client_read_bytes_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_download_bytes: - * @client: A #FwupdClient + * fwupd_client_download_bytes_async: + * @self: A #FwupdClient * @url: the remote URL * @flags: #FwupdClientDownloadFlags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Downloads data from a remote server. The fwupd_client_set_user_agent() function * should be called before this method is used. * - * Returns: (transfer full): downloaded data, or %NULL for error + * You must have called fwupd_client_connect_async() on @self before using + * this method. * - * Since: 1.4.5 + * Since: 1.5.0 **/ -GBytes * -fwupd_client_download_bytes (FwupdClient *client, - const gchar *url, - FwupdClientDownloadFlags flags, - GCancellable *cancellable, - GError **error) +void +fwupd_client_download_bytes_async (FwupdClient *self, + const gchar *url, + FwupdClientDownloadFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - guint status_code; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; g_autoptr(SoupMessage) msg = NULL; g_autoptr(SoupURI) uri = NULL; + g_autoptr(GError) error = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (url != NULL, NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (url != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* ensure networking set up */ - if (!fwupd_client_ensure_networking (client, error)) - return NULL; + task = g_task_new (self, cancellable, callback, callback_data); + if (!fwupd_client_ensure_networking (self, &error)) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } /* download data */ g_debug ("downloading %s", url); uri = soup_uri_new (url); msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); if (msg == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Failed to parse URI %s", url); - return NULL; + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to parse URI %s", url); + return; } g_signal_connect (msg, "got-chunk", G_CALLBACK (fwupd_client_download_chunk_cb), - client); - status_code = soup_session_send_message (priv->soup_session, msg); - fwupd_client_set_status (client, FWUPD_STATUS_IDLE); - if (status_code == 429) { - g_autofree gchar *str = g_strndup (msg->response_body->data, - msg->response_body->length); - if (g_strcmp0 (str, "Too Many Requests") == 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Failed to download due to server limit"); - return NULL; - } - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Failed to download due to server limit: %s", str); - return NULL; - } - if (status_code != SOUP_STATUS_OK) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Failed to download %s: %s", - url, soup_status_get_phrase (status_code)); - return NULL; - } - - /* success */ - return g_bytes_new (msg->response_body->data, msg->response_body->length); + self); + g_task_set_task_data (task, g_object_ref (msg), (GDestroyNotify) g_object_unref); + fwupd_client_set_status (self, FWUPD_STATUS_DOWNLOADING); + soup_session_send_async (priv->soup_session, msg, + cancellable, + fwupd_client_download_bytes_cb, + g_steal_pointer (&task)); } /** - * fwupd_client_upload_bytes: - * @client: A #FwupdClient + * fwupd_client_download_bytes_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_download_bytes_async(). + * + * Returns: (transfer full): downloaded data, or %NULL for error + * + * Since: 1.5.0 + **/ +GBytes * +fwupd_client_download_bytes_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + +static void +fwupd_client_upload_bytes_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + FwupdClient *self = g_task_get_source_object (task); + FwupdClientPrivate *priv = GET_PRIVATE (self); + GCancellable *cancellable = g_task_get_cancellable (task); + g_autoptr(GError) error = NULL; + g_autoptr(GInputStream) istr = NULL; + guint status_code; + SoupMessage *msg = g_task_get_task_data (task); + + /* get the result */ + istr = soup_session_send_finish (priv->soup_session, res, &error); + if (istr == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* check the input stream before reading the data */ + g_object_get (msg, "status-code", &status_code, NULL); + g_debug ("status-code was %u", status_code); + if (!SOUP_STATUS_IS_SUCCESSFUL (status_code)) { + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to download: %s", + soup_status_get_phrase (status_code)); + return; + } + + /* read the input stream into a GBytes, async */ + fwupd_input_stream_read_bytes_async (istr, cancellable, + fwupd_client_read_bytes_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_client_upload_bytes_async: + * @self: A #FwupdClient * @url: the remote URL * @payload: payload string * @signature: (nullable): signature string * @flags: #FwupdClientDownloadFlags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE * @cancellable: the #GCancellable, or %NULL - * @error: the #GError, or %NULL + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback * * Uploads data to a remote server. The fwupd_client_set_user_agent() function * should be called before this method is used. * - * Returns: (transfer full): downloaded data, or %NULL for error + * You must have called fwupd_client_connect_async() on @self before using + * this method. * - * Since: 1.4.5 + * Since: 1.5.0 **/ -GBytes * -fwupd_client_upload_bytes (FwupdClient *client, - const gchar *url, - const gchar *payload, - const gchar *signature, - FwupdClientUploadFlags flags, - GCancellable *cancellable, - GError **error) +void +fwupd_client_upload_bytes_async (FwupdClient *self, + const gchar *url, + const gchar *payload, + const gchar *signature, + FwupdClientUploadFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) { - FwupdClientPrivate *priv = GET_PRIVATE (client); - guint status_code; + FwupdClientPrivate *priv = GET_PRIVATE (self); + g_autoptr(GTask) task = NULL; g_autoptr(SoupMessage) msg = NULL; g_autoptr(SoupURI) uri = NULL; + g_autoptr(GError) error = NULL; - g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); - g_return_val_if_fail (url != NULL, NULL); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); + g_return_if_fail (FWUPD_IS_CLIENT (self)); + g_return_if_fail (url != NULL); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (priv->proxy != NULL); /* ensure networking set up */ - if (!fwupd_client_ensure_networking (client, error)) - return NULL; + task = g_task_new (self, cancellable, callback, callback_data); + if (!fwupd_client_ensure_networking (self, &error)) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* download data */ + g_debug ("downloading %s", url); + uri = soup_uri_new (url); /* build message */ if ((flags & FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART) > 0 || @@ -2590,36 +4181,51 @@ fwupd_client_upload_bytes (FwupdClient *client, /* POST request */ if (msg == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Failed to parse URI %s", url); - return NULL; + g_task_return_new_error (task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to parse URI %s", url); + return; } + g_signal_connect (msg, "got-chunk", + G_CALLBACK (fwupd_client_download_chunk_cb), + self); + g_task_set_task_data (task, g_object_ref (msg), (GDestroyNotify) g_object_unref); + fwupd_client_set_status (self, FWUPD_STATUS_IDLE); g_debug ("uploading to %s", url); - status_code = soup_session_send_message (priv->soup_session, msg); - g_debug ("server returned: %s", msg->response_body->data); + soup_session_send_async (priv->soup_session, msg, + cancellable, + fwupd_client_upload_bytes_cb, + g_steal_pointer (&task)); +} - /* fall back to HTTP status codes in case the server is offline */ - if (!SOUP_STATUS_IS_SUCCESSFUL (status_code)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Failed to upllooad to %s: %s", - url, soup_status_get_phrase (status_code)); - return NULL; - } - - /* success */ - return g_bytes_new (msg->response_body->data, msg->response_body->length); +/** + * fwupd_client_upload_bytes_finish: + * @self: A #FwupdClient + * @res: the #GAsyncResult + * @error: the #GError, or %NULL + * + * Gets the result of fwupd_client_upload_bytes_async(). + * + * Returns: (transfer full): response data, or %NULL for error + * + * Since: 1.5.0 + **/ +GBytes * +fwupd_client_upload_bytes_finish (FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL); + g_return_val_if_fail (g_task_is_valid (res, self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); } static void fwupd_client_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - FwupdClient *client = FWUPD_CLIENT (object); - FwupdClientPrivate *priv = GET_PRIVATE (client); + FwupdClient *self = FWUPD_CLIENT (object); + FwupdClientPrivate *priv = GET_PRIVATE (self); switch (prop_id) { case PROP_STATUS: @@ -2643,6 +4249,9 @@ fwupd_client_get_property (GObject *object, guint prop_id, case PROP_HOST_MACHINE_ID: g_value_set_string (value, priv->host_machine_id); break; + case PROP_HOST_SECURITY_ID: + g_value_set_string (value, priv->host_security_id); + break; case PROP_INTERACTIVE: g_value_set_boolean (value, priv->interactive); break; @@ -2656,8 +4265,8 @@ static void fwupd_client_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - FwupdClient *client = FWUPD_CLIENT (object); - FwupdClientPrivate *priv = GET_PRIVATE (client); + FwupdClient *self = FWUPD_CLIENT (object); + FwupdClientPrivate *priv = GET_PRIVATE (self); switch (prop_id) { case PROP_STATUS: @@ -2686,7 +4295,7 @@ fwupd_client_class_init (FwupdClientClass *klass) /** * FwupdClient::changed: - * @client: the #FwupdClient instance that emitted the signal + * @self: the #FwupdClient instance that emitted the signal * * The ::changed signal is emitted when the daemon internal has * changed, for instance when a device has been added or removed. @@ -2702,7 +4311,7 @@ fwupd_client_class_init (FwupdClientClass *klass) /** * FwupdClient::state-changed: - * @client: the #FwupdClient instance that emitted the signal + * @self: the #FwupdClient instance that emitted the signal * @status: the #FwupdStatus * * The ::state-changed signal is emitted when the daemon status has @@ -2719,7 +4328,7 @@ fwupd_client_class_init (FwupdClientClass *klass) /** * FwupdClient::device-added: - * @client: the #FwupdClient instance that emitted the signal + * @self: the #FwupdClient instance that emitted the signal * @result: the #FwupdDevice * * The ::device-added signal is emitted when a device has been @@ -2736,7 +4345,7 @@ fwupd_client_class_init (FwupdClientClass *klass) /** * FwupdClient::device-removed: - * @client: the #FwupdClient instance that emitted the signal + * @self: the #FwupdClient instance that emitted the signal * @result: the #FwupdDevice * * The ::device-removed signal is emitted when a device has been @@ -2753,7 +4362,7 @@ fwupd_client_class_init (FwupdClientClass *klass) /** * FwupdClient::device-changed: - * @client: the #FwupdClient instance that emitted the signal + * @self: the #FwupdClient instance that emitted the signal * @result: the #FwupdDevice * * The ::device-changed signal is emitted when a device has been @@ -2857,23 +4466,35 @@ fwupd_client_class_init (FwupdClientClass *klass) pspec = g_param_spec_string ("host-machine-id", NULL, NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_HOST_MACHINE_ID, pspec); + + /** + * FwupdClient:host-security-id: + * + * The host machine-id string + * + * Since: 1.5.0 + */ + pspec = g_param_spec_string ("host-security-id", NULL, NULL, + NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_HOST_SECURITY_ID, pspec); } static void -fwupd_client_init (FwupdClient *client) +fwupd_client_init (FwupdClient *self) { } static void fwupd_client_finalize (GObject *object) { - FwupdClient *client = FWUPD_CLIENT (object); - FwupdClientPrivate *priv = GET_PRIVATE (client); + FwupdClient *self = FWUPD_CLIENT (object); + FwupdClientPrivate *priv = GET_PRIVATE (self); g_free (priv->user_agent); g_free (priv->daemon_version); g_free (priv->host_product); g_free (priv->host_machine_id); + g_free (priv->host_security_id); if (priv->conn != NULL) g_object_unref (priv->conn); if (priv->proxy != NULL) @@ -2896,7 +4517,7 @@ fwupd_client_finalize (GObject *object) FwupdClient * fwupd_client_new (void) { - FwupdClient *client; - client = g_object_new (FWUPD_TYPE_CLIENT, NULL); - return FWUPD_CLIENT (client); + FwupdClient *self; + self = g_object_new (FWUPD_TYPE_CLIENT, NULL); + return FWUPD_CLIENT (self); } diff --git a/libfwupd/fwupd-client.h b/libfwupd/fwupd-client.h index bd6917d45..6899df329 100644 --- a/libfwupd/fwupd-client.h +++ b/libfwupd/fwupd-client.h @@ -11,6 +11,7 @@ #include "fwupd-enums.h" #include "fwupd-device.h" +#include "fwupd-plugin.h" #include "fwupd-remote.h" G_BEGIN_DECLS @@ -67,171 +68,319 @@ typedef enum { } FwupdClientUploadFlags; FwupdClient *fwupd_client_new (void); -gboolean fwupd_client_connect (FwupdClient *client, +void fwupd_client_connect_async (FwupdClient *self, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_connect_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -GPtrArray *fwupd_client_get_devices (FwupdClient *client, +void fwupd_client_get_devices_async (FwupdClient *self, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_devices_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -GPtrArray *fwupd_client_get_history (FwupdClient *client, +void fwupd_client_get_plugins_async (FwupdClient *self, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_plugins_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -GPtrArray *fwupd_client_get_releases (FwupdClient *client, +void fwupd_client_get_history_async (FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_history_finish (FwupdClient *self, + GAsyncResult *res, + GError **error); +void fwupd_client_get_releases_async (FwupdClient *self, const gchar *device_id, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_releases_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -GPtrArray *fwupd_client_get_downgrades (FwupdClient *client, +void fwupd_client_get_downgrades_async (FwupdClient *self, const gchar *device_id, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_downgrades_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -GPtrArray *fwupd_client_get_upgrades (FwupdClient *client, +void fwupd_client_get_upgrades_async (FwupdClient *self, const gchar *device_id, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_upgrades_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -GPtrArray *fwupd_client_get_details (FwupdClient *client, - const gchar *filename, +void fwupd_client_get_details_bytes_async (FwupdClient *self, + GBytes *bytes, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_details_bytes_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_verify (FwupdClient *client, +void fwupd_client_verify_async (FwupdClient *self, const gchar *device_id, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_verify_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_verify_update (FwupdClient *client, +void fwupd_client_verify_update_async (FwupdClient *self, const gchar *device_id, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_verify_update_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_unlock (FwupdClient *client, +void fwupd_client_unlock_async (FwupdClient *self, const gchar *device_id, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_unlock_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_modify_config (FwupdClient *client, +void fwupd_client_modify_config_async (FwupdClient *self, const gchar *key, const gchar *value, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_modify_config_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_activate (FwupdClient *client, - GCancellable *cancellable, - const gchar *device_id, - GError **error); -gboolean fwupd_client_clear_results (FwupdClient *client, +void fwupd_client_activate_async (FwupdClient *self, const gchar *device_id, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_activate_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -FwupdDevice *fwupd_client_get_results (FwupdClient *client, +void fwupd_client_clear_results_async (FwupdClient *self, const gchar *device_id, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_clear_results_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -FwupdDevice *fwupd_client_get_device_by_id (FwupdClient *client, +void fwupd_client_get_results_async (FwupdClient *self, const gchar *device_id, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +FwupdDevice *fwupd_client_get_results_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -GPtrArray *fwupd_client_get_devices_by_guid (FwupdClient *client, +void fwupd_client_get_host_security_attrs_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_host_security_attrs_finish(FwupdClient *self, + GAsyncResult *res, + GError **error); +void fwupd_client_get_device_by_id_async (FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +FwupdDevice *fwupd_client_get_device_by_id_finish (FwupdClient *self, + GAsyncResult *res, + GError **error); +void fwupd_client_get_devices_by_guid_async (FwupdClient *self, const gchar *guid, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_devices_by_guid_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_install (FwupdClient *client, +void fwupd_client_install_async (FwupdClient *self, const gchar *device_id, const gchar *filename, FwupdInstallFlags install_flags, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_install_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_install_bytes (FwupdClient *client, +void fwupd_client_install_bytes_async (FwupdClient *self, const gchar *device_id, GBytes *bytes, FwupdInstallFlags install_flags, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_install_bytes_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_install_release (FwupdClient *client, +void fwupd_client_install_release_async (FwupdClient *self, FwupdDevice *device, FwupdRelease *release, FwupdInstallFlags install_flags, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_install_release_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_update_metadata (FwupdClient *client, - const gchar *remote_id, - const gchar *metadata_fn, - const gchar *signature_fn, - GCancellable *cancellable, - GError **error); -gboolean fwupd_client_update_metadata_bytes (FwupdClient *client, +void fwupd_client_update_metadata_bytes_async (FwupdClient *self, const gchar *remote_id, GBytes *metadata, GBytes *signature, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_update_metadata_bytes_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_refresh_remote (FwupdClient *client, +void fwupd_client_refresh_remote_async (FwupdClient *self, FwupdRemote *remote, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_refresh_remote_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_modify_remote (FwupdClient *client, +void fwupd_client_modify_remote_async (FwupdClient *self, const gchar *remote_id, const gchar *key, const gchar *value, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_modify_remote_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_modify_device (FwupdClient *client, +void fwupd_client_modify_device_async (FwupdClient *self, const gchar *device_id, const gchar *key, const gchar *value, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_modify_device_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -FwupdStatus fwupd_client_get_status (FwupdClient *client); -gboolean fwupd_client_get_tainted (FwupdClient *client); -gboolean fwupd_client_get_daemon_interactive (FwupdClient *client); -guint fwupd_client_get_percentage (FwupdClient *client); -const gchar *fwupd_client_get_daemon_version (FwupdClient *client); -const gchar *fwupd_client_get_host_product (FwupdClient *client); -const gchar *fwupd_client_get_host_machine_id (FwupdClient *client); - -GPtrArray *fwupd_client_get_remotes (FwupdClient *client, +void fwupd_client_get_report_metadata_async (FwupdClient *self, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GHashTable *fwupd_client_get_report_metadata_finish(FwupdClient *self, + GAsyncResult *res, GError **error); -FwupdRemote *fwupd_client_get_remote_by_id (FwupdClient *client, + +FwupdStatus fwupd_client_get_status (FwupdClient *self); +gboolean fwupd_client_get_tainted (FwupdClient *self); +gboolean fwupd_client_get_daemon_interactive (FwupdClient *self); +guint fwupd_client_get_percentage (FwupdClient *self); +const gchar *fwupd_client_get_daemon_version (FwupdClient *self); +const gchar *fwupd_client_get_host_product (FwupdClient *self); +const gchar *fwupd_client_get_host_machine_id (FwupdClient *self); +const gchar *fwupd_client_get_host_security_id (FwupdClient *self); + +void fwupd_client_get_remotes_async (FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_remotes_finish (FwupdClient *self, + GAsyncResult *res, + GError **error); +void fwupd_client_get_remote_by_id_async (FwupdClient *self, const gchar *remote_id, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +FwupdRemote *fwupd_client_get_remote_by_id_finish (FwupdClient *self, + GAsyncResult *res, GError **error); - -gchar **fwupd_client_get_approved_firmware (FwupdClient *client, +void fwupd_client_get_approved_firmware_async(FwupdClient *self, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_approved_firmware_finish(FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_set_approved_firmware (FwupdClient *client, - gchar **checksums, +void fwupd_client_set_approved_firmware_async (FwupdClient *self, + GPtrArray *checksums, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_set_approved_firmware_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gchar **fwupd_client_get_blocked_firmware (FwupdClient *client, +void fwupd_client_get_blocked_firmware_async(FwupdClient *self, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray *fwupd_client_get_blocked_firmware_finish(FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_set_blocked_firmware (FwupdClient *client, - gchar **checksums, +void fwupd_client_set_blocked_firmware_async (FwupdClient *self, + GPtrArray *checksums, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_set_blocked_firmware_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gchar *fwupd_client_self_sign (FwupdClient *client, +void fwupd_client_self_sign_async (FwupdClient *self, const gchar *value, FwupdSelfSignFlags flags, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gchar *fwupd_client_self_sign_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_set_feature_flags (FwupdClient *client, +void fwupd_client_set_feature_flags_async (FwupdClient *self, FwupdFeatureFlags feature_flags, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean fwupd_client_set_feature_flags_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -void fwupd_client_set_user_agent (FwupdClient *client, +void fwupd_client_set_user_agent (FwupdClient *self, const gchar *user_agent); -void fwupd_client_set_user_agent_for_package(FwupdClient *client, +void fwupd_client_set_user_agent_for_package(FwupdClient *self, const gchar *package_name, const gchar *package_version); -GBytes *fwupd_client_download_bytes (FwupdClient *client, +void fwupd_client_download_bytes_async (FwupdClient *self, const gchar *url, FwupdClientDownloadFlags flags, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GBytes *fwupd_client_download_bytes_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -GBytes *fwupd_client_upload_bytes (FwupdClient *client, +void fwupd_client_upload_bytes_async (FwupdClient *self, const gchar *url, const gchar *payload, const gchar *signature, FwupdClientUploadFlags flags, GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GBytes *fwupd_client_upload_bytes_finish (FwupdClient *self, + GAsyncResult *res, GError **error); -gboolean fwupd_client_ensure_networking (FwupdClient *client, +gboolean fwupd_client_ensure_networking (FwupdClient *self, GError **error); G_END_DECLS diff --git a/libfwupd/fwupd-common-private.h b/libfwupd/fwupd-common-private.h index fc1f91474..fb342eea2 100644 --- a/libfwupd/fwupd-common-private.h +++ b/libfwupd/fwupd-common-private.h @@ -6,7 +6,7 @@ #pragma once -#include +#include #ifdef HAVE_GIO_UNIX #include @@ -21,6 +21,14 @@ GVariant *fwupd_hash_kv_to_variant (GHashTable *hash); GHashTable *fwupd_variant_to_hash_kv (GVariant *dict); gchar *fwupd_build_user_agent_system (void); +void fwupd_input_stream_read_bytes_async (GInputStream *stream, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GBytes *fwupd_input_stream_read_bytes_finish (GInputStream *stream, + GAsyncResult *res, + GError **error); + #ifdef HAVE_GIO_UNIX GUnixInputStream *fwupd_unix_input_stream_from_bytes (GBytes *bytes, GError **error); diff --git a/libfwupd/fwupd-common.c b/libfwupd/fwupd-common.c index 40bfc0387..e5d989618 100644 --- a/libfwupd/fwupd-common.c +++ b/libfwupd/fwupd-common.c @@ -169,7 +169,6 @@ fwupd_get_os_release (GError **error) /* find the correct file */ for (guint i = 0; paths[i] != NULL; i++) { - g_debug ("looking for os-release at %s", paths[i]); if (g_file_test (paths[i], G_FILE_TEST_EXISTS)) { filename = paths[i]; break; @@ -941,6 +940,96 @@ fwupd_variant_to_hash_kv (GVariant *dict) return hash; } +static void +fwupd_input_stream_read_bytes_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GByteArray *bufarr; + GInputStream *stream = G_INPUT_STREAM (source); + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK (user_data); +#if GLIB_CHECK_VERSION(2, 64, 0) + guint8 *buf; + gsize bufsz = 0; +#endif + + /* read buf */ + bytes = g_input_stream_read_bytes_finish (stream, res, &error); + if (bytes == NULL) { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + /* add bytes to buffer */ + bufarr = g_task_get_task_data (task); + if (g_bytes_get_size (bytes) > 0) { + GCancellable *cancellable = g_task_get_cancellable (task); + g_debug ("add %u", (guint) g_bytes_get_size (bytes)); + g_byte_array_append (bufarr, + g_bytes_get_data (bytes, NULL), + g_bytes_get_size (bytes)); + g_input_stream_read_bytes_async (g_steal_pointer (&stream), + 256 * 1024, /* bigger chunk */ + G_PRIORITY_DEFAULT, + cancellable, + fwupd_input_stream_read_bytes_cb, + g_steal_pointer (&task)); + return; + } + + /* success */ +#if GLIB_CHECK_VERSION(2, 64, 0) + buf = g_byte_array_steal (bufarr, &bufsz); + g_task_return_pointer (task, + g_bytes_new_take (buf, bufsz), + (GDestroyNotify) g_bytes_unref); +#else + g_task_return_pointer (task, + g_bytes_new (bufarr->data, bufarr->len), + (GDestroyNotify) g_bytes_unref); +#endif +} + +/** + * fwupd_input_stream_read_bytes_async: (skip): + **/ +void +fwupd_input_stream_read_bytes_async (GInputStream *stream, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (G_IS_INPUT_STREAM (stream)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (stream, cancellable, callback, callback_data); + g_task_set_task_data (task, g_byte_array_new (), (GDestroyNotify) g_byte_array_unref); + g_input_stream_read_bytes_async (stream, + 64 * 1024, /* small */ + G_PRIORITY_DEFAULT, + cancellable, + fwupd_input_stream_read_bytes_cb, + g_steal_pointer (&task)); +} + +/** + * fwupd_input_stream_read_bytes_finish: (skip): + **/ +GBytes * +fwupd_input_stream_read_bytes_finish (GInputStream *stream, + GAsyncResult *res, + GError **error) +{ + g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL); + g_return_val_if_fail (g_task_is_valid (res, stream), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer (G_TASK(res), error); +} + #ifdef HAVE_GIO_UNIX /** * fwupd_unix_input_stream_from_bytes: (skip): diff --git a/libfwupd/fwupd-device.c b/libfwupd/fwupd-device.c index 3d82ba344..2db08cfb2 100644 --- a/libfwupd/fwupd-device.c +++ b/libfwupd/fwupd-device.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017 Richard Hughes + * Copyright (C) 2015-2020 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -39,6 +39,7 @@ typedef struct { gchar *name; gchar *serial; gchar *summary; + gchar *branch; gchar *description; gchar *vendor; gchar *vendor_id; @@ -62,7 +63,7 @@ typedef struct { gchar *update_image; FwupdStatus status; GPtrArray *releases; - FwupdDevice *parent; + FwupdDevice *parent; /* noref */ } FwupdDevicePrivate; enum { @@ -71,6 +72,7 @@ enum { PROP_FLAGS, PROP_PROTOCOL, PROP_STATUS, + PROP_PARENT, PROP_LAST }; @@ -172,6 +174,42 @@ fwupd_device_set_summary (FwupdDevice *device, const gchar *summary) priv->summary = g_strdup (summary); } +/** + * fwupd_device_get_branch: + * @device: A #FwupdDevice + * + * Gets the current device branch. + * + * Returns: the device branch, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_device_get_branch (FwupdDevice *device) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_val_if_fail (FWUPD_IS_DEVICE (device), NULL); + return priv->branch; +} + +/** + * fwupd_device_set_branch: + * @device: A #FwupdDevice + * @branch: the device one line branch + * + * Sets the current device branch. + * + * Since: 1.5.0 + **/ +void +fwupd_device_set_branch (FwupdDevice *device, const gchar *branch) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + g_return_if_fail (FWUPD_IS_DEVICE (device)); + g_free (priv->branch); + priv->branch = g_strdup (branch); +} + /** * fwupd_device_get_serial: * @device: A #FwupdDevice @@ -311,10 +349,40 @@ void fwupd_device_set_parent (FwupdDevice *device, FwupdDevice *parent) { FwupdDevicePrivate *priv = GET_PRIVATE (device); - FwupdDevicePrivate *priv_parent = GET_PRIVATE (parent); g_return_if_fail (FWUPD_IS_DEVICE (device)); - g_set_object (&priv->parent, parent); - g_ptr_array_add (priv_parent->children, g_object_ref (device)); + + if (priv->parent != NULL) + g_object_remove_weak_pointer (G_OBJECT (priv->parent), (gpointer *) &priv->parent); + if (parent != NULL) + g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &priv->parent); + priv->parent = parent; + + /* this is what goes over D-Bus */ + fwupd_device_set_parent_id (device, parent != NULL ? fwupd_device_get_id (parent) : NULL); +} + +/** + * fwupd_device_add_child: + * @self: A #FwupdDevice + * @child: Another #FwupdDevice + * + * Adds a child device. An child device is logically linked to the primary + * device in some way. + * + * Since: 1.5.1 + **/ +void +fwupd_device_add_child (FwupdDevice *device, FwupdDevice *child) +{ + FwupdDevicePrivate *priv = GET_PRIVATE (device); + + /* add if the child does not already exist */ + for (guint i = 0; i < priv->children->len; i++) { + FwupdDevice *devtmp = g_ptr_array_index (priv->children, i); + if (devtmp == child) + return; + } + g_ptr_array_add (priv->children, g_object_ref (child)); } /** @@ -1098,7 +1166,6 @@ fwupd_device_get_created (FwupdDevice *device) return priv->created; } - /** * fwupd_device_set_created: * @device: A #FwupdDevice @@ -1190,6 +1257,8 @@ fwupd_device_incorporate (FwupdDevice *self, FwupdDevice *donor) fwupd_device_set_serial (self, priv_donor->serial); if (priv->summary == NULL) fwupd_device_set_summary (self, priv_donor->summary); + if (priv->branch == NULL) + fwupd_device_set_branch (self, priv_donor->branch); if (priv->vendor == NULL) fwupd_device_set_vendor (self, priv_donor->vendor); if (priv->vendor_id == NULL) @@ -1325,6 +1394,11 @@ fwupd_device_to_variant_full (FwupdDevice *device, FwupdDeviceFlags flags) FWUPD_RESULT_KEY_SUMMARY, g_variant_new_string (priv->summary)); } + if (priv->branch != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_BRANCH, + g_variant_new_string (priv->branch)); + } if (priv->checksums->len > 0) { g_autoptr(GString) str = g_string_new (""); for (guint i = 0; i < priv->checksums->len; i++) { @@ -1537,6 +1611,10 @@ fwupd_device_from_key_value (FwupdDevice *device, const gchar *key, GVariant *va fwupd_device_set_summary (device, g_variant_get_string (value, NULL)); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_BRANCH) == 0) { + fwupd_device_set_branch (device, g_variant_get_string (value, NULL)); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_DESCRIPTION) == 0) { fwupd_device_set_description (device, g_variant_get_string (value, NULL)); return; @@ -2037,6 +2115,7 @@ fwupd_device_to_json (FwupdDevice *device, JsonBuilder *builder) fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_SERIAL, priv->serial); fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_SUMMARY, priv->summary); fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_BRANCH, priv->branch); fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_PLUGIN, priv->plugin); fwupd_device_json_add_string (builder, FWUPD_RESULT_KEY_PROTOCOL, priv->protocol); if (priv->flags != FWUPD_DEVICE_FLAG_NONE) { @@ -2173,6 +2252,7 @@ fwupd_device_to_string (FwupdDevice *device) fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_SERIAL, priv->serial); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_SUMMARY, priv->summary); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_BRANCH, priv->branch); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_PLUGIN, priv->plugin); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_PROTOCOL, priv->protocol); fwupd_pad_kv_dfl (str, FWUPD_RESULT_KEY_FLAGS, priv->flags); @@ -2248,6 +2328,9 @@ fwupd_device_get_property (GObject *object, guint prop_id, case PROP_STATUS: g_value_set_uint (value, priv->status); break; + case PROP_PARENT: + g_value_set_object (value, priv->parent); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2272,6 +2355,9 @@ fwupd_device_set_property (GObject *object, guint prop_id, case PROP_STATUS: fwupd_device_set_status (self, g_value_get_uint (value)); break; + case PROP_PARENT: + fwupd_device_set_parent (self, g_value_get_object (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2316,6 +2402,13 @@ fwupd_device_class_init (FwupdDeviceClass *klass) G_PARAM_READWRITE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_STATUS, pspec); + + pspec = g_param_spec_object ("parent", NULL, NULL, + FWUPD_TYPE_DEVICE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_PARENT, pspec); } static void @@ -2337,13 +2430,14 @@ fwupd_device_finalize (GObject *object) FwupdDevicePrivate *priv = GET_PRIVATE (device); if (priv->parent != NULL) - g_object_unref (priv->parent); + g_object_remove_weak_pointer (G_OBJECT (priv->parent), (gpointer *) &priv->parent); g_free (priv->description); g_free (priv->id); g_free (priv->parent_id); g_free (priv->name); g_free (priv->serial); g_free (priv->summary); + g_free (priv->branch); g_free (priv->vendor); g_free (priv->vendor_id); g_free (priv->plugin); @@ -2439,8 +2533,10 @@ fwupd_device_array_ensure_parents (GPtrArray *devices) if (parent_id != NULL) { FwupdDevice *dev_tmp; dev_tmp = g_hash_table_lookup (devices_by_id, parent_id); - if (dev_tmp != NULL) + if (dev_tmp != NULL) { + fwupd_device_add_child (dev_tmp, dev); fwupd_device_set_parent (dev, dev_tmp); + } } } } diff --git a/libfwupd/fwupd-device.h b/libfwupd/fwupd-device.h index 4dfd593da..0744fd8a0 100644 --- a/libfwupd/fwupd-device.h +++ b/libfwupd/fwupd-device.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017 Richard Hughes + * Copyright (C) 2015-2020 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -41,6 +41,8 @@ void fwupd_device_set_parent_id (FwupdDevice *device, FwupdDevice *fwupd_device_get_parent (FwupdDevice *device); void fwupd_device_set_parent (FwupdDevice *device, FwupdDevice *parent); +void fwupd_device_add_child (FwupdDevice *device, + FwupdDevice *child); GPtrArray *fwupd_device_get_children (FwupdDevice *device); const gchar *fwupd_device_get_name (FwupdDevice *device); void fwupd_device_set_name (FwupdDevice *device, @@ -51,6 +53,9 @@ void fwupd_device_set_serial (FwupdDevice *device, const gchar *fwupd_device_get_summary (FwupdDevice *device); void fwupd_device_set_summary (FwupdDevice *device, const gchar *summary); +const gchar *fwupd_device_get_branch (FwupdDevice *device); +void fwupd_device_set_branch (FwupdDevice *device, + const gchar *branch); const gchar *fwupd_device_get_description (FwupdDevice *device); void fwupd_device_set_description (FwupdDevice *device, const gchar *description); diff --git a/libfwupd/fwupd-enums-private.h b/libfwupd/fwupd-enums-private.h index 067d2a1f3..7176d2d89 100644 --- a/libfwupd/fwupd-enums-private.h +++ b/libfwupd/fwupd-enums-private.h @@ -1,11 +1,13 @@ /* - * Copyright (C) 2016-2018 Richard Hughes + * Copyright (C) 2016-2020 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include + G_BEGIN_DECLS #define FWUPD_RESULT_KEY_APPSTREAM_ID "AppstreamId" /* s */ @@ -23,6 +25,8 @@ G_BEGIN_DECLS #define FWUPD_RESULT_KEY_FLAGS "Flags" /* t */ #define FWUPD_RESULT_KEY_FLASHES_LEFT "FlashesLeft" /* u */ #define FWUPD_RESULT_KEY_URGENCY "Urgency" /* u */ +#define FWUPD_RESULT_KEY_HSI_LEVEL "HsiLevel" /* u */ +#define FWUPD_RESULT_KEY_HSI_RESULT "HsiResult" /* u */ #define FWUPD_RESULT_KEY_INSTALL_DURATION "InstallDuration" /* u */ #define FWUPD_RESULT_KEY_GUID "Guid" /* as */ #define FWUPD_RESULT_KEY_INSTANCE_IDS "InstanceIds" /* as */ @@ -43,6 +47,7 @@ G_BEGIN_DECLS #define FWUPD_RESULT_KEY_SIZE "Size" /* t */ #define FWUPD_RESULT_KEY_STATUS "Status" /* u */ #define FWUPD_RESULT_KEY_SUMMARY "Summary" /* s */ +#define FWUPD_RESULT_KEY_BRANCH "Branch" /* s */ #define FWUPD_RESULT_KEY_TRUST_FLAGS "TrustFlags" /* t */ #define FWUPD_RESULT_KEY_UPDATE_MESSAGE "UpdateMessage" /* s */ #define FWUPD_RESULT_KEY_UPDATE_IMAGE "UpdateImage" /* s */ diff --git a/libfwupd/fwupd-enums.c b/libfwupd/fwupd-enums.c index 16d3356da..0cc32bd4d 100644 --- a/libfwupd/fwupd-enums.c +++ b/libfwupd/fwupd-enums.c @@ -197,6 +197,10 @@ fwupd_device_flag_to_string (FwupdDeviceFlags device_flag) return "updatable-hidden"; if (device_flag == FWUPD_DEVICE_FLAG_SKIPS_RESTART) return "skips-restart"; + if (device_flag == FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES) + return "has-multiple-branches"; + if (device_flag == FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL) + return "backup-before-install"; if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) return "unknown"; return NULL; @@ -295,6 +299,88 @@ fwupd_device_flag_from_string (const gchar *device_flag) return FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN; if (g_strcmp0 (device_flag, "skips-restart") == 0) return FWUPD_DEVICE_FLAG_SKIPS_RESTART; + if (g_strcmp0 (device_flag, "has-multiple-branches") == 0) + return FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES; + if (g_strcmp0 (device_flag, "backup-before-install") == 0) + return FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL; + return FWUPD_DEVICE_FLAG_UNKNOWN; +} + +/** + * fwupd_plugin_flag_to_string: + * @plugin_flag: A #FwupdPluginFlags, e.g. %FWUPD_DEVICE_FLAG_REQUIRE_AC + * + * Converts a #FwupdDeviceFlags to a string. + * + * Return value: identifier string + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_plugin_flag_to_string (FwupdPluginFlags plugin_flag) +{ + if (plugin_flag == FWUPD_DEVICE_FLAG_NONE) + return "none"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_DISABLED) + return "disabled"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_USER_WARNING) + return "user-warning"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE) + return "clear-updatable"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_NO_HARDWARE) + return "no-hardware"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED) + return "capsules-unsupported"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED) + return "unlock-required"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED) + return "efivar-not-mounted"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND) + return "esp-not-found"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_LEGACY_BIOS) + return "legacy-bios"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_FAILED_OPEN) + return "failed-open"; + if (plugin_flag == FWUPD_DEVICE_FLAG_UNKNOWN) + return "unknown"; + return NULL; +} + +/** + * fwupd_plugin_flag_from_string: + * @plugin_flag: A string, e.g. `require-ac` + * + * Converts a string to a #FwupdPluginFlags. + * + * Return value: enumerated value + * + * Since: 1.5.0 + **/ +FwupdPluginFlags +fwupd_plugin_flag_from_string (const gchar *plugin_flag) +{ + if (g_strcmp0 (plugin_flag, "none") == 0) + return FWUPD_DEVICE_FLAG_NONE; + if (g_strcmp0 (plugin_flag, "disabled") == 0) + return FWUPD_PLUGIN_FLAG_DISABLED; + if (g_strcmp0 (plugin_flag, "user-warning") == 0) + return FWUPD_PLUGIN_FLAG_USER_WARNING; + if (g_strcmp0 (plugin_flag, "clear-updatable") == 0) + return FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE; + if (g_strcmp0 (plugin_flag, "no-hardware") == 0) + return FWUPD_PLUGIN_FLAG_NO_HARDWARE; + if (g_strcmp0 (plugin_flag, "capsules-unsupported") == 0) + return FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED; + if (g_strcmp0 (plugin_flag, "unlock-required") == 0) + return FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED; + if (g_strcmp0 (plugin_flag, "efivar-not-mounted") == 0) + return FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED; + if (g_strcmp0 (plugin_flag, "esp-not-found") == 0) + return FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND; + if (g_strcmp0 (plugin_flag, "legacy-bios") == 0) + return FWUPD_PLUGIN_FLAG_LEGACY_BIOS; + if (g_strcmp0 (plugin_flag, "failed-open") == 0) + return FWUPD_PLUGIN_FLAG_FAILED_OPEN; return FWUPD_DEVICE_FLAG_UNKNOWN; } @@ -419,6 +505,8 @@ fwupd_feature_flag_to_string (FwupdFeatureFlags feature_flag) return "detach-action"; if (feature_flag == FWUPD_FEATURE_FLAG_UPDATE_ACTION) return "update-action"; + if (feature_flag == FWUPD_FEATURE_FLAG_SWITCH_BRANCH) + return "switch-branch"; return NULL; } @@ -443,6 +531,8 @@ fwupd_feature_flag_from_string (const gchar *feature_flag) return FWUPD_FEATURE_FLAG_DETACH_ACTION; if (g_strcmp0 (feature_flag, "update-action") == 0) return FWUPD_FEATURE_FLAG_UPDATE_ACTION; + if (g_strcmp0 (feature_flag, "switch-branch") == 0) + return FWUPD_FEATURE_FLAG_SWITCH_BRANCH; return FWUPD_FEATURE_FLAG_LAST; } @@ -521,6 +611,8 @@ fwupd_release_flag_to_string (FwupdReleaseFlags release_flag) return "blocked-version"; if (release_flag == FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL) return "blocked-approval"; + if (release_flag == FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH) + return "is-alternate-branch"; return NULL; } @@ -549,6 +641,8 @@ fwupd_release_flag_from_string (const gchar *release_flag) return FWUPD_RELEASE_FLAG_BLOCKED_VERSION; if (g_strcmp0 (release_flag, "blocked-approval") == 0) return FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL; + if (g_strcmp0 (release_flag, "is-alternate-branch") == 0) + return FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH; return FWUPD_RELEASE_FLAG_NONE; } @@ -578,7 +672,7 @@ fwupd_release_urgency_to_string (FwupdReleaseUrgency release_urgency) /** * fwupd_release_urgency_from_string: - * @release_urgency: A string, e.g. `trusted-payload` + * @release_urgency: A string, e.g. `low` * * Converts a string to an enumerated value. * diff --git a/libfwupd/fwupd-enums.h b/libfwupd/fwupd-enums.h index a3d004d60..0f0fcb0a0 100644 --- a/libfwupd/fwupd-enums.h +++ b/libfwupd/fwupd-enums.h @@ -70,6 +70,7 @@ typedef enum { * @FWUPD_FEATURE_FLAG_CAN_REPORT: Can upload a report of the update back to the server * @FWUPD_FEATURE_FLAG_DETACH_ACTION: Can perform detach action, typically showing text * @FWUPD_FEATURE_FLAG_UPDATE_ACTION: Can perform update action, typically showing text + * @FWUPD_FEATURE_FLAG_SWITCH_BRANCH: Can switch the firmware branch * * The flags to the feature capabilities of the front-end client. **/ @@ -78,6 +79,7 @@ typedef enum { FWUPD_FEATURE_FLAG_CAN_REPORT = 1 << 0, /* Since: 1.4.5 */ FWUPD_FEATURE_FLAG_DETACH_ACTION = 1 << 1, /* Since: 1.4.5 */ FWUPD_FEATURE_FLAG_UPDATE_ACTION = 1 << 2, /* Since: 1.4.5 */ + FWUPD_FEATURE_FLAG_SWITCH_BRANCH = 1 << 3, /* Since: 1.5.0 */ /*< private >*/ FWUPD_FEATURE_FLAG_LAST } FwupdFeatureFlags; @@ -88,7 +90,7 @@ typedef enum { * @FWUPD_DEVICE_FLAG_INTERNAL: Device cannot be removed easily * @FWUPD_DEVICE_FLAG_UPDATABLE: Device is updatable in this or any other mode * @FWUPD_DEVICE_FLAG_ONLY_OFFLINE: Update can only be done from offline mode - * @FWUPD_DEVICE_FLAG_REQUIRE_AC: Requires AC power + * @FWUPD_DEVICE_FLAG_REQUIRE_AC: System requires external power source * @FWUPD_DEVICE_FLAG_LOCKED: Is locked and can be unlocked * @FWUPD_DEVICE_FLAG_SUPPORTED: Is found in current metadata * @FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER: Requires a bootloader mode to be manually enabled by the user @@ -124,6 +126,8 @@ typedef enum { * @FWUPD_DEVICE_FLAG_NO_GUID_MATCHING: Force an explicit ID match when adding devices to the device list * @FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN: Device is updatable but should not be called by the client * @FWUPD_DEVICE_FLAG_SKIPS_RESTART: Device relies upon activation or power cycle to load firmware + * @FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES: Device supports switching to a different stream of firmware + * @FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL: Device firmware should be saved before installing firmware * * The device flags. **/ @@ -166,7 +170,9 @@ typedef enum { #define FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS (1llu << 35) /* Since: 1.4.0 */ #define FWUPD_DEVICE_FLAG_NO_GUID_MATCHING (1llu << 36) /* Since: 1.4.1 */ #define FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN (1llu << 37) /* Since: 1.4.1 */ -#define FWUPD_DEVICE_FLAG_SKIPS_RESTART (1llu << 38) /* Since: 1.4.5 */ +#define FWUPD_DEVICE_FLAG_SKIPS_RESTART (1llu << 38) /* Since: 1.5.0 */ +#define FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES (1llu << 39) /* Since: 1.5.0 */ +#define FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL (1llu << 40) /* Since: 1.5.0 */ #define FWUPD_DEVICE_FLAG_UNKNOWN G_MAXUINT64 /* Since: 0.7.3 */ typedef guint64 FwupdDeviceFlags; @@ -179,6 +185,7 @@ typedef guint64 FwupdDeviceFlags; * @FWUPD_RELEASE_FLAG_IS_DOWNGRADE: Is older than the device version * @FWUPD_RELEASE_FLAG_BLOCKED_VERSION: Blocked as below device version-lowest * @FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL: Blocked as release not approved + * @FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH: Is an alternate branch of firmware * * The release flags. **/ @@ -189,6 +196,7 @@ typedef guint64 FwupdDeviceFlags; #define FWUPD_RELEASE_FLAG_IS_DOWNGRADE (1u << 3) /* Since: 1.2.6 */ #define FWUPD_RELEASE_FLAG_BLOCKED_VERSION (1u << 4) /* Since: 1.2.6 */ #define FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL (1u << 5) /* Since: 1.2.6 */ +#define FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH (1u << 6) /* Since: 1.5.0 */ #define FWUPD_RELEASE_FLAG_UNKNOWN G_MAXUINT64 /* Since: 1.2.6 */ typedef guint64 FwupdReleaseFlags; @@ -212,6 +220,36 @@ typedef enum { FWUPD_RELEASE_URGENCY_LAST } FwupdReleaseUrgency; +/** + * FwupdPluginFlags: + * @FWUPD_PLUGIN_FLAG_NONE: No flags set + * @FWUPD_PLUGIN_FLAG_DISABLED: Disabled + * @FWUPD_PLUGIN_FLAG_USER_WARNING: Show the user a warning + * @FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE: Clear the UPDATABLE flag from devices + * @FWUPD_PLUGIN_FLAG_NO_HARDWARE: No hardware is found + * @FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED: UEFI UpdateCapsule are unsupported + * @FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED: Hardware unlock is required + * @FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED: The efivar filesystem is not found + * @FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND: The EFI ESP not found + * @FWUPD_PLUGIN_FLAG_LEGACY_BIOS: System running in legacy CSM mode + * @FWUPD_PLUGIN_FLAG_FAILED_OPEN: Failed to open plugin (missing dependency) + * + * The plugin flags. + **/ +#define FWUPD_PLUGIN_FLAG_NONE (0u) /* Since: 1.5.0 */ +#define FWUPD_PLUGIN_FLAG_DISABLED (1u << 0) /* Since: 1.5.0 */ +#define FWUPD_PLUGIN_FLAG_USER_WARNING (1u << 1) /* Since: 1.5.0 */ +#define FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE (1u << 2) /* Since: 1.5.0 */ +#define FWUPD_PLUGIN_FLAG_NO_HARDWARE (1u << 3) /* Since: 1.5.0 */ +#define FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED (1u << 4) /* Since: 1.5.0 */ +#define FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED (1u << 5) /* Since: 1.5.0 */ +#define FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED (1u << 6) /* Since: 1.5.0 */ +#define FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND (1u << 7) /* Since: 1.5.0 */ +#define FWUPD_PLUGIN_FLAG_LEGACY_BIOS (1u << 8) /* Since: 1.5.0 */ +#define FWUPD_PLUGIN_FLAG_FAILED_OPEN (1u << 9) /* Since: 1.5.0 */ +#define FWUPD_PLUGIN_FLAG_UNKNOWN G_MAXUINT64 /* Since: 1.5.0 */ +typedef guint64 FwupdPluginFlags; + /** * FwupdInstallFlags: * @FWUPD_INSTALL_FLAG_NONE: No flags set @@ -220,8 +258,12 @@ typedef enum { * @FWUPD_INSTALL_FLAG_ALLOW_OLDER: Allow downgrading firmware * @FWUPD_INSTALL_FLAG_FORCE: Force the update even if not a good idea * @FWUPD_INSTALL_FLAG_NO_HISTORY: Do not write to the history database + * @FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH: Allow firmware branch switching + * @FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM: Ignore firmware CRCs and checksums + * @FWUPD_INSTALL_FLAG_IGNORE_VID_PID: Ignore firmware vendor and project checks + * @FWUPD_INSTALL_FLAG_IGNORE_POWER: Ignore requirement of external power source * - * Flags to set when performing the firwmare update or install. + * Flags to set when performing the firmware update or install. **/ typedef enum { FWUPD_INSTALL_FLAG_NONE = 0, /* Since: 0.7.0 */ @@ -230,6 +272,10 @@ typedef enum { FWUPD_INSTALL_FLAG_ALLOW_OLDER = 1 << 2, /* Since: 0.7.0 */ FWUPD_INSTALL_FLAG_FORCE = 1 << 3, /* Since: 0.7.1 */ FWUPD_INSTALL_FLAG_NO_HISTORY = 1 << 4, /* Since: 1.0.8 */ + FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH = 1 << 5, /* Since: 1.5.0 */ + FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM = 1 << 6, /* Since: 1.5.0 */ + FWUPD_INSTALL_FLAG_IGNORE_VID_PID = 1 << 7, /* Since: 1.5.0 */ + FWUPD_INSTALL_FLAG_IGNORE_POWER = 1 << 8, /* Since: 1.5.0 */ /*< private >*/ FWUPD_INSTALL_FLAG_LAST } FwupdInstallFlags; @@ -240,7 +286,7 @@ typedef enum { * @FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP: Add the timestamp to the detached signature * @FWUPD_SELF_SIGN_FLAG_ADD_CERT: Add the certificate to the detached signature * - * Flags to set when performing the firwmare update or install. + * Flags to set when performing the firmware update or install. **/ typedef enum { FWUPD_SELF_SIGN_FLAG_NONE = 0, /* Since: 1.2.6 */ @@ -335,6 +381,8 @@ const gchar *fwupd_status_to_string (FwupdStatus status); FwupdStatus fwupd_status_from_string (const gchar *status); const gchar *fwupd_device_flag_to_string (FwupdDeviceFlags device_flag); FwupdDeviceFlags fwupd_device_flag_from_string (const gchar *device_flag); +const gchar *fwupd_plugin_flag_to_string (FwupdPluginFlags plugin_flag); +FwupdPluginFlags fwupd_plugin_flag_from_string (const gchar *plugin_flag); const gchar *fwupd_release_flag_to_string (FwupdReleaseFlags release_flag); FwupdReleaseFlags fwupd_release_flag_from_string (const gchar *release_flag); const gchar *fwupd_release_urgency_to_string (FwupdReleaseUrgency release_urgency); diff --git a/libfwupd/fwupd-plugin-private.h b/libfwupd/fwupd-plugin-private.h new file mode 100644 index 000000000..8902ada43 --- /dev/null +++ b/libfwupd/fwupd-plugin-private.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-plugin.h" + +G_BEGIN_DECLS + +GVariant *fwupd_plugin_to_variant (FwupdPlugin *plugin); +void fwupd_plugin_to_json (FwupdPlugin *plugin, + JsonBuilder *builder); + +G_END_DECLS + diff --git a/libfwupd/fwupd-plugin.c b/libfwupd/fwupd-plugin.c new file mode 100644 index 000000000..e27820521 --- /dev/null +++ b/libfwupd/fwupd-plugin.c @@ -0,0 +1,484 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fwupd-enums-private.h" +#include "fwupd-plugin-private.h" + +/** + * SECTION:fwupd-plugin + * @short_description: a hardware plugin + * + * An object that represents a fwupd plugin. + * + * See also: #FwupdRelease + */ + +static void fwupd_plugin_finalize (GObject *object); + +typedef struct { + gchar *name; + guint64 flags; +} FwupdPluginPrivate; + +enum { + PROP_0, + PROP_NAME, + PROP_FLAGS, + PROP_LAST +}; + +G_DEFINE_TYPE_WITH_PRIVATE (FwupdPlugin, fwupd_plugin, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fwupd_plugin_get_instance_private (o)) + +/** + * fwupd_plugin_get_name: + * @plugin: A #FwupdPlugin + * + * Gets the plugin name. + * + * Returns: the plugin name, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_plugin_get_name (FwupdPlugin *plugin) +{ + FwupdPluginPrivate *priv = GET_PRIVATE (plugin); + g_return_val_if_fail (FWUPD_IS_PLUGIN (plugin), NULL); + return priv->name; +} + +/** + * fwupd_plugin_set_name: + * @plugin: A #FwupdPlugin + * @name: the plugin name, e.g. `bios` + * + * Sets the plugin name. + * + * Since: 1.5.0 + **/ +void +fwupd_plugin_set_name (FwupdPlugin *plugin, const gchar *name) +{ + FwupdPluginPrivate *priv = GET_PRIVATE (plugin); + g_return_if_fail (FWUPD_IS_PLUGIN (plugin)); + g_return_if_fail (name != NULL); + g_free (priv->name); + priv->name = g_strdup (name); + g_object_notify (G_OBJECT (plugin), "name"); +} + +/** + * fwupd_plugin_get_flags: + * @plugin: A #FwupdPlugin + * + * Gets the plugin flags. + * + * Returns: the plugin flags, or 0 if unset + * + * Since: 1.5.0 + **/ +guint64 +fwupd_plugin_get_flags (FwupdPlugin *plugin) +{ + FwupdPluginPrivate *priv = GET_PRIVATE (plugin); + g_return_val_if_fail (FWUPD_IS_PLUGIN (plugin), 0); + return priv->flags; +} + +/** + * fwupd_plugin_set_flags: + * @plugin: A #FwupdPlugin + * @flags: the plugin flags, e.g. %FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED + * + * Sets the plugin flags. + * + * Since: 1.5.0 + **/ +void +fwupd_plugin_set_flags (FwupdPlugin *plugin, guint64 flags) +{ + FwupdPluginPrivate *priv = GET_PRIVATE (plugin); + g_return_if_fail (FWUPD_IS_PLUGIN (plugin)); + if (priv->flags == flags) + return; + priv->flags = flags; + g_object_notify (G_OBJECT (plugin), "flags"); +} + +/** + * fwupd_plugin_add_flag: + * @plugin: A #FwupdPlugin + * @flag: the #FwupdPluginFlags + * + * Adds a specific plugin flag to the plugin. + * + * Since: 1.5.0 + **/ +void +fwupd_plugin_add_flag (FwupdPlugin *plugin, FwupdPluginFlags flag) +{ + FwupdPluginPrivate *priv = GET_PRIVATE (plugin); + g_return_if_fail (FWUPD_IS_PLUGIN (plugin)); + if (flag == 0) + return; + if ((priv->flags & flag) > 0) + return; + priv->flags |= flag; + g_object_notify (G_OBJECT (plugin), "flags"); +} + +/** + * fwupd_plugin_remove_flag: + * @plugin: A #FwupdPlugin + * @flag: the #FwupdPluginFlags + * + * Removes a specific plugin flag from the plugin. + * + * Since: 1.5.0 + **/ +void +fwupd_plugin_remove_flag (FwupdPlugin *plugin, FwupdPluginFlags flag) +{ + FwupdPluginPrivate *priv = GET_PRIVATE (plugin); + g_return_if_fail (FWUPD_IS_PLUGIN (plugin)); + if (flag == 0) + return; + if ((priv->flags & flag) == 0) + return; + priv->flags &= ~flag; + g_object_notify (G_OBJECT (plugin), "flags"); +} + +/** + * fwupd_plugin_has_flag: + * @plugin: A #FwupdPlugin + * @flag: the #FwupdPluginFlags + * + * Finds if the plugin has a specific plugin flag. + * + * Returns: %TRUE if the flag is set + * + * Since: 1.5.0 + **/ +gboolean +fwupd_plugin_has_flag (FwupdPlugin *plugin, FwupdPluginFlags flag) +{ + FwupdPluginPrivate *priv = GET_PRIVATE (plugin); + g_return_val_if_fail (FWUPD_IS_PLUGIN (plugin), FALSE); + return (priv->flags & flag) > 0; +} + +/** + * fwupd_plugin_to_variant: + * @plugin: A #FwupdPlugin + * + * Creates a GVariant from the plugin data omitting sensitive fields + * + * Returns: the GVariant, or %NULL for error + * + * Since: 1.5.0 + **/ +GVariant * +fwupd_plugin_to_variant (FwupdPlugin *plugin) +{ + FwupdPluginPrivate *priv = GET_PRIVATE (plugin); + GVariantBuilder builder; + + g_return_val_if_fail (FWUPD_IS_PLUGIN (plugin), NULL); + + /* create an array with all the metadata in */ + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + if (priv->name != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_NAME, + g_variant_new_string (priv->name)); + } + if (priv->flags > 0) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_FLAGS, + g_variant_new_uint64 (priv->flags)); + } + return g_variant_new ("a{sv}", &builder); +} + +static void +fwupd_plugin_from_key_value (FwupdPlugin *plugin, const gchar *key, GVariant *value) +{ + if (g_strcmp0 (key, FWUPD_RESULT_KEY_NAME) == 0) { + fwupd_plugin_set_name (plugin, g_variant_get_string (value, NULL)); + return; + } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_FLAGS) == 0) { + fwupd_plugin_set_flags (plugin, g_variant_get_uint64 (value)); + return; + } +} + +static void +fwupd_pad_kv_str (GString *str, const gchar *key, const gchar *value) +{ + /* ignore */ + if (key == NULL || value == NULL) + return; + g_string_append_printf (str, " %s: ", key); + for (gsize i = strlen (key); i < 20; i++) + g_string_append (str, " "); + g_string_append_printf (str, "%s\n", value); +} + +static void +fwupd_pad_kv_dfl (GString *str, const gchar *key, guint64 plugin_flags) +{ + g_autoptr(GString) tmp = g_string_new (""); + for (guint i = 0; i < 64; i++) { + if ((plugin_flags & ((guint64) 1 << i)) == 0) + continue; + g_string_append_printf (tmp, "%s|", + fwupd_plugin_flag_to_string ((guint64) 1 << i)); + } + if (tmp->len == 0) { + g_string_append (tmp, fwupd_plugin_flag_to_string (0)); + } else { + g_string_truncate (tmp, tmp->len - 1); + } + fwupd_pad_kv_str (str, key, tmp->str); +} + +static void +fwupd_plugin_json_add_string (JsonBuilder *builder, const gchar *key, const gchar *str) +{ + if (str == NULL) + return; + json_builder_set_member_name (builder, key); + json_builder_add_string_value (builder, str); +} + +/** + * fwupd_plugin_to_json: + * @plugin: A #FwupdPlugin + * @builder: A #JsonBuilder + * + * Adds a fwupd plugin to a JSON builder + * + * Since: 1.5.0 + **/ +void +fwupd_plugin_to_json (FwupdPlugin *plugin, JsonBuilder *builder) +{ + FwupdPluginPrivate *priv = GET_PRIVATE (plugin); + + g_return_if_fail (FWUPD_IS_PLUGIN (plugin)); + g_return_if_fail (builder != NULL); + + fwupd_plugin_json_add_string (builder, FWUPD_RESULT_KEY_NAME, priv->name); + if (priv->flags != FWUPD_PLUGIN_FLAG_NONE) { + json_builder_set_member_name (builder, FWUPD_RESULT_KEY_FLAGS); + json_builder_begin_array (builder); + for (guint i = 0; i < 64; i++) { + const gchar *tmp; + if ((priv->flags & ((guint64) 1 << i)) == 0) + continue; + tmp = fwupd_plugin_flag_to_string ((guint64) 1 << i); + json_builder_add_string_value (builder, tmp); + } + json_builder_end_array (builder); + } +} + +/** + * fwupd_plugin_to_string: + * @plugin: A #FwupdPlugin + * + * Builds a text representation of the object. + * + * Returns: text, or %NULL for invalid + * + * Since: 1.5.0 + **/ +gchar * +fwupd_plugin_to_string (FwupdPlugin *plugin) +{ + FwupdPluginPrivate *priv = GET_PRIVATE (plugin); + GString *str; + + g_return_val_if_fail (FWUPD_IS_PLUGIN (plugin), NULL); + + str = g_string_new (NULL); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_NAME, priv->name); + fwupd_pad_kv_dfl (str, FWUPD_RESULT_KEY_FLAGS, priv->flags); + return g_string_free (str, FALSE); +} + +static void +fwupd_plugin_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + FwupdPlugin *self = FWUPD_PLUGIN (object); + FwupdPluginPrivate *priv = GET_PRIVATE (self); + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_FLAGS: + g_value_set_uint64 (value, priv->flags); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fwupd_plugin_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + FwupdPlugin *self = FWUPD_PLUGIN (object); + switch (prop_id) { + case PROP_NAME: + fwupd_plugin_set_name (self, g_value_get_string (value)); + break; + case PROP_FLAGS: + fwupd_plugin_set_flags (self, g_value_get_uint64 (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fwupd_plugin_class_init (FwupdPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + object_class->finalize = fwupd_plugin_finalize; + object_class->get_property = fwupd_plugin_get_property; + object_class->set_property = fwupd_plugin_set_property; + + pspec = g_param_spec_string ("name", NULL, NULL, NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_NAME, pspec); + + pspec = g_param_spec_uint64 ("flags", NULL, NULL, + FWUPD_PLUGIN_FLAG_NONE, + FWUPD_PLUGIN_FLAG_UNKNOWN, + FWUPD_PLUGIN_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_FLAGS, pspec); +} + +static void +fwupd_plugin_init (FwupdPlugin *plugin) +{ +} + +static void +fwupd_plugin_finalize (GObject *object) +{ + FwupdPlugin *plugin = FWUPD_PLUGIN (object); + FwupdPluginPrivate *priv = GET_PRIVATE (plugin); + g_free (priv->name); + G_OBJECT_CLASS (fwupd_plugin_parent_class)->finalize (object); +} + +static void +fwupd_plugin_set_from_variant_iter (FwupdPlugin *plugin, GVariantIter *iter) +{ + GVariant *value; + const gchar *key; + while (g_variant_iter_next (iter, "{&sv}", &key, &value)) { + fwupd_plugin_from_key_value (plugin, key, value); + g_variant_unref (value); + } +} + +/** + * fwupd_plugin_from_variant: + * @value: a #GVariant + * + * Creates a new plugin using packed data. + * + * Returns: (transfer full): a new #FwupdPlugin, or %NULL if @value was invalid + * + * Since: 1.5.0 + **/ +FwupdPlugin * +fwupd_plugin_from_variant (GVariant *value) +{ + FwupdPlugin *plugin = NULL; + const gchar *type_string; + g_autoptr(GVariantIter) iter = NULL; + + /* format from GetDetails */ + type_string = g_variant_get_type_string (value); + if (g_strcmp0 (type_string, "(a{sv})") == 0) { + plugin = fwupd_plugin_new (); + g_variant_get (value, "(a{sv})", &iter); + fwupd_plugin_set_from_variant_iter (plugin, iter); + } else if (g_strcmp0 (type_string, "a{sv}") == 0) { + plugin = fwupd_plugin_new (); + g_variant_get (value, "a{sv}", &iter); + fwupd_plugin_set_from_variant_iter (plugin, iter); + } else { + g_warning ("type %s not known", type_string); + } + return plugin; +} + +/** + * fwupd_plugin_array_from_variant: + * @value: a #GVariant + * + * Creates an array of new plugins using packed data. + * + * Returns: (transfer container) (element-type FwupdPlugin): plugins, or %NULL if @value was invalid + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_plugin_array_from_variant (GVariant *value) +{ + GPtrArray *array = NULL; + gsize sz; + g_autoptr(GVariant) untuple = NULL; + + array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + untuple = g_variant_get_child_value (value, 0); + sz = g_variant_n_children (untuple); + for (guint i = 0; i < sz; i++) { + FwupdPlugin *plugin; + g_autoptr(GVariant) data = NULL; + data = g_variant_get_child_value (untuple, i); + plugin = fwupd_plugin_from_variant (data); + if (plugin == NULL) + continue; + g_ptr_array_add (array, plugin); + } + return array; +} + +/** + * fwupd_plugin_new: + * + * Creates a new plugin. + * + * Returns: a new #FwupdPlugin + * + * Since: 1.5.0 + **/ +FwupdPlugin * +fwupd_plugin_new (void) +{ + FwupdPlugin *plugin; + plugin = g_object_new (FWUPD_TYPE_PLUGIN, NULL); + return FWUPD_PLUGIN (plugin); +} diff --git a/libfwupd/fwupd-plugin.h b/libfwupd/fwupd-plugin.h new file mode 100644 index 000000000..5d7cfaf12 --- /dev/null +++ b/libfwupd/fwupd-plugin.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-enums.h" + +G_BEGIN_DECLS + +#define FWUPD_TYPE_PLUGIN (fwupd_plugin_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FwupdPlugin, fwupd_plugin, FWUPD, PLUGIN, GObject) + +struct _FwupdPluginClass +{ + GObjectClass parent_class; + /*< private >*/ + void (*_fwupd_reserved1) (void); + void (*_fwupd_reserved2) (void); + void (*_fwupd_reserved3) (void); + void (*_fwupd_reserved4) (void); + void (*_fwupd_reserved5) (void); + void (*_fwupd_reserved6) (void); + void (*_fwupd_reserved7) (void); +}; + +FwupdPlugin *fwupd_plugin_new (void); +gchar *fwupd_plugin_to_string (FwupdPlugin *plugin); + +const gchar *fwupd_plugin_get_name (FwupdPlugin *plugin); +void fwupd_plugin_set_name (FwupdPlugin *plugin, + const gchar *name); +guint64 fwupd_plugin_get_flags (FwupdPlugin *plugin); +void fwupd_plugin_set_flags (FwupdPlugin *plugin, + guint64 flags); +void fwupd_plugin_add_flag (FwupdPlugin *plugin, + FwupdPluginFlags flag); +void fwupd_plugin_remove_flag (FwupdPlugin *plugin, + FwupdPluginFlags flag); +gboolean fwupd_plugin_has_flag (FwupdPlugin *plugin, + FwupdPluginFlags flag); + +FwupdPlugin *fwupd_plugin_from_variant (GVariant *value); +GPtrArray *fwupd_plugin_array_from_variant (GVariant *value); + +G_END_DECLS diff --git a/libfwupd/fwupd-release.c b/libfwupd/fwupd-release.c index 875b4e518..86cd3fcb7 100644 --- a/libfwupd/fwupd-release.c +++ b/libfwupd/fwupd-release.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2018 Richard Hughes + * Copyright (C) 2015-2020 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -46,6 +46,7 @@ typedef struct { gchar *name; gchar *name_variant_suffix; gchar *summary; + gchar *branch; gchar *uri; gchar *vendor; gchar *version; @@ -934,6 +935,42 @@ fwupd_release_set_summary (FwupdRelease *release, const gchar *summary) priv->summary = g_strdup (summary); } +/** + * fwupd_release_get_branch: + * @release: A #FwupdRelease + * + * Gets the update branch. + * + * Returns: the alternate branch, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_release_get_branch (FwupdRelease *release) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), NULL); + return priv->branch; +} + +/** + * fwupd_release_set_branch: + * @release: A #FwupdRelease + * @branch: the update one line branch + * + * Sets the alternate branch. + * + * Since: 1.5.0 + **/ +void +fwupd_release_set_branch (FwupdRelease *release, const gchar *branch) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + g_free (priv->branch); + priv->branch = g_strdup (branch); +} + /** * fwupd_release_get_vendor: * @release: A #FwupdRelease @@ -1354,6 +1391,11 @@ fwupd_release_to_variant (FwupdRelease *release) FWUPD_RESULT_KEY_SUMMARY, g_variant_new_string (priv->summary)); } + if (priv->branch != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_BRANCH, + g_variant_new_string (priv->branch)); + } if (priv->description != NULL) { g_variant_builder_add (&builder, "{sv}", FWUPD_RESULT_KEY_DESCRIPTION, @@ -1492,6 +1534,10 @@ fwupd_release_from_key_value (FwupdRelease *release, const gchar *key, GVariant fwupd_release_set_summary (release, g_variant_get_string (value, NULL)); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_BRANCH) == 0) { + fwupd_release_set_branch (release, g_variant_get_string (value, NULL)); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_DESCRIPTION) == 0) { fwupd_release_set_description (release, g_variant_get_string (value, NULL)); return; @@ -1675,6 +1721,7 @@ fwupd_release_to_json (FwupdRelease *release, JsonBuilder *builder) fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_REMOTE_ID, priv->remote_id); fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_SUMMARY, priv->summary); fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_BRANCH, priv->branch); fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_VERSION, priv->version); fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_FILENAME, priv->filename); fwupd_release_json_add_string (builder, FWUPD_RESULT_KEY_PROTOCOL, priv->protocol); @@ -1764,6 +1811,7 @@ fwupd_release_to_string (FwupdRelease *release) fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_REMOTE_ID, priv->remote_id); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_SUMMARY, priv->summary); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_BRANCH, priv->branch); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_VERSION, priv->version); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_FILENAME, priv->filename); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_PROTOCOL, priv->protocol); @@ -1844,6 +1892,7 @@ fwupd_release_finalize (GObject *object) g_free (priv->name); g_free (priv->name_variant_suffix); g_free (priv->summary); + g_free (priv->branch); g_free (priv->uri); g_free (priv->homepage); g_free (priv->details_url); diff --git a/libfwupd/fwupd-release.h b/libfwupd/fwupd-release.h index d00979047..051863101 100644 --- a/libfwupd/fwupd-release.h +++ b/libfwupd/fwupd-release.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2018 Richard Hughes + * Copyright (C) 2015-2020 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -90,6 +90,9 @@ void fwupd_release_set_name_variant_suffix (FwupdRelease *release, const gchar *fwupd_release_get_summary (FwupdRelease *release); void fwupd_release_set_summary (FwupdRelease *release, const gchar *summary); +const gchar *fwupd_release_get_branch (FwupdRelease *release); +void fwupd_release_set_branch (FwupdRelease *release, + const gchar *branch); const gchar *fwupd_release_get_description (FwupdRelease *release); void fwupd_release_set_description (FwupdRelease *release, const gchar *description); diff --git a/libfwupd/fwupd-remote.c b/libfwupd/fwupd-remote.c index 06c4b1a1f..f63a2668d 100644 --- a/libfwupd/fwupd-remote.c +++ b/libfwupd/fwupd-remote.c @@ -32,6 +32,7 @@ typedef struct { gchar *id; gchar *firmware_base_uri; gchar *report_uri; + gchar *security_report_uri; gchar *metadata_uri; gchar *metadata_uri_sig; gchar *username; @@ -50,6 +51,7 @@ typedef struct { gchar **order_before; gchar *remotes_dir; gboolean automatic_reports; + gboolean automatic_security_reports; } FwupdRemotePrivate; enum { @@ -58,6 +60,7 @@ enum { PROP_ENABLED, PROP_APPROVAL_REQUIRED, PROP_AUTOMATIC_REPORTS, + PROP_AUTOMATIC_SECURITY_REPORTS, PROP_LAST }; @@ -252,6 +255,13 @@ fwupd_remote_set_report_uri (FwupdRemote *self, const gchar *report_uri) priv->report_uri = g_strdup (report_uri); } +static void +fwupd_remote_set_security_report_uri (FwupdRemote *self, const gchar *security_report_uri) +{ + FwupdRemotePrivate *priv = GET_PRIVATE (self); + priv->security_report_uri = g_strdup (security_report_uri); +} + /** * fwupd_remote_kind_from_string: * @kind: a string, e.g. `download` @@ -343,6 +353,7 @@ fwupd_remote_load_from_filename (FwupdRemote *self, g_autofree gchar *order_after = NULL; g_autofree gchar *order_before = NULL; g_autofree gchar *report_uri = NULL; + g_autofree gchar *security_report_uri = NULL; g_autoptr(GKeyFile) kf = NULL; g_return_val_if_fail (FWUPD_IS_REMOTE (self), FALSE); @@ -415,8 +426,14 @@ fwupd_remote_load_from_filename (FwupdRemote *self, if (report_uri != NULL && report_uri[0] != '\0') fwupd_remote_set_report_uri (self, report_uri); + /* security reporting is optional */ + security_report_uri = g_key_file_get_string (kf, group, "SecurityReportURI", NULL); + if (security_report_uri != NULL && security_report_uri[0] != '\0') + fwupd_remote_set_security_report_uri (self, security_report_uri); + /* automatic report uploading */ priv->automatic_reports = g_key_file_get_boolean (kf, group, "AutomaticReports", NULL); + priv->automatic_security_reports = g_key_file_get_boolean (kf, group, "AutomaticSecurityReports", NULL); /* DOWNLOAD-type remotes */ if (priv->kind == FWUPD_REMOTE_KIND_DOWNLOAD) { @@ -879,6 +896,24 @@ fwupd_remote_get_report_uri (FwupdRemote *self) return priv->report_uri; } +/** + * fwupd_remote_get_security_report_uri: + * @self: A #FwupdRemote + * + * Gets the URI for the security report. + * + * Returns: (transfer none): a URI, or %NULL for invalid. + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_remote_get_security_report_uri (FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_REMOTE (self), NULL); + return priv->security_report_uri; +} + /** * fwupd_remote_get_metadata_uri: * @self: A #FwupdRemote @@ -1069,6 +1104,24 @@ fwupd_remote_get_automatic_reports (FwupdRemote *self) return priv->automatic_reports; } +/** + * fwupd_remote_get_automatic_security_reports: + * @self: A #FwupdRemote + * + * Gets if security reports should be automatically uploaded to this remote + * + * Returns: a #TRUE if the remote should have reports uploaded automatically + * + * Since: 1.5.0 + **/ +gboolean +fwupd_remote_get_automatic_security_reports (FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_REMOTE (self), FALSE); + return priv->automatic_security_reports; +} + /** * fwupd_remote_get_approval_required: * @self: A #FwupdRemote @@ -1133,6 +1186,8 @@ fwupd_remote_set_from_variant_iter (FwupdRemote *self, GVariantIter *iter) fwupd_remote_set_filename_source (self, g_variant_get_string (value, NULL)); if (g_strcmp0 (key, "ReportUri") == 0) fwupd_remote_set_report_uri (self, g_variant_get_string (value, NULL)); + if (g_strcmp0 (key, "SecurityReportUri") == 0) + fwupd_remote_set_security_report_uri (self, g_variant_get_string (value, NULL)); } while (g_variant_iter_loop (iter3, "{sv}", &key, &value)) { if (g_strcmp0 (key, "Username") == 0) { @@ -1157,6 +1212,8 @@ fwupd_remote_set_from_variant_iter (FwupdRemote *self, GVariantIter *iter) fwupd_remote_set_firmware_base_uri (self, g_variant_get_string (value, NULL)); } else if (g_strcmp0 (key, "AutomaticReports") == 0) { priv->automatic_reports = g_variant_get_boolean (value); + } else if (g_strcmp0 (key, "AutomaticSecurityReports") == 0) { + priv->automatic_security_reports = g_variant_get_boolean (value); } } } @@ -1213,6 +1270,10 @@ fwupd_remote_to_variant (FwupdRemote *self) g_variant_builder_add (&builder, "{sv}", "ReportUri", g_variant_new_string (priv->report_uri)); } + if (priv->security_report_uri != NULL) { + g_variant_builder_add (&builder, "{sv}", "SecurityReportUri", + g_variant_new_string (priv->security_report_uri)); + } if (priv->firmware_base_uri != NULL) { g_variant_builder_add (&builder, "{sv}", "FirmwareBaseUri", g_variant_new_string (priv->firmware_base_uri)); @@ -1251,6 +1312,8 @@ fwupd_remote_to_variant (FwupdRemote *self) g_variant_new_boolean (priv->approval_required)); g_variant_builder_add (&builder, "{sv}", "AutomaticReports", g_variant_new_boolean (priv->automatic_reports)); + g_variant_builder_add (&builder, "{sv}", "AutomaticSecurityReports", + g_variant_new_boolean (priv->automatic_security_reports)); return g_variant_new ("a{sv}", &builder); } @@ -1274,6 +1337,9 @@ fwupd_remote_get_property (GObject *obj, guint prop_id, case PROP_AUTOMATIC_REPORTS: g_value_set_boolean (value, priv->automatic_reports); break; + case PROP_AUTOMATIC_SECURITY_REPORTS: + g_value_set_boolean (value, priv->automatic_security_reports); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; @@ -1300,6 +1366,9 @@ fwupd_remote_set_property (GObject *obj, guint prop_id, case PROP_AUTOMATIC_REPORTS: priv->automatic_reports = g_value_get_boolean (value); break; + case PROP_AUTOMATIC_SECURITY_REPORTS: + priv->automatic_security_reports = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; @@ -1360,6 +1429,17 @@ fwupd_remote_class_init (FwupdRemoteClass *klass) FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_AUTOMATIC_REPORTS, pspec); + /** + * FwupdRemote:automatic-security-reports: + * + * The behavior for auto-uploading security reports. + * + * Since: 1.5.0 + */ + pspec = g_param_spec_boolean ("automatic-security-reports", NULL, NULL, + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_AUTOMATIC_SECURITY_REPORTS, pspec); + } static void @@ -1378,6 +1458,7 @@ fwupd_remote_finalize (GObject *obj) g_free (priv->metadata_uri_sig); g_free (priv->firmware_base_uri); g_free (priv->report_uri); + g_free (priv->security_report_uri); g_free (priv->username); g_free (priv->password); g_free (priv->title); diff --git a/libfwupd/fwupd-remote.h b/libfwupd/fwupd-remote.h index 2a0ac8d5c..1794040b6 100644 --- a/libfwupd/fwupd-remote.h +++ b/libfwupd/fwupd-remote.h @@ -60,11 +60,13 @@ const gchar *fwupd_remote_get_filename_cache_sig (FwupdRemote *self); const gchar *fwupd_remote_get_filename_source (FwupdRemote *self); const gchar *fwupd_remote_get_firmware_base_uri (FwupdRemote *self); const gchar *fwupd_remote_get_report_uri (FwupdRemote *self); +const gchar *fwupd_remote_get_security_report_uri (FwupdRemote *self); const gchar *fwupd_remote_get_metadata_uri (FwupdRemote *self); const gchar *fwupd_remote_get_metadata_uri_sig (FwupdRemote *self); gboolean fwupd_remote_get_enabled (FwupdRemote *self); gboolean fwupd_remote_get_approval_required (FwupdRemote *self); gboolean fwupd_remote_get_automatic_reports (FwupdRemote *self); +gboolean fwupd_remote_get_automatic_security_reports (FwupdRemote *self); gint fwupd_remote_get_priority (FwupdRemote *self); guint64 fwupd_remote_get_age (FwupdRemote *self); FwupdRemoteKind fwupd_remote_get_kind (FwupdRemote *self); diff --git a/libfwupd/fwupd-security-attr-private.h b/libfwupd/fwupd-security-attr-private.h new file mode 100644 index 000000000..ad88826ec --- /dev/null +++ b/libfwupd/fwupd-security-attr-private.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +#include "fwupd-security-attr.h" + +G_BEGIN_DECLS + +#define FWUPD_SECURITY_ATTR_ID_ACPI_DMAR "org.fwupd.hsi.AcpiDmar" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM "org.fwupd.hsi.EncryptedRam" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_FWUPD_ATTESTATION "org.fwupd.hsi.Fwupd.Attestation" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS "org.fwupd.hsi.Fwupd.Plugins" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_FWUPD_UPDATES "org.fwupd.hsi.Fwupd.Updates" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED "org.fwupd.hsi.IntelBootguard.Enabled" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED "org.fwupd.hsi.IntelBootguard.Verified" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM "org.fwupd.hsi.IntelBootguard.Acm" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY "org.fwupd.hsi.IntelBootguard.Policy" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP "org.fwupd.hsi.IntelBootguard.Otp" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_INTEL_CET_ENABLED "org.fwupd.hsi.IntelCet.Enabled" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_INTEL_CET_ACTIVE "org.fwupd.hsi.IntelCet.Active" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_INTEL_SMAP "org.fwupd.hsi.IntelSmap" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_IOMMU "org.fwupd.hsi.Iommu" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN "org.fwupd.hsi.Kernel.Lockdown" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP "org.fwupd.hsi.Kernel.Swap" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED "org.fwupd.hsi.Kernel.Tainted" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE "org.fwupd.hsi.Mei.ManufacturingMode" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP "org.fwupd.hsi.Mei.OverrideStrap" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_MEI_VERSION "org.fwupd.hsi.Mei.Version" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE "org.fwupd.hsi.Spi.Bioswe" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_SPI_BLE "org.fwupd.hsi.Spi.Ble" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP "org.fwupd.hsi.Spi.SmmBwp" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE "org.fwupd.hsi.SuspendToIdle" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM "org.fwupd.hsi.SuspendToRam" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0 "org.fwupd.hsi.Tpm.ReconstructionPcr0" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20 "org.fwupd.hsi.Tpm.Version20" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT "org.fwupd.hsi.Uefi.SecureBoot" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_INTEL_DCI_ENABLED "org.fwupd.hsi.IntelDci.Enabled" /* Since: 1.5.0 */ +#define FWUPD_SECURITY_ATTR_ID_INTEL_DCI_LOCKED "org.fwupd.hsi.IntelDci.Locked" /* Since: 1.5.0 */ + +GVariant *fwupd_security_attr_to_variant (FwupdSecurityAttr *self); +void fwupd_security_attr_to_json (FwupdSecurityAttr *self, + JsonBuilder *builder); + +G_END_DECLS + diff --git a/libfwupd/fwupd-security-attr.c b/libfwupd/fwupd-security-attr.c new file mode 100644 index 000000000..1adeabd73 --- /dev/null +++ b/libfwupd/fwupd-security-attr.c @@ -0,0 +1,916 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fwupd-common-private.h" +#include "fwupd-enums-private.h" +#include "fwupd-security-attr-private.h" + +/** + * SECTION:fwupd-security-attr + * + * An object that represents an Host Security ID attribute. + */ + +static void fwupd_security_attr_finalize (GObject *object); + +typedef struct { + gchar *appstream_id; + GPtrArray *obsoletes; + GHashTable *metadata; /* (nullable) */ + gchar *name; + gchar *plugin; + gchar *url; + FwupdSecurityAttrLevel level; + FwupdSecurityAttrResult result; + FwupdSecurityAttrFlags flags; +} FwupdSecurityAttrPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (FwupdSecurityAttr, fwupd_security_attr, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fwupd_security_attr_get_instance_private (o)) + +/** + * fwupd_security_attr_flag_to_string: + * @flag: A #FwupdSecurityAttrFlags, e.g. %FWUPD_SECURITY_ATTR_FLAG_SUCCESS + * + * Returns the printable string for the flag. + * + * Returns: string, or %NULL + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_flag_to_string (FwupdSecurityAttrFlags flag) +{ + if (flag == FWUPD_SECURITY_ATTR_FLAG_NONE) + return "none"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_SUCCESS) + return "success"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_OBSOLETED) + return "obsoleted"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES) + return "runtime-updates"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION) + return "runtime-attestation"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) + return "runtime-issue"; + return NULL; +} + +/** + * fwupd_security_attr_result_to_string: + * @result: A #FwupdSecurityAttrResult, e.g. %FWUPD_SECURITY_ATTR_RESULT_ENABLED + * + * Returns the printable string for the result enum. + * + * Returns: string, or %NULL + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_result_to_string (FwupdSecurityAttrResult result) +{ + if (result == FWUPD_SECURITY_ATTR_RESULT_VALID) + return "valid"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_VALID) + return "not-valid"; + if (result == FWUPD_SECURITY_ATTR_RESULT_ENABLED) + return "enabled"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED) + return "not-enabled"; + if (result == FWUPD_SECURITY_ATTR_RESULT_LOCKED) + return "locked"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED) + return "not-locked"; + if (result == FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED) + return "encrypted"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED) + return "not-encrypted"; + if (result == FWUPD_SECURITY_ATTR_RESULT_TAINTED) + return "tainted"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED) + return "not-tainted"; + if (result == FWUPD_SECURITY_ATTR_RESULT_FOUND) + return "found"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND) + return "not-found"; + if (result == FWUPD_SECURITY_ATTR_RESULT_SUPPORTED) + return "supported"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED) + return "not-supported"; + return NULL; +} + +/** + * fwupd_security_attr_flag_to_suffix: + * @flag: A #FwupdSecurityAttrFlags, e.g. %FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES + * + * Returns the string suffix for the flag. + * + * Returns: string, or %NULL + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_flag_to_suffix (FwupdSecurityAttrFlags flag) +{ + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES) + return "U"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION) + return "A"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) + return "!"; + return NULL; +} + +/** + * fwupd_security_attr_get_obsoletes: + * @self: A #FwupdSecurityAttr + * + * Gets the list of attribute obsoletes. The obsoleted attributes will not + * contribute to the calculated HSI value or be visible in command line tools. + * + * Returns: (element-type utf8) (transfer none): the obsoletes, which may be empty + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_security_attr_get_obsoletes (FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), NULL); + return priv->obsoletes; +} + +/** + * fwupd_security_attr_add_obsolete: + * @self: A #FwupdSecurityAttr + * @appstream_id: the appstream_id or plugin name + * + * Adds an attribute appstream_id to obsolete. The obsoleted attribute will not + * contribute to the calculated HSI value or be visible in command line tools. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_add_obsolete (FwupdSecurityAttr *self, const gchar *appstream_id) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (self)); + g_return_if_fail (appstream_id != NULL); + if (fwupd_security_attr_has_obsolete (self, appstream_id)) + return; + g_ptr_array_add (priv->obsoletes, g_strdup (appstream_id)); +} + +/** + * fwupd_security_attr_has_obsolete: + * @self: A #FwupdSecurityAttr + * @appstream_id: the attribute appstream_id + * + * Finds out if the attribute obsoletes a specific appstream_id. + * + * Returns: %TRUE if the self matches + * + * Since: 1.5.0 + **/ +gboolean +fwupd_security_attr_has_obsolete (FwupdSecurityAttr *self, const gchar *appstream_id) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), FALSE); + g_return_val_if_fail (appstream_id != NULL, FALSE); + for (guint i = 0; i < priv->obsoletes->len; i++) { + const gchar *obsolete_tmp = g_ptr_array_index (priv->obsoletes, i); + if (g_strcmp0 (obsolete_tmp, appstream_id) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fwupd_security_attr_get_appstream_id: + * @self: A #FwupdSecurityAttr + * + * Gets the AppStream ID. + * + * Returns: the AppStream ID, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_get_appstream_id (FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), NULL); + return priv->appstream_id; +} + +/** + * fwupd_security_attr_set_appstream_id: + * @self: A #FwupdSecurityAttr + * @appstream_id: the AppStream component ID, e.g. `com.intel.BiosGuard` + * + * Sets the AppStream ID. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_appstream_id (FwupdSecurityAttr *self, const gchar *appstream_id) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (self)); + + /* sanity check */ + if (!g_str_has_prefix (appstream_id, "org.fwupd.hsi.")) + g_critical ("HSI attributes need to have a 'org.fwupd.hsi.' prefix"); + + g_free (priv->appstream_id); + priv->appstream_id = g_strdup (appstream_id); +} + +/** + * fwupd_security_attr_get_url: + * @self: A #FwupdSecurityAttr + * + * Gets the attribute URL. + * + * Returns: the attribute result, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_get_url (FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), NULL); + return priv->url; +} + +/** + * fwupd_security_attr_set_name: + * @self: A #FwupdSecurityAttr + * @name: the attribute name + * + * Sets the attribute name. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_name (FwupdSecurityAttr *self, const gchar *name) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (self)); + g_free (priv->name); + priv->name = g_strdup (name); +} + +/** + * fwupd_security_attr_set_plugin: + * @self: A #FwupdSecurityAttr + * @plugin: the plugin name + * + * Sets the plugin that created the attribute. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_plugin (FwupdSecurityAttr *self, const gchar *plugin) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (self)); + g_free (priv->plugin); + priv->plugin = g_strdup (plugin); +} + +/** + * fwupd_security_attr_set_url: + * @self: A #FwupdSecurityAttr + * @url: the attribute URL + * + * Sets the attribute result. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_url (FwupdSecurityAttr *self, const gchar *url) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (self)); + g_free (priv->url); + priv->url = g_strdup (url); +} + +/** + * fwupd_security_attr_get_name: + * @self: A #FwupdSecurityAttr + * + * Gets the attribute name. + * + * Returns: the attribute name, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_get_name (FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), NULL); + return priv->name; +} + +/** + * fwupd_security_attr_get_plugin: + * @self: A #FwupdSecurityAttr + * + * Gets the plugin that created the attribute. + * + * Returns: the plugin name, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_get_plugin (FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), NULL); + return priv->plugin; +} + +/** + * fwupd_security_attr_get_flags: + * @self: A #FwupdSecurityAttr + * + * Gets the self flags. + * + * Returns: the self flags, or 0 if unset + * + * Since: 1.5.0 + **/ +FwupdSecurityAttrFlags +fwupd_security_attr_get_flags (FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), 0); + return priv->flags; +} + +/** + * fwupd_security_attr_set_flags: + * @self: A #FwupdSecurityAttr + * @flags: the self flags, e.g. %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED + * + * Sets the self flags. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_flags (FwupdSecurityAttr *self, FwupdSecurityAttrFlags flags) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (self)); + priv->flags = flags; +} + +/** + * fwupd_security_attr_add_flag: + * @self: A #FwupdSecurityAttr + * @flag: the #FwupdSecurityAttrFlags + * + * Adds a specific self flag to the self. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_add_flag (FwupdSecurityAttr *self, FwupdSecurityAttrFlags flag) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (self)); + priv->flags |= flag; +} + +/** + * fwupd_security_attr_has_flag: + * @self: A #FwupdSecurityAttr + * @flag: the #FwupdSecurityAttrFlags + * + * Finds if the self has a specific self flag. + * + * Returns: %TRUE if the flag is set + * + * Since: 1.5.0 + **/ +gboolean +fwupd_security_attr_has_flag (FwupdSecurityAttr *self, FwupdSecurityAttrFlags flag) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), FALSE); + return (priv->flags & flag) > 0; +} + +/** + * fwupd_security_attr_get_level: + * @self: A #FwupdSecurityAttr + * + * Gets the HSI level. + * + * Returns: the #FwupdSecurityAttrLevel, or %FWUPD_SECURITY_ATTR_LEVEL_NONE if unset + * + * Since: 1.5.0 + **/ +FwupdSecurityAttrLevel +fwupd_security_attr_get_level (FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), 0); + return priv->level; +} + +/** + * fwupd_security_attr_set_level: + * @self: A #FwupdSecurityAttr + * @level: A #FwupdSecurityAttrLevel, e.g. %FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT + * + * Sets the HSI level. A @level of %FWUPD_SECURITY_ATTR_LEVEL_NONE is not used + * for the HSI calculation. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_level (FwupdSecurityAttr *self, FwupdSecurityAttrLevel level) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (self)); + priv->level = level; +} + +/** + * fwupd_security_attr_set_result: + * @self: A #FwupdSecurityAttr + * @result: A #FwupdSecurityAttrResult, e.g. %FWUPD_SECURITY_ATTR_LEVEL_LOCKED + * + * Sets the optional HSI result. This is required because some attributes may + * be a "success" when something is `locked` or may be "failed" if `found`. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_result (FwupdSecurityAttr *self, FwupdSecurityAttrResult result) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (self)); + priv->result = result; +} + +/** + * fwupd_security_attr_get_result: + * @self: A #FwupdSecurityAttr + * + * Gets the optional HSI result. + * + * Returns: the #FwupdSecurityAttrResult, e.g %FWUPD_SECURITY_ATTR_LEVEL_LOCKED + * + * Since: 1.5.0 + **/ +FwupdSecurityAttrResult +fwupd_security_attr_get_result (FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), 0); + return priv->result; +} + +/** + * fwupd_security_attr_to_variant: + * @self: A #FwupdSecurityAttr + * + * Creates a GVariant from the self data. + * + * Returns: the GVariant, or %NULL for error + * + * Since: 1.5.0 + **/ +GVariant * +fwupd_security_attr_to_variant (FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + GVariantBuilder builder; + + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), NULL); + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + if (priv->appstream_id != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_APPSTREAM_ID, + g_variant_new_string (priv->appstream_id)); + } + if (priv->name != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_NAME, + g_variant_new_string (priv->name)); + } + if (priv->url != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_URI, + g_variant_new_string (priv->url)); + } + if (priv->obsoletes->len > 0) { + g_autofree const gchar **strv = g_new0 (const gchar *, priv->obsoletes->len + 1); + for (guint i = 0; i < priv->obsoletes->len; i++) + strv[i] = (const gchar *) g_ptr_array_index (priv->obsoletes, i); + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_CATEGORIES, + g_variant_new_strv (strv, -1)); + } + if (priv->flags != 0) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_FLAGS, + g_variant_new_uint64 (priv->flags)); + } + if (priv->level > 0) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_HSI_LEVEL, + g_variant_new_uint32 (priv->level)); + } + if (priv->result != FWUPD_SECURITY_ATTR_RESULT_UNKNOWN) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_HSI_RESULT, + g_variant_new_uint32 (priv->result)); + } + if (priv->metadata != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_METADATA, + fwupd_hash_kv_to_variant (priv->metadata)); + } + return g_variant_new ("a{sv}", &builder); +} + +/** + * fwupd_security_attr_get_metadata: + * @self: A #FwupdSecurityAttr + * @key: metadata key + * + * Gets private metadata from the attribute which may be used in the name. + * + * Returns: (nullable): the metadata value, or %NULL if unfound + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_get_metadata (FwupdSecurityAttr *self, const gchar *key) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), NULL); + g_return_val_if_fail (key != NULL, NULL); + + if (priv->metadata == NULL) + return NULL; + return g_hash_table_lookup (priv->metadata, key); +} + +/** + * fwupd_security_attr_add_metadata: + * @self: A #FwupdSecurityAttr + * @key: metadata key + * @value: (nullable): metadata value + * + * Adds metadata to the attribute which may be used in the name. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_add_metadata (FwupdSecurityAttr *self, + const gchar *key, + const gchar *value) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (self)); + g_return_if_fail (key != NULL); + + if (priv->metadata == NULL) { + priv->metadata = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + } + g_hash_table_insert (priv->metadata, g_strdup (key), g_strdup (value)); +} + +static void +fwupd_security_attr_from_key_value (FwupdSecurityAttr *self, const gchar *key, GVariant *value) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + + if (g_strcmp0 (key, FWUPD_RESULT_KEY_APPSTREAM_ID) == 0) { + fwupd_security_attr_set_appstream_id (self, g_variant_get_string (value, NULL)); + return; + } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_NAME) == 0) { + fwupd_security_attr_set_name (self, g_variant_get_string (value, NULL)); + return; + } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_URI) == 0) { + fwupd_security_attr_set_url (self, g_variant_get_string (value, NULL)); + return; + } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_FLAGS) == 0) { + fwupd_security_attr_set_flags (self, g_variant_get_uint64 (value)); + return; + } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_HSI_LEVEL) == 0) { + fwupd_security_attr_set_level (self, g_variant_get_uint32 (value)); + return; + } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_HSI_RESULT) == 0) { + fwupd_security_attr_set_result (self, g_variant_get_uint32 (value)); + return; + } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_METADATA) == 0) { + if (priv->metadata != NULL) + g_hash_table_unref (priv->metadata); + priv->metadata = fwupd_variant_to_hash_kv (value); + return; + } +} + +static void +fwupd_pad_kv_str (GString *str, const gchar *key, const gchar *value) +{ + /* ignore */ + if (key == NULL || value == NULL) + return; + g_string_append_printf (str, " %s: ", key); + for (gsize i = strlen (key); i < 20; i++) + g_string_append (str, " "); + g_string_append_printf (str, "%s\n", value); +} + +static void +fwupd_pad_kv_tfl (GString *str, const gchar *key, FwupdSecurityAttrFlags security_attr_flags) +{ + g_autoptr(GString) tmp = g_string_new (""); + for (guint i = 0; i < 64; i++) { + if ((security_attr_flags & ((guint64) 1 << i)) == 0) + continue; + g_string_append_printf (tmp, "%s|", + fwupd_security_attr_flag_to_string ((guint64) 1 << i)); + } + if (tmp->len == 0) { + g_string_append (tmp, fwupd_security_attr_flag_to_string (0)); + } else { + g_string_truncate (tmp, tmp->len - 1); + } + fwupd_pad_kv_str (str, key, tmp->str); +} + +static void +fwupd_pad_kv_int (GString *str, const gchar *key, guint32 value) +{ + g_autofree gchar *tmp = NULL; + + /* ignore */ + if (value == 0) + return; + tmp = g_strdup_printf("%" G_GUINT32_FORMAT, value); + fwupd_pad_kv_str (str, key, tmp); +} + +static void +fwupd_security_attr_json_add_string (JsonBuilder *builder, const gchar *key, const gchar *str) +{ + if (str == NULL) + return; + json_builder_set_member_name (builder, key); + json_builder_add_string_value (builder, str); +} + +static void +fwupd_security_attr_json_add_int (JsonBuilder *builder, const gchar *key, guint64 num) +{ + if (num == 0) + return; + json_builder_set_member_name (builder, key); + json_builder_add_int_value (builder, num); +} + +/** + * fwupd_security_attr_to_json: + * @self: A #FwupdSecurityAttr + * @builder: A #JsonBuilder + * + * Adds a fwupd self to a JSON builder + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_to_json (FwupdSecurityAttr *self, JsonBuilder *builder) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (self)); + g_return_if_fail (builder != NULL); + + fwupd_security_attr_json_add_string (builder, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->appstream_id); + fwupd_security_attr_json_add_int (builder, FWUPD_RESULT_KEY_HSI_LEVEL, priv->level); + fwupd_security_attr_json_add_string (builder, FWUPD_RESULT_KEY_HSI_RESULT, + fwupd_security_attr_result_to_string (priv->result)); + fwupd_security_attr_json_add_string (builder, FWUPD_RESULT_KEY_NAME, priv->name); + fwupd_security_attr_json_add_string (builder, FWUPD_RESULT_KEY_PLUGIN, priv->plugin); + fwupd_security_attr_json_add_string (builder, FWUPD_RESULT_KEY_URI, priv->url); + if (priv->flags != FWUPD_SECURITY_ATTR_FLAG_NONE) { + json_builder_set_member_name (builder, FWUPD_RESULT_KEY_FLAGS); + json_builder_begin_array (builder); + for (guint i = 0; i < 64; i++) { + const gchar *tmp; + if ((priv->flags & ((guint64) 1 << i)) == 0) + continue; + tmp = fwupd_security_attr_flag_to_string ((guint64) 1 << i); + json_builder_add_string_value (builder, tmp); + } + json_builder_end_array (builder); + } + if (priv->metadata != NULL) { + g_autoptr(GList) keys = g_hash_table_get_keys (priv->metadata); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup (priv->metadata, key); + fwupd_security_attr_json_add_string (builder, key, value); + } + } +} + +/** + * fwupd_security_attr_to_string: + * @self: A #FwupdSecurityAttr + * + * Builds a text representation of the object. + * + * Returns: text, or %NULL for invalid + * + * Since: 1.5.0 + **/ +gchar * +fwupd_security_attr_to_string (FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + GString *str; + + g_return_val_if_fail (FWUPD_IS_SECURITY_ATTR (self), NULL); + + str = g_string_new (""); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->appstream_id); + fwupd_pad_kv_int (str, FWUPD_RESULT_KEY_HSI_LEVEL, priv->level); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_HSI_RESULT, + fwupd_security_attr_result_to_string (priv->result)); + if (priv->flags != FWUPD_SECURITY_ATTR_FLAG_NONE) + fwupd_pad_kv_tfl (str, FWUPD_RESULT_KEY_FLAGS, priv->flags); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_NAME, priv->name); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_PLUGIN, priv->plugin); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_URI, priv->url); + for (guint i = 0; i < priv->obsoletes->len; i++) { + const gchar *appstream_id = g_ptr_array_index (priv->obsoletes, i); + fwupd_pad_kv_str (str, "Obsolete", appstream_id); + } + if (priv->metadata != NULL) { + g_autoptr(GList) keys = g_hash_table_get_keys (priv->metadata); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup (priv->metadata, key); + fwupd_pad_kv_str (str, key, value); + } + } + + return g_string_free (str, FALSE); +} + +static void +fwupd_security_attr_class_init (FwupdSecurityAttrClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fwupd_security_attr_finalize; +} + +static void +fwupd_security_attr_init (FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + priv->obsoletes = g_ptr_array_new_with_free_func (g_free); +} + +static void +fwupd_security_attr_finalize (GObject *object) +{ + FwupdSecurityAttr *self = FWUPD_SECURITY_ATTR (object); + FwupdSecurityAttrPrivate *priv = GET_PRIVATE (self); + + if (priv->metadata != NULL) + g_hash_table_unref (priv->metadata); + g_free (priv->appstream_id); + g_free (priv->name); + g_free (priv->plugin); + g_free (priv->url); + g_ptr_array_unref (priv->obsoletes); + + G_OBJECT_CLASS (fwupd_security_attr_parent_class)->finalize (object); +} + +static void +fwupd_security_attr_set_from_variant_iter (FwupdSecurityAttr *self, GVariantIter *iter) +{ + GVariant *value; + const gchar *key; + while (g_variant_iter_next (iter, "{&sv}", &key, &value)) { + fwupd_security_attr_from_key_value (self, key, value); + g_variant_unref (value); + } +} + +/** + * fwupd_security_attr_from_variant: + * @value: a #GVariant + * + * Creates a new self using packed data. + * + * Returns: (transfer full): a new #FwupdSecurityAttr, or %NULL if @value was invalid + * + * Since: 1.5.0 + **/ +FwupdSecurityAttr * +fwupd_security_attr_from_variant (GVariant *value) +{ + FwupdSecurityAttr *rel = NULL; + const gchar *type_string; + g_autoptr(GVariantIter) iter = NULL; + + type_string = g_variant_get_type_string (value); + if (g_strcmp0 (type_string, "(a{sv})") == 0) { + rel = fwupd_security_attr_new (NULL); + g_variant_get (value, "(a{sv})", &iter); + fwupd_security_attr_set_from_variant_iter (rel, iter); + } else if (g_strcmp0 (type_string, "a{sv}") == 0) { + rel = fwupd_security_attr_new (NULL); + g_variant_get (value, "a{sv}", &iter); + fwupd_security_attr_set_from_variant_iter (rel, iter); + } else { + g_warning ("type %s not known", type_string); + } + return rel; +} + +/** + * fwupd_security_attr_array_from_variant: + * @value: a #GVariant + * + * Creates an array of new security_attrs using packed data. + * + * Returns: (transfer container) (element-type FwupdSecurityAttr): attributes, or %NULL if @value was invalid + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_security_attr_array_from_variant (GVariant *value) +{ + GPtrArray *array = NULL; + gsize sz; + g_autoptr(GVariant) untuple = NULL; + + array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + untuple = g_variant_get_child_value (value, 0); + sz = g_variant_n_children (untuple); + for (guint i = 0; i < sz; i++) { + FwupdSecurityAttr *rel; + g_autoptr(GVariant) data = NULL; + data = g_variant_get_child_value (untuple, i); + rel = fwupd_security_attr_from_variant (data); + if (rel == NULL) + continue; + g_ptr_array_add (array, rel); + } + return array; +} + +/** + * fwupd_security_attr_new: + * @appstream_id: (allow-none): the AppStream component ID, e.g. `com.intel.BiosGuard` + * + * Creates a new self. + * + * Returns: a new #FwupdSecurityAttr + * + * Since: 1.5.0 + **/ +FwupdSecurityAttr * +fwupd_security_attr_new (const gchar *appstream_id) +{ + FwupdSecurityAttr *self; + self = g_object_new (FWUPD_TYPE_SECURITY_ATTR, NULL); + if (appstream_id != NULL) + fwupd_security_attr_set_appstream_id (self, appstream_id); + return FWUPD_SECURITY_ATTR (self); +} diff --git a/libfwupd/fwupd-security-attr.h b/libfwupd/fwupd-security-attr.h new file mode 100644 index 000000000..34e197087 --- /dev/null +++ b/libfwupd/fwupd-security-attr.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-enums.h" + +G_BEGIN_DECLS + +#define FWUPD_TYPE_SECURITY_ATTR (fwupd_security_attr_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FwupdSecurityAttr, fwupd_security_attr, FWUPD, SECURITY_ATTR, GObject) + +struct _FwupdSecurityAttrClass +{ + GObjectClass parent_class; + /*< private >*/ + void (*_fwupd_reserved1) (void); + void (*_fwupd_reserved2) (void); + void (*_fwupd_reserved3) (void); + void (*_fwupd_reserved4) (void); + void (*_fwupd_reserved5) (void); + void (*_fwupd_reserved6) (void); + void (*_fwupd_reserved7) (void); +}; + + +/** + * FwupdSecurityAttrFlags: + * @FWUPD_SECURITY_ATTR_FLAG_NONE: No flags set + * @FWUPD_SECURITY_ATTR_FLAG_SUCCESS: Success + * @FWUPD_SECURITY_ATTR_FLAG_OBSOLETED: Obsoleted by another attribute + * @FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES: Suffix `U` + * @FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION: Suffix `A` + * @FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE: Suffix `!` + * + * The flags available for HSI attributes. + **/ +typedef enum { + FWUPD_SECURITY_ATTR_FLAG_NONE = 0, + FWUPD_SECURITY_ATTR_FLAG_SUCCESS = 1 << 0, + FWUPD_SECURITY_ATTR_FLAG_OBSOLETED = 1 << 1, + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES = 1 << 8, + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION = 1 << 9, + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE = 1 << 10, +} FwupdSecurityAttrFlags; + +/** + * FwupdSecurityAttrLevel: + * @FWUPD_SECURITY_ATTR_LEVEL_NONE: Very few detected firmware protections + * @FWUPD_SECURITY_ATTR_LEVEL_CRITICAL: The most basic of security protections + * @FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT: Firmware security issues considered important + * @FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL: Firmware security issues that pose a theoretical concern + * @FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_PROTECTION: Out-of-band protection of the system firmware + * @FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_ATTESTATION: Out-of-band attestation of the system firmware + * + * The HSI level. + **/ +typedef enum { + FWUPD_SECURITY_ATTR_LEVEL_NONE = 0, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_LEVEL_CRITICAL = 1, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT = 2, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL = 3, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_PROTECTION = 4, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_ATTESTATION = 5, /* Since: 1.5.0 */ + /*< private >*/ + FWUPD_SECURITY_ATTR_LEVEL_LAST = 6 /* perhaps increased in the future */ +} FwupdSecurityAttrLevel; + +/** + * FwupdSecurityAttrResult: + * @FWUPD_SECURITY_ATTR_RESULT_UNKNOWN: Not known + * @FWUPD_SECURITY_ATTR_RESULT_ENABLED: Enabled + * @FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED: Not enabled + * @FWUPD_SECURITY_ATTR_RESULT_VALID: Valid + * @FWUPD_SECURITY_ATTR_RESULT_NOT_VALID: Not valid + * @FWUPD_SECURITY_ATTR_RESULT_LOCKED: Locked + * @FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED: Not locked + * @FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED: Encrypted + * @FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED: Not encrypted + * @FWUPD_SECURITY_ATTR_RESULT_TAINTED: Tainted + * @FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED: Not tainted + * @FWUPD_SECURITY_ATTR_RESULT_FOUND: Found + * @FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND: NOt found + * @FWUPD_SECURITY_ATTR_RESULT_SUPPORTED: Supported + * @FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED: Not supported + * + * The HSI result. + **/ +typedef enum { + FWUPD_SECURITY_ATTR_RESULT_UNKNOWN, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_ENABLED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_VALID, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_VALID, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_LOCKED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_TAINTED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_FOUND, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_SUPPORTED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED, /* Since: 1.5.0 */ + /*< private >*/ + FWUPD_SECURITY_ATTR_RESULT_LAST +} FwupdSecurityAttrResult; + +FwupdSecurityAttr *fwupd_security_attr_new (const gchar *appstream_id); +gchar *fwupd_security_attr_to_string (FwupdSecurityAttr *self); + +const gchar *fwupd_security_attr_get_appstream_id (FwupdSecurityAttr *self); +void fwupd_security_attr_set_appstream_id (FwupdSecurityAttr *self, + const gchar *appstream_id); +FwupdSecurityAttrLevel fwupd_security_attr_get_level (FwupdSecurityAttr *self); +void fwupd_security_attr_set_level (FwupdSecurityAttr *self, + FwupdSecurityAttrLevel level); +FwupdSecurityAttrResult fwupd_security_attr_get_result (FwupdSecurityAttr *self); +void fwupd_security_attr_set_result (FwupdSecurityAttr *self, + FwupdSecurityAttrResult result); +const gchar *fwupd_security_attr_get_name (FwupdSecurityAttr *self); +void fwupd_security_attr_set_name (FwupdSecurityAttr *self, + const gchar *name); +const gchar *fwupd_security_attr_get_plugin (FwupdSecurityAttr *self); +void fwupd_security_attr_set_plugin (FwupdSecurityAttr *self, + const gchar *plugin); +const gchar *fwupd_security_attr_get_url (FwupdSecurityAttr *self); +void fwupd_security_attr_set_url (FwupdSecurityAttr *self, + const gchar *url); +GPtrArray *fwupd_security_attr_get_obsoletes (FwupdSecurityAttr *self); +void fwupd_security_attr_add_obsolete (FwupdSecurityAttr *self, + const gchar *appstream_id); +gboolean fwupd_security_attr_has_obsolete (FwupdSecurityAttr *self, + const gchar *appstream_id); +const gchar *fwupd_security_attr_get_metadata (FwupdSecurityAttr *self, + const gchar *key); +void fwupd_security_attr_add_metadata (FwupdSecurityAttr *self, + const gchar *key, + const gchar *value); +FwupdSecurityAttrFlags fwupd_security_attr_get_flags (FwupdSecurityAttr *self); +void fwupd_security_attr_set_flags (FwupdSecurityAttr *self, + FwupdSecurityAttrFlags flags); +void fwupd_security_attr_add_flag (FwupdSecurityAttr *self, + FwupdSecurityAttrFlags flag); +gboolean fwupd_security_attr_has_flag (FwupdSecurityAttr *self, + FwupdSecurityAttrFlags flag); +const gchar *fwupd_security_attr_flag_to_string (FwupdSecurityAttrFlags flag); +const gchar *fwupd_security_attr_flag_to_suffix (FwupdSecurityAttrFlags flag); +const gchar *fwupd_security_attr_result_to_string (FwupdSecurityAttrResult result); + +FwupdSecurityAttr *fwupd_security_attr_from_variant (GVariant *value); +GPtrArray *fwupd_security_attr_array_from_variant (GVariant *value); + +G_END_DECLS diff --git a/libfwupd/fwupd-self-test.c b/libfwupd/fwupd-self-test.c index 679360b02..62d9b082e 100644 --- a/libfwupd/fwupd-self-test.c +++ b/libfwupd/fwupd-self-test.c @@ -12,6 +12,7 @@ #endif #include "fwupd-client.h" +#include "fwupd-client-sync.h" #include "fwupd-common.h" #include "fwupd-enums.h" #include "fwupd-error.h" @@ -147,6 +148,12 @@ fwupd_enums_func (void) break; g_assert_cmpint (fwupd_device_flag_from_string (tmp), ==, i); } + for (guint64 i = 1; i < FWUPD_PLUGIN_FLAG_UNKNOWN; i *= 2) { + const gchar *tmp = fwupd_plugin_flag_to_string (i); + if (tmp == NULL) + break; + g_assert_cmpint (fwupd_plugin_flag_from_string (tmp), ==, i); + } } static void diff --git a/libfwupd/fwupd.h b/libfwupd/fwupd.h index 82571aa26..a3c020ed9 100644 --- a/libfwupd/fwupd.h +++ b/libfwupd/fwupd.h @@ -14,10 +14,13 @@ #define __FWUPD_H_INSIDE__ #include +#include #include #include +#include #include #include +#include #include #include #include diff --git a/libfwupd/fwupd.map b/libfwupd/fwupd.map index b4edfbe58..632f26faa 100644 --- a/libfwupd/fwupd.map +++ b/libfwupd/fwupd.map @@ -475,3 +475,144 @@ LIBFWUPD_1.4.6 { fwupd_client_set_blocked_firmware; local: *; } LIBFWUPD_1.4.5; + +LIBFWUPD_1.5.0 { + global: + fwupd_client_activate_async; + fwupd_client_activate_finish; + fwupd_client_clear_results_async; + fwupd_client_clear_results_finish; + fwupd_client_connect_async; + fwupd_client_connect_finish; + fwupd_client_download_bytes_async; + fwupd_client_download_bytes_finish; + fwupd_client_get_approved_firmware_async; + fwupd_client_get_approved_firmware_finish; + fwupd_client_get_blocked_firmware_async; + fwupd_client_get_blocked_firmware_finish; + fwupd_client_get_details_bytes; + fwupd_client_get_details_bytes_async; + fwupd_client_get_details_bytes_finish; + fwupd_client_get_device_by_id_async; + fwupd_client_get_device_by_id_finish; + fwupd_client_get_devices_async; + fwupd_client_get_devices_by_guid_async; + fwupd_client_get_devices_by_guid_finish; + fwupd_client_get_devices_finish; + fwupd_client_get_downgrades_async; + fwupd_client_get_downgrades_finish; + fwupd_client_get_history_async; + fwupd_client_get_history_finish; + fwupd_client_get_host_security_attrs; + fwupd_client_get_host_security_attrs_async; + fwupd_client_get_host_security_attrs_finish; + fwupd_client_get_host_security_id; + fwupd_client_get_plugins; + fwupd_client_get_plugins_async; + fwupd_client_get_plugins_finish; + fwupd_client_get_releases_async; + fwupd_client_get_releases_finish; + fwupd_client_get_remote_by_id_async; + fwupd_client_get_remote_by_id_finish; + fwupd_client_get_remotes_async; + fwupd_client_get_remotes_finish; + fwupd_client_get_report_metadata; + fwupd_client_get_report_metadata_async; + fwupd_client_get_report_metadata_finish; + fwupd_client_get_results_async; + fwupd_client_get_results_finish; + fwupd_client_get_upgrades_async; + fwupd_client_get_upgrades_finish; + fwupd_client_install_async; + fwupd_client_install_bytes_async; + fwupd_client_install_bytes_finish; + fwupd_client_install_finish; + fwupd_client_install_release_async; + fwupd_client_install_release_finish; + fwupd_client_modify_config_async; + fwupd_client_modify_config_finish; + fwupd_client_modify_device_async; + fwupd_client_modify_device_finish; + fwupd_client_modify_remote_async; + fwupd_client_modify_remote_finish; + fwupd_client_refresh_remote_async; + fwupd_client_refresh_remote_finish; + fwupd_client_self_sign_async; + fwupd_client_self_sign_finish; + fwupd_client_set_approved_firmware_async; + fwupd_client_set_approved_firmware_finish; + fwupd_client_set_blocked_firmware_async; + fwupd_client_set_blocked_firmware_finish; + fwupd_client_set_feature_flags_async; + fwupd_client_set_feature_flags_finish; + fwupd_client_unlock_async; + fwupd_client_unlock_finish; + fwupd_client_update_metadata_bytes_async; + fwupd_client_update_metadata_bytes_finish; + fwupd_client_upload_bytes_async; + fwupd_client_upload_bytes_finish; + fwupd_client_verify_async; + fwupd_client_verify_finish; + fwupd_client_verify_update_async; + fwupd_client_verify_update_finish; + fwupd_device_get_branch; + fwupd_device_set_branch; + fwupd_plugin_add_flag; + fwupd_plugin_array_from_variant; + fwupd_plugin_flag_from_string; + fwupd_plugin_flag_to_string; + fwupd_plugin_from_variant; + fwupd_plugin_get_flags; + fwupd_plugin_get_name; + fwupd_plugin_get_type; + fwupd_plugin_has_flag; + fwupd_plugin_new; + fwupd_plugin_remove_flag; + fwupd_plugin_set_flags; + fwupd_plugin_set_name; + fwupd_plugin_to_json; + fwupd_plugin_to_string; + fwupd_plugin_to_variant; + fwupd_release_get_branch; + fwupd_release_set_branch; + fwupd_remote_get_automatic_security_reports; + fwupd_remote_get_security_report_uri; + fwupd_security_attr_add_flag; + fwupd_security_attr_add_metadata; + fwupd_security_attr_add_obsolete; + fwupd_security_attr_array_from_variant; + fwupd_security_attr_flag_to_string; + fwupd_security_attr_flag_to_suffix; + fwupd_security_attr_from_variant; + fwupd_security_attr_get_appstream_id; + fwupd_security_attr_get_flags; + fwupd_security_attr_get_level; + fwupd_security_attr_get_metadata; + fwupd_security_attr_get_name; + fwupd_security_attr_get_obsoletes; + fwupd_security_attr_get_plugin; + fwupd_security_attr_get_result; + fwupd_security_attr_get_type; + fwupd_security_attr_get_url; + fwupd_security_attr_has_flag; + fwupd_security_attr_has_obsolete; + fwupd_security_attr_new; + fwupd_security_attr_result_to_string; + fwupd_security_attr_set_appstream_id; + fwupd_security_attr_set_flags; + fwupd_security_attr_set_level; + fwupd_security_attr_set_name; + fwupd_security_attr_set_plugin; + fwupd_security_attr_set_result; + fwupd_security_attr_set_url; + fwupd_security_attr_to_json; + fwupd_security_attr_to_string; + fwupd_security_attr_to_variant; + local: *; +} LIBFWUPD_1.4.6; + +LIBFWUPD_1.5.1 { + global: + fwupd_device_add_child; + local: *; +} LIBFWUPD_1.5.0; diff --git a/libfwupd/meson.build b/libfwupd/meson.build index 9fe6cec3f..4ac681473 100644 --- a/libfwupd/meson.build +++ b/libfwupd/meson.build @@ -15,13 +15,16 @@ install_headers( install_headers([ 'fwupd-client.h', + 'fwupd-client-sync.h', 'fwupd-common.h', 'fwupd-deprecated.h', 'fwupd-device.h', 'fwupd-enums.h', 'fwupd-error.h', 'fwupd-remote.h', + 'fwupd-security-attr.h', 'fwupd-release.h', + 'fwupd-plugin.h', fwupd_version_h, ], subdir : 'fwupd-1/libfwupd', @@ -33,11 +36,14 @@ fwupd = shared_library( 'fwupd', sources : [ 'fwupd-client.c', + 'fwupd-client-sync.c', 'fwupd-common.c', 'fwupd-device.c', 'fwupd-enums.c', 'fwupd-error.c', + 'fwupd-security-attr.c', 'fwupd-release.c', + 'fwupd-plugin.c', 'fwupd-remote.c', ], soversion : libfwupd_lt_current, @@ -74,6 +80,8 @@ if get_option('introspection') sources : [ 'fwupd-client.c', 'fwupd-client.h', + 'fwupd-client-sync.c', + 'fwupd-client-sync.h', 'fwupd-common.c', 'fwupd-common.h', 'fwupd-common-private.h', @@ -85,9 +93,15 @@ if get_option('introspection') 'fwupd-enums-private.h', 'fwupd-error.c', 'fwupd-error.h', + 'fwupd-security-attr.c', + 'fwupd-security-attr.h', + 'fwupd-security-attr-private.h', 'fwupd-release.c', 'fwupd-release.h', 'fwupd-release-private.h', + 'fwupd-plugin.c', + 'fwupd-plugin.h', + 'fwupd-plugin-private.h', 'fwupd-remote.c', 'fwupd-remote.h', 'fwupd-remote-private.h', diff --git a/libfwupdplugin/fu-cabinet.c b/libfwupdplugin/fu-cabinet.c index 86a87954b..32bf6c757 100644 --- a/libfwupdplugin/fu-cabinet.c +++ b/libfwupdplugin/fu-cabinet.c @@ -341,8 +341,11 @@ fu_cabinet_set_container_checksum_cb (XbBuilderFixup *builder_fixup, /* verify it is correct */ if (g_strcmp0 (xb_builder_node_get_text (csum), self->container_checksum) != 0) { - g_debug ("invalid container checksum %s, fixing up to %s", - xb_builder_node_get_text (csum), self->container_checksum); + if (xb_builder_node_get_text (csum) != NULL) { + g_warning ("invalid container checksum %s, fixing up to %s", + xb_builder_node_get_text (csum), + self->container_checksum); + } xb_builder_node_set_text (csum, self->container_checksum, -1); } return TRUE; @@ -485,7 +488,7 @@ fu_cabinet_build_silo (FuCabinet *self, GBytes *data, GError **error) g_autoptr(XbBuilderFixup) fixup2 = NULL; /* verbose profiling */ - if (g_getenv ("FWUPD_VERBOSE") != NULL) { + if (g_getenv ("FWUPD_XMLB_VERBOSE") != NULL) { xb_builder_set_profile_flags (self->builder, XB_SILO_PROFILE_FLAG_XPATH | XB_SILO_PROFILE_FLAG_DEBUG); @@ -504,7 +507,6 @@ fu_cabinet_build_silo (FuCabinet *self, GBytes *data, GError **error) /* adds each metainfo file to the silo */ for (guint i = 0; i < folders->len; i++) { GCabFolder *cabfolder = GCAB_FOLDER (g_ptr_array_index (folders, i)); - g_debug ("processing folder: %u/%u", i + 1, folders->len); if (!fu_cabinet_build_silo_folder (self, cabfolder, error)) return FALSE; } diff --git a/libfwupdplugin/fu-common-version.c b/libfwupdplugin/fu-common-version.c index 582e85cde..a09172de7 100644 --- a/libfwupdplugin/fu-common-version.c +++ b/libfwupdplugin/fu-common-version.c @@ -444,10 +444,8 @@ fu_common_version_verify_format (const gchar *version, return TRUE; /* nothing we can check for */ - if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN) { - g_debug ("not checking %s as no version format set", version); + if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN) return TRUE; - } /* check the base format */ fmt_guess = fu_common_version_guess_format (version); @@ -484,6 +482,13 @@ fu_common_vercmp_full (const gchar *version_a, { if (fmt == FWUPD_VERSION_FORMAT_PLAIN) return g_strcmp0 (version_a, version_b); + if (fmt == FWUPD_VERSION_FORMAT_HEX) { + g_autofree gchar *hex_a = NULL; + g_autofree gchar *hex_b = NULL; + hex_a = fu_common_version_parse_from_format (version_a, fmt); + hex_b = fu_common_version_parse_from_format (version_b, fmt); + return fu_common_vercmp (hex_a, hex_b); + } return fu_common_vercmp (version_a, version_b); } diff --git a/libfwupdplugin/fu-common.c b/libfwupdplugin/fu-common.c index f91c4f74d..01b688a08 100644 --- a/libfwupdplugin/fu-common.c +++ b/libfwupdplugin/fu-common.c @@ -19,6 +19,10 @@ #include #endif +#ifdef HAVE_CPUID_H +#include +#endif + #include #include #include @@ -31,11 +35,12 @@ #include "fu-common.h" #include "fu-volume-private.h" -#define UDISKS_DBUS_SERVICE "org.freedesktop.UDisks2" -#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2/Manager" -#define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager" -#define UDISKS_DBUS_PART_INTERFACE "org.freedesktop.UDisks2.Partition" -#define UDISKS_DBUS_FILE_INTERFACE "org.freedesktop.UDisks2.Filesystem" +#define UDISKS_DBUS_SERVICE "org.freedesktop.UDisks2" +#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2/Manager" +#define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager" +#define UDISKS_DBUS_INTERFACE_PARTITION "org.freedesktop.UDisks2.Partition" +#define UDISKS_DBUS_INTERFACE_FILESYSTEM "org.freedesktop.UDisks2.Filesystem" +#define UDISKS_DBUS_INTERFACE_BLOCK "org.freedesktop.UDisks2.Block" /** * SECTION:fu-common @@ -159,7 +164,8 @@ fu_common_mkdir_parent (const gchar *filename, GError **error) g_autofree gchar *parent = NULL; parent = g_path_get_dirname (filename); - g_debug ("creating path %s", parent); + if (!g_file_test (parent, G_FILE_TEST_IS_DIR)) + g_debug ("creating path %s", parent); if (g_mkdir_with_parents (parent, 0755) == -1) { g_set_error (error, FWUPD_ERROR, @@ -1033,6 +1039,12 @@ fu_common_get_path (FuPathKind path_kind) if (tmp != NULL) return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL); return g_build_filename (FWUPD_LOCALSTATEDIR, NULL); + /* /proc */ + case FU_PATH_KIND_PROCFS: + tmp = g_getenv ("FWUPD_PROCFS"); + if (tmp != NULL) + return g_strdup (tmp); + return g_strdup ("/proc"); /* /sys/firmware */ case FU_PATH_KIND_SYSFSDIR_FW: tmp = g_getenv ("FWUPD_SYSFSFWDIR"); @@ -1057,6 +1069,12 @@ fu_common_get_path (FuPathKind path_kind) if (tmp != NULL) return g_strdup (tmp); return g_strdup ("/sys/kernel/security"); + /* /sys/firmware/acpi/tables */ + case FU_PATH_KIND_ACPI_TABLES: + tmp = g_getenv ("FWUPD_ACPITABLESDIR"); + if (tmp != NULL) + return g_strdup (tmp); + return g_strdup ("/sys/firmware/acpi/tables"); /* /etc */ case FU_PATH_KIND_SYSCONFDIR: tmp = g_getenv ("FWUPD_SYSCONFDIR"); @@ -1217,6 +1235,9 @@ fu_common_strwidth (const gchar *text) { const gchar *p = text; gsize width = 0; + + g_return_val_if_fail (text != NULL, 0); + while (*p) { gunichar c = g_utf8_get_char (p); if (g_unichar_iswide (c)) @@ -1679,6 +1700,49 @@ fu_common_fnmatch (const gchar *pattern, const gchar *str) #endif } +static gint +fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b) +{ + return g_strcmp0 (*(const gchar **)a, *(const gchar **)b); +} + +/** + * fu_common_filename_glob: + * @directory: a directory path + * @pattern: a glob pattern, e.g. `*foo*` + * @error: A #GError or %NULL + * + * Returns all the filenames that match a specific glob pattern. + * Any results are sorted. No matching files will set @error. + * + * Return value: (element-type utf8) (transfer container): matching files, or %NULL + * + * Since: 1.5.0 + **/ +GPtrArray * +fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error) +{ + const gchar *basename; + g_autoptr(GDir) dir = g_dir_open (directory, 0, error); + g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free); + if (dir == NULL) + return NULL; + while ((basename = g_dir_read_name (dir)) != NULL) { + if (!fu_common_fnmatch (pattern, basename)) + continue; + g_ptr_array_add (files, g_build_filename (directory, basename, NULL)); + } + if (files->len == 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "no files matched pattern"); + return NULL; + } + g_ptr_array_sort (files, fu_common_filename_glob_sort_cb); + return g_steal_pointer (&files); +} + /** * fu_common_strnsplit: * @str: a string to split @@ -1937,6 +2001,24 @@ fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endia g_byte_array_append (array, buf, sizeof(buf)); } +/** + * fu_byte_array_set_size: + * @array: a #GByteArray + * @length: the new size of the GByteArray + * + * Sets the size of the GByteArray, expanding it with NULs if necessary. + * + * Since: 1.5.0 + **/ +void +fu_byte_array_set_size (GByteArray *array, guint length) +{ + guint oldlength = array->len; + g_byte_array_set_size (array, length); + if (length > oldlength) + memset (array->data + oldlength, 0x0, length - oldlength); +} + /** * fu_common_kernel_locked_down: * @@ -1971,6 +2053,83 @@ fu_common_kernel_locked_down (void) #endif } +/** + * fu_common_cpuid: + * @leaf: The CPUID level, now called the 'leaf' by Intel + * @eax: (out) (nullable): EAX register + * @ebx: (out) (nullable): EBX register + * @ecx: (out) (nullable): ECX register + * @edx: (out) (nullable): EDX register + * @error: A #GError or NULL + * + * Calls CPUID and returns the registers for the given leaf. + * + * Return value: %TRUE if the registers are set. + * + * Since: 1.5.0 + **/ +gboolean +fu_common_cpuid (guint32 leaf, + guint32 *eax, + guint32 *ebx, + guint32 *ecx, + guint32 *edx, + GError **error) +{ +#ifdef HAVE_CPUID_H + guint eax_tmp = 0; + guint ebx_tmp = 0; + guint ecx_tmp = 0; + guint edx_tmp = 0; + + /* get vendor */ + __get_cpuid_count (leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp); + if (eax != NULL) + *eax = eax_tmp; + if (ebx != NULL) + *ebx = ebx_tmp; + if (ecx != NULL) + *ecx = ecx_tmp; + if (edx != NULL) + *edx = edx_tmp; + return TRUE; +#else + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "no support"); + return FALSE; +#endif +} + +/** + * fu_common_is_cpu_intel: + * + * Uses CPUID to discover the CPU vendor and check if it is Intel. + * + * Return value: %TRUE if the vendor was Intel. + * + * Since: 1.5.0 + **/ +gboolean +fu_common_is_cpu_intel (void) +{ + guint ebx = 0; + guint ecx = 0; + guint edx = 0; + + if (!fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, NULL)) + return FALSE; +#ifdef HAVE_CPUID_H + if (ebx == signature_INTEL_ebx && + edx == signature_INTEL_edx && + ecx == signature_INTEL_ecx) { + return TRUE; + } +#endif + return FALSE; +} + /** * fu_common_is_live_media: * @@ -2006,16 +2165,21 @@ fu_common_is_live_media (void) } static GPtrArray * -fu_common_get_block_devices (GDBusConnection *connection, GError **error) +fu_common_get_block_devices (GError **error) { GVariantBuilder builder; - GVariant *input; const gchar *obj; g_autoptr(GVariant) output = NULL; g_autoptr(GDBusProxy) proxy = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GVariantIter) iter = NULL; + g_autoptr(GDBusConnection) connection = NULL; + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); + if (connection == NULL) { + g_prefix_error (error, "failed to get system bus: "); + return NULL; + } proxy = g_dbus_proxy_new_sync (connection, G_DBUS_PROXY_FLAGS_NONE, NULL, UDISKS_DBUS_SERVICE, @@ -2027,17 +2191,36 @@ fu_common_get_block_devices (GDBusConnection *connection, GError **error) return NULL; } g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); - input = g_variant_new ("(a{sv})", &builder); output = g_dbus_proxy_call_sync (proxy, - "GetBlockDevices", g_variant_ref (input), + "GetBlockDevices", + g_variant_new ("(a{sv})", &builder), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); - if (output == NULL) + if (output == NULL) { + if (error != NULL) + g_dbus_error_strip_remote_error (*error); + g_prefix_error (error, "failed to call %s.%s(): ", + UDISKS_DBUS_SERVICE, + "GetBlockDevices"); return NULL; - devices = g_ptr_array_new_with_free_func (g_free); + } + devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); g_variant_get (output, "(ao)", &iter); - while (g_variant_iter_next (iter, "o", &obj)) - g_ptr_array_add (devices, g_strdup (obj)); + while (g_variant_iter_next (iter, "&o", &obj)) { + g_autoptr(GDBusProxy) proxy_blk = NULL; + proxy_blk = g_dbus_proxy_new_sync (connection, + G_DBUS_PROXY_FLAGS_NONE, NULL, + UDISKS_DBUS_SERVICE, + obj, + UDISKS_DBUS_INTERFACE_BLOCK, + NULL, error); + if (proxy_blk == NULL) { + g_prefix_error (error, "failed to initialize d-bus proxy for %s: ", obj); + return NULL; + } + g_ptr_array_add (devices, g_steal_pointer (&proxy_blk)); + } + return g_steal_pointer (&devices); } @@ -2047,9 +2230,7 @@ fu_common_get_block_devices (GDBusConnection *connection, GError **error) * @kind: A volume kind, typically a GUID * @error: A #GError or NULL * - * Call into the plugin's get results routine - * - * Finds all volumes of a specific type + * Finds all volumes of a specific partition type * * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found * @@ -2058,56 +2239,56 @@ fu_common_get_block_devices (GDBusConnection *connection, GError **error) GPtrArray * fu_common_get_volumes_by_kind (const gchar *kind, GError **error) { - g_autoptr(GDBusConnection) connection = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GPtrArray) volumes = NULL; - connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); - if (connection == NULL) { - g_prefix_error (error, "failed to get system bus: "); - return NULL; - } - devices = fu_common_get_block_devices (connection, error); + devices = fu_common_get_block_devices (error); if (devices == NULL) return NULL; volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); for (guint i = 0; i < devices->len; i++) { - const gchar *obj = g_ptr_array_index (devices, i); + GDBusProxy *proxy_blk = g_ptr_array_index (devices, i); const gchar *type_str; g_autoptr(GDBusProxy) proxy_part = NULL; - g_autoptr(GDBusProxy) proxy_file = NULL; - g_autoptr(GError) error_local = NULL; + g_autoptr(GDBusProxy) proxy_fs = NULL; g_autoptr(GVariant) val = NULL; - proxy_part = g_dbus_proxy_new_sync (connection, + proxy_part = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk), G_DBUS_PROXY_FLAGS_NONE, NULL, UDISKS_DBUS_SERVICE, - obj, - UDISKS_DBUS_PART_INTERFACE, + g_dbus_proxy_get_object_path (proxy_blk), + UDISKS_DBUS_INTERFACE_PARTITION, NULL, error); if (proxy_part == NULL) { - g_prefix_error (error, "failed to initialize d-bus proxy %s: ", obj); + g_prefix_error (error, "failed to initialize d-bus proxy %s: ", + g_dbus_proxy_get_object_path (proxy_blk)); return NULL; } val = g_dbus_proxy_get_cached_property (proxy_part, "Type"); if (val == NULL) continue; - g_variant_get (val, "s", &type_str); - g_debug ("device %s, type: %s", obj, type_str); + g_variant_get (val, "&s", &type_str); + g_debug ("device %s, type: %s", + g_dbus_proxy_get_object_path (proxy_blk), type_str); if (g_strcmp0 (type_str, kind) != 0) continue; - proxy_file = g_dbus_proxy_new_sync (connection, - G_DBUS_PROXY_FLAGS_NONE, NULL, - UDISKS_DBUS_SERVICE, - obj, - UDISKS_DBUS_FILE_INTERFACE, - NULL, error); - if (proxy_file == NULL) { - g_prefix_error (error, "failed to initialize d-bus proxy %s: ", obj); + proxy_fs = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk), + G_DBUS_PROXY_FLAGS_NONE, NULL, + UDISKS_DBUS_SERVICE, + g_dbus_proxy_get_object_path (proxy_blk), + UDISKS_DBUS_INTERFACE_FILESYSTEM, + NULL, error); + if (proxy_fs == NULL) { + g_prefix_error (error, "failed to initialize d-bus proxy %s: ", + g_dbus_proxy_get_object_path (proxy_blk)); return NULL; } - g_ptr_array_add (volumes, fu_volume_new_from_proxy (proxy_file)); + g_ptr_array_add (volumes, + g_object_new (FU_TYPE_VOLUME, + "proxy-block", proxy_blk, + "proxy-filesystem", proxy_fs, + NULL)); } if (volumes->len == 0) { g_set_error (error, @@ -2119,6 +2300,90 @@ fu_common_get_volumes_by_kind (const gchar *kind, GError **error) return g_steal_pointer (&volumes); } +/** + * fu_common_get_volume_by_device: + * @device: A device string, typcically starting with `/dev/` + * @error: A #GError or NULL + * + * Finds the first volume from the specified device. + * + * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found + * + * Since: 1.5.1 + **/ +FuVolume * +fu_common_get_volume_by_device (const gchar *device, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* find matching block device */ + devices = fu_common_get_block_devices (error); + if (devices == NULL) + return NULL; + for (guint i = 0; i < devices->len; i++) { + GDBusProxy *proxy_blk = g_ptr_array_index (devices, i); + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property (proxy_blk, "Device"); + if (val == NULL) + continue; + if (g_strcmp0 (g_variant_get_bytestring (val), device) == 0) { + return g_object_new (FU_TYPE_VOLUME, + "proxy-block", proxy_blk, + NULL); + } + } + + /* failed */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "no volumes for device %s", + device); + return NULL; +} + +/** + * fu_common_get_volume_by_devnum: + * @devicenum: A device number + * @error: A #GError or NULL + * + * Finds the first volume from the specified device. + * + * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found + * + * Since: 1.5.1 + **/ +FuVolume * +fu_common_get_volume_by_devnum (guint32 devnum, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* find matching block device */ + devices = fu_common_get_block_devices (error); + if (devices == NULL) + return NULL; + for (guint i = 0; i < devices->len; i++) { + GDBusProxy *proxy_blk = g_ptr_array_index (devices, i); + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property (proxy_blk, "DeviceNumber"); + if (val == NULL) + continue; + if (devnum == g_variant_get_uint64 (val)) { + return g_object_new (FU_TYPE_VOLUME, + "proxy-block", proxy_blk, + NULL); + } + } + + /* failed */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "no volumes for devnum %u", + devnum); + return NULL; +} + /** * fu_common_get_esp_default: * @error: A #GError or NULL @@ -2186,7 +2451,7 @@ fu_common_get_esp_for_path (const gchar *esp_path, GError **error) return NULL; for (guint i = 0; i < volumes->len; i++) { FuVolume *vol = g_ptr_array_index (volumes, i); - g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_id (vol)); + g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_mount_point (vol)); if (g_strcmp0 (basename, vol_basename) == 0) return g_object_ref (vol); } @@ -2197,3 +2462,101 @@ fu_common_get_esp_for_path (const gchar *esp_path, GError **error) esp_path); return NULL; } + +/** + * fu_common_crc8: + * @buf: memory buffer + * @bufsz: sizeof buf + * + * Returns the cyclic redundancy check value for the given memory buffer. + * + * Returns: CRC value + * + * Since: 1.5.0 + **/ +guint8 +fu_common_crc8 (const guint8 *buf, gsize bufsz) +{ + guint32 crc = 0; + for (gsize j = bufsz; j > 0; j--) { + crc ^= (*(buf++) << 8); + for (guint32 i = 8; i; i--) { + if (crc & 0x8000) + crc ^= (0x1070 << 3); + crc <<= 1; + } + } + return ~((guint8) (crc >> 8)); +} + +/** + * fu_common_crc16: + * @buf: memory buffer + * @bufsz: sizeof buf + * + * Returns the cyclic redundancy check value for the given memory buffer. + * + * Returns: CRC value + * + * Since: 1.5.0 + **/ +guint16 +fu_common_crc16 (const guint8 *buf, gsize bufsz) +{ + guint16 crc = 0xffff; + for (gsize len = bufsz; len > 0; len--) { + crc = (guint16) (crc ^ (*buf++)); + for (guint8 i = 0; i < 8; i++) { + if (crc & 0x1) { + crc = (crc >> 1) ^ 0xa001; + } else { + crc >>= 1; + } + } + } + return ~crc; +} + +/** + * fu_common_crc32_full: + * @buf: memory buffer + * @bufsz: sizeof buf + * @crc: initial CRC value, typically 0xFFFFFFFF + * @polynomial: CRC polynomial, typically 0xEDB88320 + * + * Returns the cyclic redundancy check value for the given memory buffer. + * + * Returns: CRC value + * + * Since: 1.5.0 + **/ +guint32 +fu_common_crc32_full (const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial) +{ + for (guint32 idx = 0; idx < bufsz; idx++) { + guint8 data = *buf++; + crc = crc ^ data; + for (guint32 bit = 0; bit < 8; bit++) { + guint32 mask = -(crc & 1); + crc = (crc >> 1) ^ (polynomial & mask); + } + } + return ~crc; +} + +/** + * fu_common_crc32: + * @buf: memory buffer + * @bufsz: sizeof buf + * + * Returns the cyclic redundancy check value for the given memory buffer. + * + * Returns: CRC value + * + * Since: 1.5.0 + **/ +guint32 +fu_common_crc32 (const guint8 *buf, gsize bufsz) +{ + return fu_common_crc32_full (buf, bufsz, 0xFFFFFFFF, 0xEDB88320); +} diff --git a/libfwupdplugin/fu-common.h b/libfwupdplugin/fu-common.h index 110ac088f..8a454fa0d 100644 --- a/libfwupdplugin/fu-common.h +++ b/libfwupdplugin/fu-common.h @@ -55,9 +55,11 @@ typedef guint FuEndianType; * @FU_PATH_KIND_SYSFSDIR_FW: The sysfs firmware location (IE /sys/firmware) * @FU_PATH_KIND_SYSFSDIR_DRIVERS: The platform sysfs directory (IE /sys/bus/platform/drivers) * @FU_PATH_KIND_SYSFSDIR_TPM: The TPM sysfs directory (IE /sys/class/tpm) + * @FU_PATH_KIND_PROCFS: The procfs location (IE /proc) * @FU_PATH_KIND_POLKIT_ACTIONS: The directory for policy kit actions (IE /usr/share/polkit-1/actions/) * @FU_PATH_KIND_OFFLINE_TRIGGER: The file for the offline trigger (IE /system-update) * @FU_PATH_KIND_SYSFSDIR_SECURITY: The sysfs security location (IE /sys/kernel/security) + * @FU_PATH_KIND_ACPI_TABLES: The location of the ACPI tables * * Path types to use when dynamically determining a path at runtime **/ @@ -73,9 +75,11 @@ typedef enum { FU_PATH_KIND_SYSFSDIR_FW, FU_PATH_KIND_SYSFSDIR_DRIVERS, FU_PATH_KIND_SYSFSDIR_TPM, + FU_PATH_KIND_PROCFS, FU_PATH_KIND_POLKIT_ACTIONS, FU_PATH_KIND_OFFLINE_TRIGGER, FU_PATH_KIND_SYSFSDIR_SECURITY, + FU_PATH_KIND_ACPI_TABLES, /*< private >*/ FU_PATH_KIND_LAST } FuPathKind; @@ -93,6 +97,9 @@ gboolean fu_common_spawn_sync (const gchar * const *argv, gchar *fu_common_get_path (FuPathKind path_kind); gchar *fu_common_realpath (const gchar *filename, GError **error); +GPtrArray *fu_common_filename_glob (const gchar *directory, + const gchar *pattern, + GError **error); gboolean fu_common_fnmatch (const gchar *pattern, const gchar *str); gboolean fu_common_rmtree (const gchar *directory, @@ -175,6 +182,8 @@ gboolean fu_common_read_uint32_safe (const guint8 *buf, FuEndianType endian, GError **error); +void fu_byte_array_set_size (GByteArray *array, + guint length); void fu_byte_array_append_uint8 (GByteArray *array, guint8 data); void fu_byte_array_append_uint16 (GByteArray *array, @@ -219,9 +228,31 @@ gchar **fu_common_strnsplit (const gchar *str, const gchar *delimiter, gint max_tokens); gboolean fu_common_kernel_locked_down (void); +gboolean fu_common_cpuid (guint32 leaf, + guint32 *eax, + guint32 *ebx, + guint32 *ecx, + guint32 *edx, + GError **error); +gboolean fu_common_is_cpu_intel (void); gboolean fu_common_is_live_media (void); GPtrArray *fu_common_get_volumes_by_kind (const gchar *kind, GError **error); +FuVolume *fu_common_get_volume_by_device (const gchar *device, + GError **error); +FuVolume *fu_common_get_volume_by_devnum (guint32 devnum, + GError **error); FuVolume *fu_common_get_esp_for_path (const gchar *esp_path, GError **error); FuVolume *fu_common_get_esp_default (GError **error); + +guint8 fu_common_crc8 (const guint8 *buf, + gsize bufsz); +guint16 fu_common_crc16 (const guint8 *buf, + gsize bufsz); +guint32 fu_common_crc32 (const guint8 *buf, + gsize bufsz); +guint32 fu_common_crc32_full (const guint8 *buf, + gsize bufsz, + guint32 crc, + guint32 polynomial); diff --git a/libfwupdplugin/fu-device-private.h b/libfwupdplugin/fu-device-private.h index 1a3375fa3..4937e216f 100644 --- a/libfwupdplugin/fu-device-private.h +++ b/libfwupdplugin/fu-device-private.h @@ -16,12 +16,11 @@ gboolean fu_device_has_parent_guid (FuDevice *self, const gchar *guid); void fu_device_set_parent (FuDevice *self, FuDevice *parent); -guint fu_device_get_order (FuDevice *self); +gint fu_device_get_order (FuDevice *self); void fu_device_set_order (FuDevice *self, - guint order); + gint order); void fu_device_set_alternate (FuDevice *self, FuDevice *alternate); -GType fu_device_get_specialized_gtype (FuDevice *self); gboolean fu_device_ensure_id (FuDevice *self, GError **error); void fu_device_incorporate_from_component (FuDevice *device, @@ -29,3 +28,5 @@ void fu_device_incorporate_from_component (FuDevice *device, void fu_device_convert_instance_ids (FuDevice *self); gchar *fu_device_get_guids_as_str (FuDevice *self); GPtrArray *fu_device_get_possible_plugins (FuDevice *self); +void fu_device_add_possible_plugin (FuDevice *self, + const gchar *plugin); diff --git a/libfwupdplugin/fu-device.c b/libfwupdplugin/fu-device.c index 144a02ba1..71f00bc59 100644 --- a/libfwupdplugin/fu-device.c +++ b/libfwupdplugin/fu-device.c @@ -38,17 +38,15 @@ typedef struct { gchar *logical_id; gchar *proxy_guid; FuDevice *alternate; - FuDevice *parent; /* noref */ FuDevice *proxy; /* noref */ FuQuirks *quirks; - GHashTable *metadata; + GHashTable *metadata; /* (nullable) */ GRWLock metadata_mutex; GPtrArray *parent_guids; GRWLock parent_guids_mutex; - GPtrArray *children; guint remove_delay; /* ms */ guint progress; - guint order; + gint order; guint priority; guint poll_id; gboolean done_probe; @@ -75,7 +73,6 @@ enum { PROP_PHYSICAL_ID, PROP_LOGICAL_ID, PROP_QUIRKS, - PROP_PARENT, PROP_PROXY, PROP_LAST }; @@ -102,9 +99,6 @@ fu_device_get_property (GObject *object, guint prop_id, case PROP_QUIRKS: g_value_set_object (value, priv->quirks); break; - case PROP_PARENT: - g_value_set_object (value, priv->parent); - break; case PROP_PROXY: g_value_set_object (value, priv->proxy); break; @@ -132,9 +126,6 @@ fu_device_set_property (GObject *object, guint prop_id, case PROP_QUIRKS: fu_device_set_quirks (self, g_value_get_object (value)); break; - case PROP_PARENT: - fu_device_set_parent (self, g_value_get_object (value)); - break; case PROP_PROXY: fu_device_set_proxy (self, g_value_get_object (value)); break; @@ -169,12 +160,22 @@ fu_device_get_possible_plugins (FuDevice *self) * Adds a plugin name to the list of plugins that *might* be able to handle this * device. This is tyically called from a quirk handler. * - * Since: 1.3.3 + * Duplicate plugin names are ignored. + * + * Since: 1.5.1 **/ -static void +void fu_device_add_possible_plugin (FuDevice *self, const gchar *plugin) { FuDevicePrivate *priv = GET_PRIVATE (self); + + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (plugin != NULL); + + /* add if it does not already exist */ + if (g_ptr_array_find_with_equal_func (priv->possible_plugins, plugin, + g_str_equal, NULL)) + return; g_ptr_array_add (priv->possible_plugins, g_strdup (plugin)); } @@ -405,7 +406,7 @@ fu_device_set_poll_interval (FuDevice *self, guint interval) * * Since: 1.0.8 **/ -guint +gint fu_device_get_order (FuDevice *self) { FuDevicePrivate *priv = GET_PRIVATE (self); @@ -424,7 +425,7 @@ fu_device_get_order (FuDevice *self) * Since: 1.0.8 **/ void -fu_device_set_order (FuDevice *self, guint order) +fu_device_set_order (FuDevice *self, gint order) { FuDevicePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_DEVICE (self)); @@ -603,9 +604,8 @@ fu_device_set_alternate (FuDevice *self, FuDevice *alternate) FuDevice * fu_device_get_parent (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_DEVICE (self), NULL); - return priv->parent; + return FU_DEVICE (fwupd_device_get_parent (FWUPD_DEVICE (self))); } /** @@ -624,12 +624,13 @@ fu_device_get_parent (FuDevice *self) FuDevice * fu_device_get_root (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (self); + FuDevice *parent; g_return_val_if_fail (FU_IS_DEVICE (self), NULL); - while (priv->parent != NULL) { - self = priv->parent; - priv = GET_PRIVATE (self); - } + do { + parent = fu_device_get_parent (self); + if (parent != NULL) + self = parent; + } while (parent != NULL); return g_object_ref (self); } @@ -649,17 +650,8 @@ fu_device_get_root (FuDevice *self) void fu_device_set_parent (FuDevice *self, FuDevice *parent) { - FuDevicePrivate *priv = GET_PRIVATE (self); - g_return_if_fail (FU_IS_DEVICE (self)); - /* if unspecified, always child before parent */ - if (parent != NULL && - fu_device_get_order (parent) == fu_device_get_order (self)) { - g_debug ("auto-setting %s order", fu_device_get_id (parent)); - fu_device_set_order (parent, fu_device_get_order (self) + 1); - } - /* if the parent has quirks, make the child inherit it */ if (parent != NULL) { if (fu_device_get_quirks (self) == NULL && @@ -667,15 +659,7 @@ fu_device_set_parent (FuDevice *self, FuDevice *parent) fu_device_set_quirks (self, fu_device_get_quirks (parent)); } - if (priv->parent != NULL) - g_object_remove_weak_pointer (G_OBJECT (priv->parent), (gpointer *) &priv->parent); - if (parent != NULL) - g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &priv->parent); - priv->parent = parent; - - /* this is what goes over D-Bus */ - fwupd_device_set_parent_id (FWUPD_DEVICE (self), - parent != NULL ? fu_device_get_id (parent) : NULL); + fwupd_device_set_parent (FWUPD_DEVICE (self), FWUPD_DEVICE (parent)); } /** @@ -740,9 +724,8 @@ fu_device_get_proxy (FuDevice *self) GPtrArray * fu_device_get_children (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_DEVICE (self), NULL); - return priv->children; + return fwupd_device_get_children (FWUPD_DEVICE (self)); } /** @@ -759,20 +742,18 @@ void fu_device_add_child (FuDevice *self, FuDevice *child) { FuDevicePrivate *priv = GET_PRIVATE (self); + GPtrArray *children; + g_return_if_fail (FU_IS_DEVICE (self)); g_return_if_fail (FU_IS_DEVICE (child)); /* add if the child does not already exist */ - for (guint i = 0; i < priv->children->len; i++) { - FuDevice *devtmp = g_ptr_array_index (priv->children, i); - if (devtmp == child) - return; - } - g_ptr_array_add (priv->children, g_object_ref (child)); + fwupd_device_add_child (FWUPD_DEVICE (self), FWUPD_DEVICE (child)); /* ensure the parent has the MAX() of the childrens removal delay */ - for (guint i = 0; i < priv->children->len; i++) { - FuDevice *child_tmp = g_ptr_array_index (priv->children, i); + children = fu_device_get_children (self); + for (guint i = 0; i < children->len; i++) { + FuDevice *child_tmp = g_ptr_array_index (children, i); guint remove_delay = fu_device_get_remove_delay (child_tmp); if (remove_delay > priv->remove_delay) { g_debug ("setting remove delay to %u as child is greater than %u", @@ -802,15 +783,6 @@ fu_device_add_child (FuDevice *self, FuDevice *child) /* ensure the parent is also set on the child */ fu_device_set_parent (child, self); - - /* order devices so they are updated in the correct sequence */ - if (fu_device_has_flag (child, FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST)) { - if (priv->order >= fu_device_get_order (child)) - fu_device_set_order (child, priv->order + 1); - } else { - if (priv->order <= fu_device_get_order (child)) - priv->order = fu_device_get_order (child) + 1; - } } /** @@ -989,6 +961,10 @@ fu_device_set_quirk_kv (FuDevice *self, fu_device_set_summary (self, value); return TRUE; } + if (g_strcmp0 (key, FU_QUIRKS_BRANCH) == 0) { + fu_device_set_branch (self, value); + return TRUE; + } if (g_strcmp0 (key, FU_QUIRKS_VENDOR) == 0) { fu_device_set_vendor (self, value); return TRUE; @@ -1409,6 +1385,8 @@ fu_device_get_metadata (FuDevice *self, const gchar *key) g_return_val_if_fail (FU_IS_DEVICE (self), NULL); g_return_val_if_fail (key != NULL, NULL); g_return_val_if_fail (locker != NULL, NULL); + if (priv->metadata == NULL) + return NULL; return g_hash_table_lookup (priv->metadata, key); } @@ -1434,6 +1412,8 @@ fu_device_get_metadata_boolean (FuDevice *self, const gchar *key) g_return_val_if_fail (key != NULL, FALSE); g_return_val_if_fail (locker != NULL, FALSE); + if (priv->metadata == NULL) + return FALSE; tmp = g_hash_table_lookup (priv->metadata, key); if (tmp == NULL) return FALSE; @@ -1464,6 +1444,8 @@ fu_device_get_metadata_integer (FuDevice *self, const gchar *key) g_return_val_if_fail (key != NULL, G_MAXUINT); g_return_val_if_fail (locker != NULL, G_MAXUINT); + if (priv->metadata == NULL) + return G_MAXUINT; tmp = g_hash_table_lookup (priv->metadata, key); if (tmp == NULL) return G_MAXUINT; @@ -1492,6 +1474,8 @@ fu_device_remove_metadata (FuDevice *self, const gchar *key) g_return_if_fail (FU_IS_DEVICE (self)); g_return_if_fail (key != NULL); g_return_if_fail (locker != NULL); + if (priv->metadata == NULL) + return; g_hash_table_remove (priv->metadata, key); } @@ -1514,6 +1498,10 @@ fu_device_set_metadata (FuDevice *self, const gchar *key, const gchar *value) g_return_if_fail (key != NULL); g_return_if_fail (value != NULL); g_return_if_fail (locker != NULL); + if (priv->metadata == NULL) { + priv->metadata = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + } g_hash_table_insert (priv->metadata, g_strdup (key), g_strdup (value)); } @@ -1617,6 +1605,7 @@ void fu_device_set_id (FuDevice *self, const gchar *id) { FuDevicePrivate *priv = GET_PRIVATE (self); + GPtrArray *children; g_autofree gchar *id_hash = NULL; g_return_if_fail (FU_IS_DEVICE (self)); @@ -1633,8 +1622,9 @@ fu_device_set_id (FuDevice *self, const gchar *id) priv->device_id_valid = TRUE; /* ensure the parent ID is set */ - for (guint i = 0; i < priv->children->len; i++) { - FuDevice *devtmp = g_ptr_array_index (priv->children, i); + children = fu_device_get_children (self); + for (guint i = 0; i < children->len; i++) { + FuDevice *devtmp = g_ptr_array_index (children, i); fwupd_device_set_parent_id (FWUPD_DEVICE (devtmp), id_hash); } } @@ -2250,6 +2240,32 @@ fu_device_set_progress_full (FuDevice *self, gsize progress_done, gsize progress fu_device_set_progress (self, (guint) percentage); } +/** + * fu_device_sleep_with_progress: + * @self: A #FuDevice + * @delay_secs: the delay in seconds + * + * Sleeps, setting the device progress from 0..100% as time continues. + * The value is gven in whole seconds as it does not make sense to show the + * progressbar advancing so quickly for durations of less than one second. + * + * Since: 1.5.0 + **/ +void +fu_device_sleep_with_progress (FuDevice *self, guint delay_secs) +{ + gulong delay_us_pc = (delay_secs * G_USEC_PER_SEC) / 100; + + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (delay_secs > 0); + + fu_device_set_progress (self, 0); + for (guint i = 0; i < 100; i++) { + g_usleep (delay_us_pc); + fu_device_set_progress (self, i + 1); + } +} + static void fu_device_add_string (FuDevice *self, guint idt, GString *str) { @@ -2257,7 +2273,6 @@ fu_device_add_string (FuDevice *self, guint idt, GString *str) FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); FuDevicePrivate *priv = GET_PRIVATE (self); g_autofree gchar *tmp = NULL; - g_autoptr(GList) keys = NULL; g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&priv->metadata_mutex); g_return_if_fail (locker != NULL); @@ -2288,15 +2303,17 @@ fu_device_add_string (FuDevice *self, guint idt, GString *str) g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_max); fu_common_string_append_kv (str, idt + 1, "FirmwareSizeMax", sz); } - if (priv->order > 0) + if (priv->order != G_MAXINT) fu_common_string_append_ku (str, idt + 1, "Order", priv->order); if (priv->priority > 0) fu_common_string_append_ku (str, idt + 1, "Priority", priv->priority); - keys = g_hash_table_get_keys (priv->metadata); - for (GList *l = keys; l != NULL; l = l->next) { - const gchar *key = l->data; - const gchar *value = g_hash_table_lookup (priv->metadata, key); - fu_common_string_append_kv (str, idt + 1, key, value); + if (priv->metadata != NULL) { + g_autoptr(GList) keys = g_hash_table_get_keys (priv->metadata); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup (priv->metadata, key); + fu_common_string_append_kv (str, idt + 1, key, value); + } } /* subclassed */ @@ -2305,11 +2322,9 @@ fu_device_add_string (FuDevice *self, guint idt, GString *str) /* print children also */ children = fu_device_get_children (self); - if (children != NULL) { - for (guint i = 0; i < children->len; i++) { - FuDevice *child = g_ptr_array_index (children, i); - fu_device_add_string (child, idt + 1, str); - } + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index (children, i); + fu_device_add_string (child, idt + 1, str); } } @@ -2474,6 +2489,7 @@ fu_device_prepare_firmware (FuDevice *self, /* optionally subclassed */ if (klass->prepare_firmware != NULL) { + fu_device_set_status (self, FWUPD_STATUS_DECOMPRESSING); firmware = klass->prepare_firmware (self, fw, flags, error); if (firmware == NULL) return NULL; @@ -2517,6 +2533,11 @@ fu_device_prepare_firmware (FuDevice *self, * @error: A #GError * * Reads firmware from the device by calling a plugin-specific vfunc. + * The device subclass should try to ensure the firmware does not contain any + * serial numbers or user-configuration values and can be used to calculate the + * device checksum. + * + * The return value can be converted to a blob of memory using fu_firmware_write(). * * Returns: (transfer full): A #FuFirmware, or %NULL for error * @@ -2526,14 +2547,13 @@ FuFirmware * fu_device_read_firmware (FuDevice *self, GError **error) { FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + g_autoptr(GBytes) fw = NULL; g_return_val_if_fail (FU_IS_DEVICE (self), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - /* no plugin-specific method or device doesn't support */ - if (!fu_device_has_flag (self, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE) || - klass->read_firmware == NULL) { + /* device does not support reading for verification CRCs */ + if (!fu_device_has_flag (self, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -2542,7 +2562,49 @@ fu_device_read_firmware (FuDevice *self, GError **error) } /* call vfunc */ - return klass->read_firmware (self, error); + if (klass->read_firmware != NULL) + return klass->read_firmware (self, error); + + /* use the default FuFirmware when only ->dump_firmware is provided */ + fw = fu_device_dump_firmware (self, error); + if (fw == NULL) + return NULL; + return fu_firmware_new_from_bytes (fw); +} + +/** + * fu_device_dump_firmware: + * @self: A #FuDevice + * @error: A #GError + * + * Reads the raw firmware image from the device by calling a plugin-specific + * vfunc. This raw firmware image may contain serial numbers or device-specific + * configuration but should be a byte-for-byte match compared to using an + * external SPI programmer. + * + * Returns: (transfer full): A #GBytes, or %NULL for error + * + * Since: 1.5.0 + **/ +GBytes * +fu_device_dump_firmware (FuDevice *self, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* use the default FuFirmware when only ->dump_firmware is provided */ + if (klass->dump_firmware == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported"); + return NULL; + } + + /* proxy */ + return klass->dump_firmware (self, error); } /** @@ -2878,7 +2940,7 @@ fu_device_rescan (FuDevice *self, GError **error) void fu_device_convert_instance_ids (FuDevice *self) { - FuDevicePrivate *priv = GET_PRIVATE (self); + GPtrArray *children; GPtrArray *instance_ids = fwupd_device_get_instance_ids (FWUPD_DEVICE (self)); /* OEM specific hardware */ @@ -2891,8 +2953,9 @@ fu_device_convert_instance_ids (FuDevice *self) } /* convert all children too */ - for (guint i = 0; i < priv->children->len; i++) { - FuDevice *devtmp = g_ptr_array_index (priv->children, i); + children = fu_device_get_children (self); + for (guint i = 0; i < children->len; i++) { + FuDevice *devtmp = g_ptr_array_index (children, i); fu_device_convert_instance_ids (devtmp); } } @@ -2987,6 +3050,138 @@ fu_device_probe_invalidate (FuDevice *self) priv->done_setup = FALSE; } +/** + * fu_device_report_metadata_pre: + * @self: A #FuDevice + * + * Collects metadata that would be useful for debugging a failed update report. + * + * Returns: (transfer full) (nullable): A #GHashTable, or %NULL if there is no data + * + * Since: 1.5.0 + **/ +GHashTable * +fu_device_report_metadata_pre (FuDevice *self) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + g_autoptr(GHashTable) metadata = NULL; + + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + + /* not implemented */ + if (klass->report_metadata_pre == NULL) + return NULL; + + /* metadata for all devices */ + metadata = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + klass->report_metadata_pre (self, metadata); + return g_steal_pointer (&metadata); +} + +/** + * fu_device_report_metadata_post: + * @self: A #FuDevice + * + * Collects metadata that would be useful for debugging a failed update report. + * + * Returns: (transfer full) (nullable): A #GHashTable, or %NULL if there is no data + * + * Since: 1.5.0 + **/ +GHashTable * +fu_device_report_metadata_post (FuDevice *self) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + g_autoptr(GHashTable) metadata = NULL; + + g_return_val_if_fail (FU_IS_DEVICE (self), NULL); + + /* not implemented */ + if (klass->report_metadata_post == NULL) + return NULL; + + /* metadata for all devices */ + metadata = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + klass->report_metadata_post (self, metadata); + return g_steal_pointer (&metadata); +} + +/** + * fu_device_bind_driver: + * @self: A #FuDevice + * @subsystem: A subsystem string, e.g. `pci` + * @driver: A kernel module name, e.g. `tg3` + * @error: A #GError, or %NULL + * + * Binds a driver to the device, which normally means the kernel driver takes + * control of the hardware. + * + * Returns: %TRUE if driver was bound. + * + * Since: 1.5.0 + **/ +gboolean +fu_device_bind_driver (FuDevice *self, + const gchar *subsystem, + const gchar *driver, + GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (subsystem != NULL, FALSE); + g_return_val_if_fail (driver != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* not implemented */ + if (klass->bind_driver == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported"); + return FALSE; + } + + /* subclass */ + return klass->bind_driver (self, subsystem, driver, error); +} + +/** + * fu_device_unbind_driver: + * @self: A #FuDevice + * @error: A #GError, or %NULL + * + * Unbinds the driver from the device, which normally means the kernel releases + * the hardware so it can be used from userspace. + * + * If there is no driver bound then this function will return with success + * without actually doing anything. + * + * Returns: %TRUE if driver was unbound. + * + * Since: 1.5.0 + **/ +gboolean +fu_device_unbind_driver (FuDevice *self, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* not implemented */ + if (klass->unbind_driver == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported"); + return FALSE; + } + + /* subclass */ + return klass->unbind_driver (self, error); +} + /** * fu_device_incorporate: * @self: A #FuDevice @@ -3004,7 +3199,6 @@ fu_device_incorporate (FuDevice *self, FuDevice *donor) FuDevicePrivate *priv_donor = GET_PRIVATE (donor); GPtrArray *instance_ids = fu_device_get_instance_ids (donor); GPtrArray *parent_guids = fu_device_get_parent_guids (donor); - g_autoptr(GList) metadata_keys = NULL; g_return_if_fail (FU_IS_DEVICE (self)); g_return_if_fail (FU_IS_DEVICE (donor)); @@ -3029,12 +3223,14 @@ fu_device_incorporate (FuDevice *self, FuDevice *donor) fu_device_add_parent_guid (self, g_ptr_array_index (parent_guids, i)); g_rw_lock_reader_unlock (&priv_donor->parent_guids_mutex); g_rw_lock_reader_lock (&priv_donor->metadata_mutex); - metadata_keys = g_hash_table_get_keys (priv_donor->metadata); - for (GList *l = metadata_keys; l != NULL; l = l->next) { - const gchar *key = l->data; - if (g_hash_table_lookup (priv->metadata, key) == NULL) { - const gchar *value = g_hash_table_lookup (priv_donor->metadata, key); - fu_device_set_metadata (self, key, value); + if (priv->metadata != NULL) { + g_autoptr(GList) keys = g_hash_table_get_keys (priv_donor->metadata); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + if (g_hash_table_lookup (priv->metadata, key) == NULL) { + const gchar *value = g_hash_table_lookup (priv_donor->metadata, key); + fu_device_set_metadata (self, key, value); + } } } g_rw_lock_reader_unlock (&priv_donor->metadata_mutex); @@ -3134,13 +3330,6 @@ fu_device_class_init (FuDeviceClass *klass) G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_QUIRKS, pspec); - pspec = g_param_spec_object ("parent", NULL, NULL, - FU_TYPE_DEVICE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_NAME); - g_object_class_install_property (object_class, PROP_PARENT, pspec); - pspec = g_param_spec_object ("proxy", NULL, NULL, FU_TYPE_DEVICE, G_PARAM_READWRITE | @@ -3153,13 +3342,11 @@ static void fu_device_init (FuDevice *self) { FuDevicePrivate *priv = GET_PRIVATE (self); - priv->children = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + priv->order = G_MAXINT; priv->parent_guids = g_ptr_array_new_with_free_func (g_free); priv->possible_plugins = g_ptr_array_new_with_free_func (g_free); priv->retry_recs = g_ptr_array_new_with_free_func (g_free); g_rw_lock_init (&priv->parent_guids_mutex); - priv->metadata = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_free); g_rw_lock_init (&priv->metadata_mutex); } @@ -3169,20 +3356,19 @@ fu_device_finalize (GObject *object) FuDevice *self = FU_DEVICE (object); FuDevicePrivate *priv = GET_PRIVATE (self); + g_rw_lock_clear (&priv->metadata_mutex); + g_rw_lock_clear (&priv->parent_guids_mutex); + if (priv->alternate != NULL) g_object_unref (priv->alternate); - if (priv->parent != NULL) - g_object_remove_weak_pointer (G_OBJECT (priv->parent), (gpointer *) &priv->parent); if (priv->proxy != NULL) g_object_remove_weak_pointer (G_OBJECT (priv->proxy), (gpointer *) &priv->proxy); if (priv->quirks != NULL) g_object_unref (priv->quirks); if (priv->poll_id != 0) g_source_remove (priv->poll_id); - g_rw_lock_clear (&priv->metadata_mutex); - g_rw_lock_clear (&priv->parent_guids_mutex); - g_hash_table_unref (priv->metadata); - g_ptr_array_unref (priv->children); + if (priv->metadata != NULL) + g_hash_table_unref (priv->metadata); g_ptr_array_unref (priv->parent_guids); g_ptr_array_unref (priv->possible_plugins); g_ptr_array_unref (priv->retry_recs); diff --git a/libfwupdplugin/fu-device.h b/libfwupdplugin/fu-device.h index 39a492e93..376a27db5 100644 --- a/libfwupdplugin/fu-device.h +++ b/libfwupdplugin/fu-device.h @@ -64,8 +64,20 @@ struct _FuDeviceClass gboolean (*cleanup) (FuDevice *self, FwupdInstallFlags flags, GError **error); + void (*report_metadata_pre) (FuDevice *self, + GHashTable *metadata); + void (*report_metadata_post)(FuDevice *self, + GHashTable *metadata); + gboolean (*bind_driver) (FuDevice *self, + const gchar *subsystem, + const gchar *driver, + GError **error); + gboolean (*unbind_driver) (FuDevice *self, + GError **error); + GBytes *(*dump_firmware) (FuDevice *self, + GError **error); /*< private >*/ - gpointer padding[16]; + gpointer padding[11]; }; /** @@ -120,6 +132,7 @@ FuDevice *fu_device_new (void); #define fu_device_set_plugin(d,v) fwupd_device_set_plugin(FWUPD_DEVICE(d),v) #define fu_device_set_serial(d,v) fwupd_device_set_serial(FWUPD_DEVICE(d),v) #define fu_device_set_summary(d,v) fwupd_device_set_summary(FWUPD_DEVICE(d),v) +#define fu_device_set_branch(d,v) fwupd_device_set_branch(FWUPD_DEVICE(d),v) #define fu_device_set_update_message(d,v) fwupd_device_set_update_message(FWUPD_DEVICE(d),v) #define fu_device_set_update_image(d,v) fwupd_device_set_update_image(FWUPD_DEVICE(d),v) #define fu_device_set_update_error(d,v) fwupd_device_set_update_error(FWUPD_DEVICE(d),v) @@ -142,6 +155,7 @@ FuDevice *fu_device_new (void); #define fu_device_get_name(d) fwupd_device_get_name(FWUPD_DEVICE(d)) #define fu_device_get_serial(d) fwupd_device_get_serial(FWUPD_DEVICE(d)) #define fu_device_get_summary(d) fwupd_device_get_summary(FWUPD_DEVICE(d)) +#define fu_device_get_branch(d) fwupd_device_get_branch(FWUPD_DEVICE(d)) #define fu_device_get_id(d) fwupd_device_get_id(FWUPD_DEVICE(d)) #define fu_device_get_plugin(d) fwupd_device_get_plugin(FWUPD_DEVICE(d)) #define fu_device_get_update_error(d) fwupd_device_get_update_error(FWUPD_DEVICE(d)) @@ -259,10 +273,13 @@ void fu_device_set_progress (FuDevice *self, void fu_device_set_progress_full (FuDevice *self, gsize progress_done, gsize progress_total); +void fu_device_sleep_with_progress (FuDevice *self, + guint delay_secs); void fu_device_set_quirks (FuDevice *self, FuQuirks *quirks); FuQuirks *fu_device_get_quirks (FuDevice *self); FwupdRelease *fu_device_get_release_default (FuDevice *self); +GType fu_device_get_specialized_gtype (FuDevice *self); gboolean fu_device_write_firmware (FuDevice *self, GBytes *fw, FwupdInstallFlags flags, @@ -273,6 +290,8 @@ FuFirmware *fu_device_prepare_firmware (FuDevice *self, GError **error); FuFirmware *fu_device_read_firmware (FuDevice *self, GError **error); +GBytes *fu_device_dump_firmware (FuDevice *self, + GError **error); gboolean fu_device_attach (FuDevice *self, GError **error); gboolean fu_device_detach (FuDevice *self, @@ -318,3 +337,11 @@ gboolean fu_device_retry (FuDevice *self, guint count, gpointer user_data, GError **error); +gboolean fu_device_bind_driver (FuDevice *self, + const gchar *subsystem, + const gchar *driver, + GError **error); +gboolean fu_device_unbind_driver (FuDevice *self, + GError **error); +GHashTable *fu_device_report_metadata_pre (FuDevice *self); +GHashTable *fu_device_report_metadata_post (FuDevice *self); diff --git a/libfwupdplugin/fu-dfu-firmware.c b/libfwupdplugin/fu-dfu-firmware.c index bc9e47950..17c28d14c 100644 --- a/libfwupdplugin/fu-dfu-firmware.c +++ b/libfwupdplugin/fu-dfu-firmware.c @@ -181,60 +181,6 @@ fu_dfu_firmware_set_version (FuDfuFirmware *self, guint16 version) priv->version = version; } -static guint32 _crctbl[] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; - -static guint32 -fu_dfu_firmware_generate_crc32 (const guint8 *data, gsize length) -{ - guint32 accum = 0xffffffff; - for (guint i = 0; i < length; i++) - accum = _crctbl[(accum^data[i]) & 0xff] ^ (accum >> 8); - return accum; -} - typedef struct __attribute__((packed)) { guint16 release; guint16 pid; @@ -289,8 +235,8 @@ fu_dfu_firmware_parse (FuFirmware *firmware, sizeof(FuDfuFirmwareFooter), error)) return FALSE; crc = GUINT32_FROM_LE(ftr.crc); - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { - crc_new = fu_dfu_firmware_generate_crc32 (data, len - 4); + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + crc_new = ~fu_common_crc32 (data, len - 4); if (crc != crc_new) { g_set_error (error, FWUPD_ERROR, @@ -343,9 +289,7 @@ fu_dfu_firmware_add_footer (FuDfuFirmware *self, GBytes *contents, GError **erro fu_byte_array_append_uint16 (buf, priv->version, G_LITTLE_ENDIAN); g_byte_array_append (buf, (const guint8 *) "UFD", 3); fu_byte_array_append_uint8 (buf, sizeof(FuDfuFirmwareFooter)); - fu_byte_array_append_uint32 (buf, - fu_dfu_firmware_generate_crc32 (buf->data, buf->len), - G_LITTLE_ENDIAN); + fu_byte_array_append_uint32 (buf, ~fu_common_crc32 (buf->data, buf->len), G_LITTLE_ENDIAN); return g_byte_array_free_to_bytes (buf); } diff --git a/libfwupdplugin/fu-efivar.c b/libfwupdplugin/fu-efivar.c index 0bde17fd6..8e266f571 100644 --- a/libfwupdplugin/fu-efivar.c +++ b/libfwupdplugin/fu-efivar.c @@ -69,13 +69,13 @@ fu_efivar_supported (GError **error) #endif } +#ifndef _WIN32 static gboolean fu_efivar_set_immutable_fd (int fd, gboolean value, gboolean *value_old, GError **error) { -#ifndef _WIN32 guint flags; gboolean is_immutable; int rc; @@ -124,14 +124,8 @@ fu_efivar_set_immutable_fd (int fd, return FALSE; } return TRUE; -#else - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "efivarfs not currently supported on Windows"); - return FALSE; -#endif } +#endif static gboolean fu_efivar_set_immutable (const gchar *fn, @@ -328,6 +322,124 @@ fu_efivar_get_data (const gchar *guid, const gchar *name, guint8 **data, #endif } +/** + * fu_efivar_get_data_bytes: + * @guid: Globally unique identifier + * @name: Variable name + * @attr: (nullable): Attributes + * @error: A #GError + * + * Gets the data from a UEFI variable in NVRAM + * + * Returns: (transfer full): a #GBytes, or %NULL + * + * Since: 1.5.0 + **/ +GBytes * +fu_efivar_get_data_bytes (const gchar *guid, + const gchar *name, + guint32 *attr, + GError **error) +{ + guint8 *data = NULL; + gsize datasz = 0; + if (!fu_efivar_get_data (guid, name, &data, &datasz, attr, error)) + return NULL; + return g_bytes_new_take (data, datasz); +} + +/** + * fu_efivar_get_names: + * @guid: Globally unique identifier + * @error: A #GError + * + * Gets the list of names where the GUID matches. An error is set if there are + * no names matching the GUID. + * + * Returns: (transfer container) (element-type utf8): array of names + * + * Since: 1.4.7 + **/ +GPtrArray * +fu_efivar_get_names (const gchar *guid, GError **error) +{ + const gchar *name_guid; + g_autofree gchar *path = fu_efivar_get_path (); + g_autoptr(GDir) dir = NULL; + g_autoptr(GPtrArray) names = g_ptr_array_new_with_free_func (g_free); + + /* find names with matching GUID */ + dir = g_dir_open (path, 0, error); + if (dir == NULL) + return NULL; + while ((name_guid = g_dir_read_name (dir)) != NULL) { + gsize name_guidsz = strlen (name_guid); + if (name_guidsz < 38) + continue; + if (g_strcmp0 (name_guid + name_guidsz - 36, guid) == 0) { + g_ptr_array_add (names, + g_strndup (name_guid, name_guidsz - 37)); + } + } + + /* nothing found */ + if (names->len == 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "no names for GUID %s", guid); + return NULL; + } + + /* success */ + return g_steal_pointer (&names); +} + +/** + * fu_efivar_space_used: + * @error: A #GError + * + * Gets the total size used by all EFI variables. This may be less than the size reported by the + * kernel as some (hopefully small) variables are hidden from userspace. + * + * Returns: total allocated size of all visible variables, or %G_MAXUINT64 on error + * + * Since: 1.5.1 + **/ +guint64 +fu_efivar_space_used (GError **error) +{ + const gchar *fn; + guint64 total = 0; + g_autoptr(GDir) dir = NULL; + g_autofree gchar *path = fu_efivar_get_path (); + + /* stat each file */ + dir = g_dir_open (path, 0, error); + if (dir == NULL) + return G_MAXUINT64; + while ((fn = g_dir_read_name (dir)) != NULL) { + guint64 sz; + g_autofree gchar *pathfn = g_build_filename (path, fn, NULL); + g_autoptr(GFile) file = g_file_new_for_path (pathfn); + g_autoptr(GFileInfo) info = NULL; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE "," + G_FILE_ATTRIBUTE_STANDARD_SIZE, + G_FILE_QUERY_INFO_NONE, + NULL, error); + if (info == NULL) + return G_MAXUINT64; + sz = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE); + if (sz == 0) + sz = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE); + total += sz; + } + + /* success */ + return total; +} /** * fu_efivar_set_data: * @guid: Globally unique identifier @@ -413,6 +525,64 @@ fu_efivar_set_data (const gchar *guid, const gchar *name, const guint8 *data, #endif } +/** + * fu_efivar_set_data_bytes: + * @guid: Globally unique identifier + * @name: Variable name + * @bytes: a #GBytes + * @attr: Attributes + * @error: A #GError + * + * Sets the data to a UEFI variable in NVRAM + * + * Returns: %TRUE on success + * + * Since: 1.5.0 + **/ +gboolean +fu_efivar_set_data_bytes (const gchar *guid, const gchar *name, GBytes *bytes, + guint32 attr, GError **error) +{ + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data (bytes, &bufsz); + return fu_efivar_set_data (guid, name, buf, bufsz, attr, error); +} + +/** + * fu_efivar_secure_boot_enabled_full: + * @error: A #GError + * + * Determines if secure boot was enabled + * + * Returns: %TRUE on success + * + * Since: 1.5.0 + **/ +gboolean +fu_efivar_secure_boot_enabled_full (GError **error) +{ + gsize data_size = 0; + g_autofree guint8 *data = NULL; + + if (!fu_efivar_get_data (FU_EFIVAR_GUID_EFI_GLOBAL, "SecureBoot", + &data, &data_size, NULL, NULL)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "SecureBoot is not available"); + return FALSE; + } + if (data_size >= 1 && data[0] & 1) + return TRUE; + + /* available, but not enabled */ + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "SecureBoot is not enabled"); + return FALSE; +} + /** * fu_efivar_secure_boot_enabled: * @@ -425,13 +595,5 @@ fu_efivar_set_data (const gchar *guid, const gchar *name, const guint8 *data, gboolean fu_efivar_secure_boot_enabled (void) { - gsize data_size = 0; - g_autofree guint8 *data = NULL; - - if (!fu_efivar_get_data (FU_EFIVAR_GUID_EFI_GLOBAL, "SecureBoot", - &data, &data_size, NULL, NULL)) - return FALSE; - if (data_size >= 1 && data[0] & 1) - return TRUE; - return FALSE; + return fu_efivar_secure_boot_enabled_full (NULL); } diff --git a/libfwupdplugin/fu-efivar.h b/libfwupdplugin/fu-efivar.h index 6bea1768c..9269db117 100644 --- a/libfwupdplugin/fu-efivar.h +++ b/libfwupdplugin/fu-efivar.h @@ -10,19 +10,21 @@ #include #define FU_EFIVAR_GUID_EFI_GLOBAL "8be4df61-93ca-11d2-aa0d-00e098032b8c" -#define FU_EFIVAR_GUID_FWUPDATE "0abba7dc-e516-4167-bbf5-4d9d1c739416" +#define FU_EFIVAR_GUID_FWUPDATE "0abba7dc-e516-4167-bbf5-4d9d1c739416" #define FU_EFIVAR_GUID_UX_CAPSULE "3b8c8162-188c-46a4-aec9-be43f1d65697" #define FU_EFIVAR_GUID_SECURITY_DATABASE "d719b2cb-3d3a-4596-a3bc-dad00e67656f" +#define FU_EFIVAR_GUID_UX_CAPSULE "3b8c8162-188c-46a4-aec9-be43f1d65697" #define FU_EFIVAR_ATTR_NON_VOLATILE (1 << 0) #define FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS (1 << 1) -#define FU_EFIVAR_ATTR_RUNTIME_ACCESS (1 << 2) +#define FU_EFIVAR_ATTR_RUNTIME_ACCESS (1 << 2) #define FU_EFIVAR_ATTR_HARDWARE_ERROR_RECORD (1 << 3) #define FU_EFIVAR_ATTR_AUTHENTICATED_WRITE_ACCESS (1 << 4) #define FU_EFIVAR_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS (1 << 5) #define FU_EFIVAR_ATTR_APPEND_WRITE (1 << 6) gboolean fu_efivar_supported (GError **error); +guint64 fu_efivar_space_used (GError **error); gboolean fu_efivar_exists (const gchar *guid, const gchar *name); gboolean fu_efivar_get_data (const gchar *guid, @@ -31,16 +33,28 @@ gboolean fu_efivar_get_data (const gchar *guid, gsize *data_sz, guint32 *attr, GError **error); +GBytes *fu_efivar_get_data_bytes (const gchar *guid, + const gchar *name, + guint32 *attr, + GError **error); gboolean fu_efivar_set_data (const gchar *guid, const gchar *name, const guint8 *data, gsize sz, guint32 attr, GError **error); +gboolean fu_efivar_set_data_bytes (const gchar *guid, + const gchar *name, + GBytes *bytes, + guint32 attr, + GError **error); gboolean fu_efivar_delete (const gchar *guid, const gchar *name, GError **error); gboolean fu_efivar_delete_with_glob (const gchar *guid, const gchar *name_glob, GError **error); -gboolean fu_efivar_secure_boot_enabled (void); +GPtrArray *fu_efivar_get_names (const gchar *guid, + GError **error); +gboolean fu_efivar_secure_boot_enabled (void); +gboolean fu_efivar_secure_boot_enabled_full(GError **error); diff --git a/libfwupdplugin/fu-firmware-image.c b/libfwupdplugin/fu-firmware-image.c index c23ceac36..a7c3034bc 100644 --- a/libfwupdplugin/fu-firmware-image.c +++ b/libfwupdplugin/fu-firmware-image.c @@ -22,8 +22,10 @@ typedef struct { gchar *id; GBytes *bytes; guint64 addr; + guint64 offset; guint64 idx; gchar *version; + gchar *filename; } FuFirmwareImagePrivate; G_DEFINE_TYPE_WITH_PRIVATE (FuFirmwareImage, fu_firmware_image, G_TYPE_OBJECT) @@ -65,6 +67,42 @@ fu_firmware_image_set_version (FuFirmwareImage *self, const gchar *version) priv->version = g_strdup (version); } +/** + * fu_firmware_image_get_filename: + * @self: A #FuFirmwareImage + * + * Gets an optional filename that represents the image source or destination. + * + * Returns: a string, or %NULL + * + * Since: 1.5.0 + **/ +const gchar * +fu_firmware_image_get_filename (FuFirmwareImage *self) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), NULL); + return priv->filename; +} + +/** + * fu_firmware_image_set_filename: + * @self: A #FuFirmwareImage + * @filename: (nullable): A string filename, or %NULL + * + * Sets an optional filename that represents the image source or destination. + * + * Since: 1.5.0 + **/ +void +fu_firmware_image_set_filename (FuFirmwareImage *self, const gchar *filename) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); + g_free (priv->filename); + priv->filename = g_strdup (filename); +} + /** * fu_firmware_image_set_id: * @self: a #FuPlugin @@ -134,6 +172,41 @@ fu_firmware_image_get_addr (FuFirmwareImage *self) return priv->addr; } +/** + * fu_firmware_image_set_offset: + * @self: a #FuPlugin + * @offset: integer + * + * Sets the base offset of the image. + * + * Since: 1.5.0 + **/ +void +fu_firmware_image_set_offset (FuFirmwareImage *self, guint64 offset) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); + priv->offset = offset; +} + +/** + * fu_firmware_image_get_offset: + * @self: a #FuPlugin + * + * Gets the base offset of the image. + * + * Returns: integer + * + * Since: 1.5.0 + **/ +guint64 +fu_firmware_image_get_offset (FuFirmwareImage *self) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), G_MAXUINT64); + return priv->offset; +} + /** * fu_firmware_image_set_idx: * @self: a #FuPlugin @@ -188,6 +261,135 @@ fu_firmware_image_set_bytes (FuFirmwareImage *self, GBytes *bytes) priv->bytes = g_bytes_ref (bytes); } +/** + * fu_firmware_image_get_bytes: + * @self: a #FuPlugin + * + * Gets the data set using fu_firmware_image_set_bytes(). + * + * This should only really be used by objects subclassing #FuFirmwareImage as + * images are normally exported to a file using fu_firmware_image_write(). + * + * Returns: (transfer full): a #GBytes of the data, or %NULL if the bytes is not set + * + * Since: 1.5.0 + **/ +GBytes * +fu_firmware_image_get_bytes (FuFirmwareImage *self) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), NULL); + if (priv->bytes == NULL) + return NULL; + return g_bytes_ref (priv->bytes); +} + +/** + * fu_firmware_image_parse: + * @self: A #FuFirmwareImage + * @fw: A #GBytes + * @flags: some #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: A #GError, or %NULL + * + * Parses a firmware image, typically checking image CRCs and/or headers. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_image_parse (FuFirmwareImage *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuFirmwareImageClass *klass = FU_FIRMWARE_IMAGE_GET_CLASS (self); + + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), FALSE); + g_return_val_if_fail (fw != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* subclassed */ + if (klass->parse != NULL) + return klass->parse (self, fw, flags, error); + + /* just add entire blob */ + fu_firmware_image_set_bytes (self, fw); + return TRUE; +} + +/** + * fu_firmware_image_build: + * @self: A #FuFirmwareImage + * @n: A #XbNode + * @error: A #GError, or %NULL + * + * Builds a firmware image from an XML manifest. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_image_build (FuFirmwareImage *self, XbNode *n, GError **error) +{ + FuFirmwareImageClass *klass = FU_FIRMWARE_IMAGE_GET_CLASS (self); + guint64 tmpval; + const gchar *tmp; + g_autoptr(XbNode) data = NULL; + + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), FALSE); + g_return_val_if_fail (XB_IS_NODE (n), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + tmp = xb_node_query_text (n, "version", NULL); + if (tmp != NULL) + fu_firmware_image_set_version (self, tmp); + tmp = xb_node_query_text (n, "id", NULL); + if (tmp != NULL) + fu_firmware_image_set_id (self, tmp); + tmpval = xb_node_query_text_as_uint (n, "idx", NULL); + if (tmpval != G_MAXUINT64) + fu_firmware_image_set_idx (self, tmpval); + tmpval = xb_node_query_text_as_uint (n, "addr", NULL); + if (tmpval != G_MAXUINT64) + fu_firmware_image_set_addr (self, tmpval); + tmpval = xb_node_query_text_as_uint (n, "offset", NULL); + if (tmpval != G_MAXUINT64) + fu_firmware_image_set_offset (self, tmpval); + tmp = xb_node_query_text (n, "filename", NULL); + if (tmp != NULL) { + g_autoptr(GBytes) blob = NULL; + blob = fu_common_get_contents_bytes (tmp, error); + if (blob == NULL) + return FALSE; + fu_firmware_image_set_bytes (self, blob); + fu_firmware_image_set_filename (self, tmp); + } + data = xb_node_query_first (n, "data", NULL); + if (data != NULL && xb_node_get_text (data) != NULL) { + gsize bufsz = 0; + g_autofree guchar *buf = NULL; + g_autoptr(GBytes) blob = NULL; + buf = g_base64_decode (xb_node_get_text (data), &bufsz); + blob = g_bytes_new (buf, bufsz); + fu_firmware_image_set_bytes (self, blob); + } else if (data != NULL) { + g_autoptr(GBytes) blob = NULL; + blob = g_bytes_new (NULL, 0); + fu_firmware_image_set_bytes (self, blob); + } + + /* subclassed */ + if (klass->build != NULL) { + if (!klass->build (self, n, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + /** * fu_firmware_image_write: * @self: a #FuPlugin @@ -297,8 +499,12 @@ fu_firmware_image_add_string (FuFirmwareImage *self, guint idt, GString *str) fu_common_string_append_kx (str, idt, "Index", priv->idx); if (priv->addr != 0x0) fu_common_string_append_kx (str, idt, "Address", priv->addr); + if (priv->offset != 0x0) + fu_common_string_append_kx (str, idt, "Offset", priv->offset); if (priv->version != NULL) fu_common_string_append_kv (str, idt, "Version", priv->version); + if (priv->filename != NULL) + fu_common_string_append_kv (str, idt, "Filename", priv->filename); if (priv->bytes != NULL) { fu_common_string_append_kx (str, idt, "Data", g_bytes_get_size (priv->bytes)); @@ -339,6 +545,7 @@ fu_firmware_image_finalize (GObject *object) FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_free (priv->id); g_free (priv->version); + g_free (priv->filename); if (priv->bytes != NULL) g_bytes_unref (priv->bytes); G_OBJECT_CLASS (fu_firmware_image_parent_class)->finalize (object); diff --git a/libfwupdplugin/fu-firmware-image.h b/libfwupdplugin/fu-firmware-image.h index 7c946e6e8..ebbae446d 100644 --- a/libfwupdplugin/fu-firmware-image.h +++ b/libfwupdplugin/fu-firmware-image.h @@ -8,6 +8,7 @@ #include #include +#include #define FU_TYPE_FIRMWARE_IMAGE (fu_firmware_image_get_type ()) G_DECLARE_DERIVABLE_TYPE (FuFirmwareImage, fu_firmware_image, FU, FIRMWARE_IMAGE, GObject) @@ -24,8 +25,11 @@ struct _FuFirmwareImageClass GString *str); GBytes *(*write) (FuFirmwareImage *self, GError **error); + gboolean (*build) (FuFirmwareImage *self, + XbNode *n, + GError **error); /*< private >*/ - gpointer padding[28]; + gpointer padding[27]; }; #define FU_FIRMWARE_IMAGE_ID_PAYLOAD "payload" @@ -38,17 +42,31 @@ gchar *fu_firmware_image_to_string (FuFirmwareImage *self); const gchar *fu_firmware_image_get_version (FuFirmwareImage *self); void fu_firmware_image_set_version (FuFirmwareImage *self, const gchar *version); +const gchar *fu_firmware_image_get_filename (FuFirmwareImage *self); +void fu_firmware_image_set_filename (FuFirmwareImage *self, + const gchar *filename); const gchar *fu_firmware_image_get_id (FuFirmwareImage *self); void fu_firmware_image_set_id (FuFirmwareImage *self, const gchar *id); guint64 fu_firmware_image_get_addr (FuFirmwareImage *self); void fu_firmware_image_set_addr (FuFirmwareImage *self, guint64 addr); +guint64 fu_firmware_image_get_offset (FuFirmwareImage *self); +void fu_firmware_image_set_offset (FuFirmwareImage *self, + guint64 offset); guint64 fu_firmware_image_get_idx (FuFirmwareImage *self); void fu_firmware_image_set_idx (FuFirmwareImage *self, guint64 idx); +GBytes *fu_firmware_image_get_bytes (FuFirmwareImage *self); void fu_firmware_image_set_bytes (FuFirmwareImage *self, GBytes *bytes); +gboolean fu_firmware_image_parse (FuFirmwareImage *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error); +gboolean fu_firmware_image_build (FuFirmwareImage *self, + XbNode *n, + GError **error); GBytes *fu_firmware_image_write (FuFirmwareImage *self, GError **error); GBytes *fu_firmware_image_write_chunk (FuFirmwareImage *self, diff --git a/libfwupdplugin/fu-firmware.c b/libfwupdplugin/fu-firmware.c index d2ddcd76e..f7c9fcbbf 100644 --- a/libfwupdplugin/fu-firmware.c +++ b/libfwupdplugin/fu-firmware.c @@ -21,6 +21,7 @@ */ typedef struct { + FuFirmwareFlags flags; GPtrArray *images; /* FuFirmwareImage */ gchar *version; } FuFirmwarePrivate; @@ -28,6 +29,85 @@ typedef struct { G_DEFINE_TYPE_WITH_PRIVATE (FuFirmware, fu_firmware, G_TYPE_OBJECT) #define GET_PRIVATE(o) (fu_firmware_get_instance_private (o)) +/** + * fu_firmware_flag_to_string: + * @flag: A #FuFirmwareFlags, e.g. %FU_FIRMWARE_FLAG_DEDUPE_ID + * + * Converts a #FuFirmwareFlags to a string. + * + * Return value: identifier string + * + * Since: 1.5.0 + **/ +const gchar * +fu_firmware_flag_to_string (FuFirmwareFlags flag) +{ + if (flag == FU_FIRMWARE_FLAG_NONE) + return "none"; + if (flag == FU_FIRMWARE_FLAG_DEDUPE_ID) + return "dedupe-id"; + if (flag == FU_FIRMWARE_FLAG_DEDUPE_IDX) + return "dedupe-idx"; + return NULL; +} + +/** + * fu_firmware_flag_from_string: + * @flag: A string, e.g. `dedupe-id` + * + * Converts a string to a #FuFirmwareFlags. + * + * Return value: enumerated value + * + * Since: 1.5.0 + **/ +FuFirmwareFlags +fu_firmware_flag_from_string (const gchar *flag) +{ + if (g_strcmp0 (flag, "dedupe-id") == 0) + return FU_FIRMWARE_FLAG_DEDUPE_ID; + if (g_strcmp0 (flag, "dedupe-idx") == 0) + return FU_FIRMWARE_FLAG_DEDUPE_IDX; + return FU_FIRMWARE_FLAG_NONE; +} + +/** + * fu_firmware_add_flag: + * @firmware: A #FuFirmware + * @flag: the #FuFirmwareFlags + * + * Adds a specific firmware flag to the firmware. + * + * Since: 1.5.0 + **/ +void +fu_firmware_add_flag (FuFirmware *firmware, FuFirmwareFlags flag) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (firmware); + g_return_if_fail (FU_IS_FIRMWARE (firmware)); + priv->flags |= flag; +} + + +/** + * fu_firmware_has_flag: + * @firmware: A #FuFirmware + * @flag: the #FuFirmwareFlags + * + * Finds if the firmware has a specific firmware flag. + * + * Returns: %TRUE if the flag is set + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_has_flag (FuFirmware *firmware, FuFirmwareFlags flag) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (firmware); + g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE); + return (priv->flags & flag) > 0; +} + /** * fu_firmware_get_version: * @self: A #FuFirmware @@ -159,6 +239,108 @@ fu_firmware_parse (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError return fu_firmware_parse_full (self, fw, 0x0, 0x0, flags, error); } +/** + * fu_firmware_build: + * @self: A #FuFirmware + * @n: A #XbNode + * @error: A #GError, or %NULL + * + * Builds a firmware from an XML manifest. The manifest would typically have the + * following form: + * + * |[ + * + * + * 1.2.3 + * + * 7.8.9 + * stage1 + * 0x01 + * stage1.bin + * + * + * stage2 + * + * + * + * ape + * 0x7 + * aGVsbG8gd29ybGQ= + * + * + * ]| + * + * This would be used in a build-system to merge images from generated files: + * `fwupdtool firmware-build fw.builder.xml test.fw` + * + * Static binary content can be specified in the `/` sections and + * is encoded as base64 text if not empty. + * + * Additionally, extra nodes can be included under `` and `` + * which can be parsed by the subclassed objects. You should verify the + * subclassed object `FuFirmwareImage->build` vfunc for the specific additional + * options supported. + * + * Plugins should manually g_type_ensure() subclassed image objects if not + * constructed as part of the plugin fu_plugin_init() or fu_plugin_setup() + * functions. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_build (FuFirmware *self, XbNode *n, GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self); + const gchar *tmp; + g_autoptr(GPtrArray) xb_images = NULL; + + g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); + g_return_val_if_fail (XB_IS_NODE (n), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* set attributes */ + tmp = xb_node_query_text (n, "version", NULL); + if (tmp != NULL) + fu_firmware_set_version (self, tmp); + + /* parse images */ + xb_images = xb_node_query (n, "image", 0, NULL); + if (xb_images != NULL) { + for (guint i = 0; i < xb_images->len; i++) { + XbNode *xb_image = g_ptr_array_index (xb_images, i); + g_autoptr(FuFirmwareImage) img = NULL; + tmp = xb_node_get_attr (xb_image, "gtype"); + if (tmp != NULL) { + GType gtype = g_type_from_name (tmp); + if (gtype == G_TYPE_INVALID) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not registered", tmp); + return FALSE; + } + img = g_object_new (gtype, NULL); + } else { + img = fu_firmware_image_new (NULL); + } + if (!fu_firmware_image_build (img, xb_image, error)) + return FALSE; + fu_firmware_add_image (self, img); + } + } + + /* subclassed */ + if (klass->build != NULL) { + if (!klass->build (self, n, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + /** * fu_firmware_parse_file: * @self: A #FuFirmware @@ -245,7 +427,8 @@ fu_firmware_write_file (FuFirmware *self, GFile *file, GError **error) * * Adds an image to the firmware. * - * If an image with the same ID is already present it is replaced. + * If %FU_FIRMWARE_FLAG_DEDUPE_ID is set, an image with the same ID is already + * present it is replaced. * * Since: 1.3.1 **/ @@ -255,9 +438,118 @@ fu_firmware_add_image (FuFirmware *self, FuFirmwareImage *img) FuFirmwarePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_FIRMWARE (self)); g_return_if_fail (FU_IS_FIRMWARE_IMAGE (img)); + + /* dedupe */ + for (guint i = 0; i < priv->images->len; i++) { + FuFirmwareImage *img_tmp = g_ptr_array_index (priv->images, i); + if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_ID) { + if (g_strcmp0 (fu_firmware_image_get_id (img_tmp), + fu_firmware_image_get_id (img)) == 0) { + g_ptr_array_remove_index (priv->images, i); + break; + } + } + if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_IDX) { + if (fu_firmware_image_get_idx (img_tmp) == + fu_firmware_image_get_idx (img)) { + g_ptr_array_remove_index (priv->images, i); + break; + } + } + } + g_ptr_array_add (priv->images, g_object_ref (img)); } +/** + * fu_firmware_remove_image: + * @self: a #FuPlugin + * @img: A #FuFirmwareImage + * @error: A #GError, or %NULL + * + * Remove an image from the firmware. + * + * Returns: %TRUE if the image was removed + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_remove_image (FuFirmware *self, FuFirmwareImage *img, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (self); + + g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (img), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (g_ptr_array_remove (priv->images, img)) + return TRUE; + + /* did not exist */ + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "image %s not found in firmware", + fu_firmware_image_get_id (img)); + return FALSE; +} + +/** + * fu_firmware_remove_image_by_idx: + * @self: a #FuPlugin + * @idx: index + * @error: A #GError, or %NULL + * + * Removes the first image from the firmware matching the index. + * + * Returns: %TRUE if an image was removed + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_remove_image_by_idx (FuFirmware *self, guint64 idx, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (self); + g_autoptr(FuFirmwareImage) img = NULL; + + g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + img = fu_firmware_get_image_by_idx (self, idx, error); + if (img == NULL) + return FALSE; + g_ptr_array_remove (priv->images, img); + return TRUE; +} + +/** + * fu_firmware_remove_image_by_id: + * @self: a #FuPlugin + * @id: (nullable): image ID, e.g. "config" + * @error: A #GError, or %NULL + * + * Removes the first image from the firmware matching the ID. + * + * Returns: %TRUE if an image was removed + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_remove_image_by_id (FuFirmware *self, const gchar *id, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (self); + g_autoptr(FuFirmwareImage) img = NULL; + + g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + img = fu_firmware_get_image_by_id (self, id, error); + if (img == NULL) + return FALSE; + g_ptr_array_remove (priv->images, img); + return TRUE; +} + /** * fu_firmware_get_images: * @self: a #FuFirmware @@ -464,6 +756,18 @@ fu_firmware_to_string (FuFirmware *self) /* subclassed type */ fu_common_string_append_kv (str, 0, G_OBJECT_TYPE_NAME (self), NULL); + if (priv->flags != FU_FIRMWARE_FLAG_NONE) { + g_autoptr(GString) tmp = g_string_new (""); + for (guint i = 0; i < 64; i++) { + if ((priv->flags & ((guint64) 1 << i)) == 0) + continue; + g_string_append_printf (tmp, "%s|", + fu_firmware_flag_to_string ((guint64) 1 << i)); + } + if (tmp->len > 0) + g_string_truncate (tmp, tmp->len - 1); + fu_common_string_append_kv (str, 0, "Flags", tmp->str); + } if (priv->version != NULL) fu_common_string_append_kv (str, 0, "Version", priv->version); diff --git a/libfwupdplugin/fu-firmware.h b/libfwupdplugin/fu-firmware.h index e6b3e65c1..c31bb2bee 100644 --- a/libfwupdplugin/fu-firmware.h +++ b/libfwupdplugin/fu-firmware.h @@ -32,21 +32,47 @@ struct _FuFirmwareClass GBytes *fw, FwupdInstallFlags flags, GError **error); + gboolean (*build) (FuFirmware *self, + XbNode *n, + GError **error); /*< private >*/ - gpointer padding[28]; + gpointer padding[27]; }; +/** + * FuFirmwareFlags: + * @FU_FIRMWARE_FLAG_NONE: No flags set + * @FU_FIRMWARE_FLAG_DEDUPE_ID: Dedupe imges by ID + * @FU_FIRMWARE_FLAG_DEDUPE_IDX: Dedupe imges by IDX + * + * The firmware flags. + **/ +#define FU_FIRMWARE_FLAG_NONE (0u) /* Since: 1.5.0 */ +#define FU_FIRMWARE_FLAG_DEDUPE_ID (1u << 0) /* Since: 1.5.0 */ +#define FU_FIRMWARE_FLAG_DEDUPE_IDX (1u << 1) /* Since: 1.5.0 */ +typedef guint64 FuFirmwareFlags; + +const gchar *fu_firmware_flag_to_string (FuFirmwareFlags flag); +FuFirmwareFlags fu_firmware_flag_from_string (const gchar *flag); + FuFirmware *fu_firmware_new (void); FuFirmware *fu_firmware_new_from_bytes (GBytes *fw); gchar *fu_firmware_to_string (FuFirmware *self); const gchar *fu_firmware_get_version (FuFirmware *self); void fu_firmware_set_version (FuFirmware *self, const gchar *version); +void fu_firmware_add_flag (FuFirmware *firmware, + FuFirmwareFlags flag); +gboolean fu_firmware_has_flag (FuFirmware *firmware, + FuFirmwareFlags flag); gboolean fu_firmware_tokenize (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error); +gboolean fu_firmware_build (FuFirmware *self, + XbNode *n, + GError **error); gboolean fu_firmware_parse (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, @@ -69,6 +95,15 @@ gboolean fu_firmware_write_file (FuFirmware *self, void fu_firmware_add_image (FuFirmware *self, FuFirmwareImage *img); +gboolean fu_firmware_remove_image (FuFirmware *self, + FuFirmwareImage *img, + GError **error); +gboolean fu_firmware_remove_image_by_idx (FuFirmware *self, + guint64 idx, + GError **error); +gboolean fu_firmware_remove_image_by_id (FuFirmware *self, + const gchar *id, + GError **error); GPtrArray *fu_firmware_get_images (FuFirmware *self); FuFirmwareImage *fu_firmware_get_image_by_id (FuFirmware *self, const gchar *id, diff --git a/libfwupdplugin/fu-fmap-firmware.c b/libfwupdplugin/fu-fmap-firmware.c new file mode 100644 index 000000000..d41ac5f83 --- /dev/null +++ b/libfwupdplugin/fu-fmap-firmware.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-fmap-firmware.h" + +#define FMAP_SIGNATURE "__FMAP__" +#define FMAP_AREANAME "FMAP" + +G_DEFINE_TYPE (FuFmapFirmware, fu_fmap_firmware, FU_TYPE_FIRMWARE) + +/* returns size of fmap data structure if successful, <0 to indicate error */ +static gint +fmap_size (FuFmap *fmap) +{ + if (fmap == NULL) + return -1; + + return sizeof (*fmap) + (fmap->nareas * sizeof (FuFmap)); +} + +/* brute force linear search */ +static gboolean +fmap_lsearch (const guint8 *image, gsize len, gsize *offset, GError **error) +{ + gsize i; + gboolean fmap_found = FALSE; + + if (offset == NULL) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "offset return not valid"); + return FALSE; + } + + for (i = 0; i < len - strlen(FMAP_SIGNATURE); i++) { + if (!memcmp(&image[i], + FMAP_SIGNATURE, + strlen(FMAP_SIGNATURE))) { + fmap_found = TRUE; + break; + } + } + + if (!fmap_found) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "fmap not found using linear search"); + return FALSE; + } + + if (i + fmap_size ((FuFmap *)&image[i]) > len) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "malformed fmap too close to end of image"); + return FALSE; + } + + *offset = i; + return TRUE; +} + +/* if image length is a power of 2, use binary search */ +static gboolean +fmap_bsearch (const guint8 *image, gsize len, gsize *offset, GError **error) +{ + gsize i; + gboolean fmap_found = FALSE; + + if (offset == NULL) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "offset return not valid"); + return FALSE; + } + + /* + * For efficient operation, we start with the largest stride possible + * and then decrease the stride on each iteration. Also, check for a + * remainder when modding the offset with the previous stride. This + * makes it so that each offset is only checked once. + */ + for (gint stride = len / 2; stride >= 1; stride /= 2) { + if (fmap_found) + break; + + for (i = 0; i < len - strlen(FMAP_SIGNATURE); i += stride) { + if ((i % (stride * 2) == 0) && (i != 0)) + continue; + if (!memcmp (&image[i], + FMAP_SIGNATURE, + strlen (FMAP_SIGNATURE))) { + fmap_found = TRUE; + break; + } + } + } + + if (!fmap_found) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "fmap not found using binary search"); + return FALSE; + } + + if (i + fmap_size ((FuFmap *)&image[i]) > len) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "malformed fmap too close to end of image"); + return FALSE; + } + + *offset = i; + return TRUE; +} + +static gint +popcnt (guint u) +{ + gint count; + + /* K&R method */ + for (count = 0; u; count++) + u &= (u - 1); + + return count; +} + +static gboolean +fmap_find (const guint8 *image, gsize image_len, gsize *offset, GError **error) +{ + if (image == NULL || image_len == 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid image"); + return FALSE; + } + + if (popcnt (image_len) == 1) { + if (!fmap_bsearch (image, image_len, offset, error)) { + g_prefix_error (error, "failed fmap_find using bsearch: "); + return FALSE; + } + } else { + if (!fmap_lsearch (image, image_len, offset, error)) { + g_prefix_error (error, "failed fmap_find using lsearch: "); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +fu_fmap_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + FuFmapFirmwareClass *klass_firmware = FU_FMAP_FIRMWARE_GET_CLASS (firmware); + gsize image_len; + guint8 *image = (guint8 *)g_bytes_get_data (fw, &image_len); + gsize offset; + const FuFmap *fmap; + + /* corrupt */ + if (g_bytes_get_size (fw) < sizeof (FuFmap)) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware too small for fmap"); + return FALSE; + } + + if (!fmap_find (image, image_len, &offset, error)) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot find fmap in image"); + return FALSE; + } + + fmap = (const FuFmap *)(image + offset); + + if (fmap->size != image_len) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "file size incorrect, expected 0x%04x got 0x%04x", + (guint) fmap->size, + (guint) image_len); + return FALSE; + } + + if (fmap->nareas < 1) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "number of areas too small, got %" G_GUINT16_FORMAT, + fmap->nareas); + return FALSE; + } + + for (gsize i = 0; i < fmap->nareas; i++) { + const FuFmapArea *area = &fmap->areas[i]; + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(GBytes) bytes = NULL; + + img = fu_firmware_image_new (NULL); + bytes = g_bytes_new_from_bytes (fw, + (gsize) area->offset, + (gsize) area->size); + fu_firmware_image_set_id (img, (const gchar *) area->name); + fu_firmware_image_set_idx (img, i + 1); + fu_firmware_image_set_addr (img, (guint64) area->offset); + fu_firmware_image_set_bytes (img, bytes); + fu_firmware_add_image (firmware, img); + + if (g_strcmp0 ((const char *)area->name, FMAP_AREANAME) == 0) { + g_autofree gchar *version = g_strdup_printf ("%d.%d", + fmap->ver_major, + fmap->ver_minor); + fu_firmware_image_set_version (img, version); + } + } + + /* subclassed */ + if (klass_firmware->parse != NULL) { + if (!klass_firmware->parse (firmware, fw, addr_start, addr_end, flags, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_fmap_firmware_init (FuFmapFirmware *self) +{ +} + +static void +fu_fmap_firmware_class_init (FuFmapFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_fmap_firmware_parse; +} + +/** + * fu_fmap_firmware_new + * + * Creates a new #FuFirmware of sub type fmap + * + * Since: 1.5.0 + **/ +FuFirmware * +fu_fmap_firmware_new (void) +{ + return FU_FIRMWARE (g_object_new (FU_TYPE_FMAP_FIRMWARE, NULL)); +} diff --git a/libfwupdplugin/fu-fmap-firmware.h b/libfwupdplugin/fu-fmap-firmware.h new file mode 100644 index 000000000..54ad5e6f5 --- /dev/null +++ b/libfwupdplugin/fu-fmap-firmware.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_FMAP_FIRMWARE_STRLEN 32 /* maximum length for strings, */ + /* including null-terminator */ + +#define FU_TYPE_FMAP_FIRMWARE (fu_fmap_firmware_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FuFmapFirmware, fu_fmap_firmware, FU, FMAP_FIRMWARE, FuFirmware) + +struct _FuFmapFirmwareClass +{ + FuFirmwareClass parent_class; + gboolean (*parse) (FuFirmware *self, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error); + /*< private >*/ + gpointer padding[14]; +}; + +/* mapping of volatile and static regions in firmware binary */ +typedef struct __attribute__((packed)) { + guint32 offset; /* offset relative to base */ + guint32 size; /* size in bytes */ + guint8 name[FU_FMAP_FIRMWARE_STRLEN]; /* descriptive name */ + guint16 flags; /* flags for this area */ +} FuFmapArea; + +typedef struct __attribute__((packed)) { + guint8 signature[8]; /* "__FMAP__" (0x5F5F464D41505F5F) */ + guint8 ver_major; /* major version */ + guint8 ver_minor; /* minor version */ + guint64 base; /* address of the firmware binary */ + guint32 size; /* size of firmware binary in bytes */ + guint8 name[FU_FMAP_FIRMWARE_STRLEN]; /* name of this firmware binary */ + guint16 nareas; /* number of areas described by + areas[] below */ + FuFmapArea areas[]; +} FuFmap; + +FuFirmware *fu_fmap_firmware_new (void); diff --git a/libfwupdplugin/fu-hid-device.c b/libfwupdplugin/fu-hid-device.c index e2ab80b32..0568e5d4d 100644 --- a/libfwupdplugin/fu-hid-device.c +++ b/libfwupdplugin/fu-hid-device.c @@ -17,6 +17,8 @@ #define FU_HID_REPORT_TYPE_OUTPUT 0x02 #define FU_HID_REPORT_TYPE_FEATURE 0x03 +#define FU_HID_DEVICE_RETRIES 10 + /** * SECTION:fu-hid-device * @short_description: a HID device @@ -190,6 +192,63 @@ fu_hid_device_get_interface (FuHidDevice *self) return priv->interface; } +typedef struct { + guint8 value; + guint8 *buf; + gsize bufsz; + guint timeout; + FuHidDeviceFlags flags; +} FuHidDeviceRetryHelper; + +static gboolean +fu_hid_device_set_report_internal (FuHidDevice *self, + FuHidDeviceRetryHelper *helper, + GError **error) +{ + FuHidDevicePrivate *priv = GET_PRIVATE (self); + GUsbDevice *usb_device; + gsize actual_len = 0; + guint16 wvalue = (FU_HID_REPORT_TYPE_OUTPUT << 8) | helper->value; + + /* special case */ + if (helper->flags & FU_HID_DEVICE_FLAG_IS_FEATURE) + wvalue = (FU_HID_REPORT_TYPE_FEATURE << 8) | helper->value; + + if (g_getenv ("FU_HID_DEVICE_VERBOSE") != NULL) { + fu_common_dump_raw (G_LOG_DOMAIN, "HID::SetReport", + helper->buf, helper->bufsz); + } + usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + FU_HID_REPORT_SET, + wvalue, priv->interface, + helper->buf, helper->bufsz, + &actual_len, + helper->timeout, + NULL, error)) { + g_prefix_error (error, "failed to SetReport: "); + return FALSE; + } + if ((helper->flags & FU_HID_DEVICE_FLAG_ALLOW_TRUNC) == 0 && actual_len != helper->bufsz) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "wrote %" G_GSIZE_FORMAT ", requested %" G_GSIZE_FORMAT " bytes", + actual_len, helper->bufsz); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_hid_device_set_report_internal_cb (FuDevice *device, gpointer user_data, GError **error) +{ + FuHidDevice *self = FU_HID_DEVICE (device); + FuHidDeviceRetryHelper *helper = (FuHidDeviceRetryHelper *) user_data; + return fu_hid_device_set_report_internal (self, helper, error); +} + /** * fu_hid_device_set_report: * @self: A #FuHidDevice @@ -215,44 +274,83 @@ fu_hid_device_set_report (FuHidDevice *self, FuHidDeviceFlags flags, GError **error) { - FuHidDevicePrivate *priv = GET_PRIVATE (self); - GUsbDevice *usb_device; - gsize actual_len = 0; - guint16 wvalue = (FU_HID_REPORT_TYPE_OUTPUT << 8) | value; - - /* special case */ - if (flags & FU_HID_DEVICE_FLAG_IS_FEATURE) - wvalue = (FU_HID_REPORT_TYPE_FEATURE << 8) | value; + FuHidDeviceRetryHelper helper; g_return_val_if_fail (FU_HID_DEVICE (self), FALSE); g_return_val_if_fail (buf != NULL, FALSE); g_return_val_if_fail (bufsz != 0, FALSE); - if (g_getenv ("FU_HID_DEVICE_VERBOSE") != NULL) - fu_common_dump_raw (G_LOG_DOMAIN, "HID::SetReport", buf, bufsz); + /* create helper */ + helper.value = value; + helper.buf = buf; + helper.bufsz = bufsz; + helper.timeout = timeout; + helper.flags = flags; + + /* special case */ + if (flags & FU_HID_DEVICE_FLAG_RETRY_FAILURE) { + return fu_device_retry (FU_DEVICE (self), + fu_hid_device_set_report_internal_cb, + FU_HID_DEVICE_RETRIES, + &helper, + error); + } + + /* just one */ + return fu_hid_device_set_report_internal (self, &helper, error); +} + +static gboolean +fu_hid_device_get_report_internal (FuHidDevice *self, + FuHidDeviceRetryHelper *helper, + GError **error) +{ + FuHidDevicePrivate *priv = GET_PRIVATE (self); + GUsbDevice *usb_device; + gsize actual_len = 0; + guint16 wvalue = (FU_HID_REPORT_TYPE_INPUT << 8) | helper->value; + + /* special case */ + if (helper->flags & FU_HID_DEVICE_FLAG_IS_FEATURE) + wvalue = (FU_HID_REPORT_TYPE_FEATURE << 8) | helper->value; + + if (g_getenv ("FU_HID_DEVICE_VERBOSE") != NULL) { + fu_common_dump_raw (G_LOG_DOMAIN, "HID::GetReport", + helper->buf, actual_len); + } usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); if (!g_usb_device_control_transfer (usb_device, - G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, G_USB_DEVICE_REQUEST_TYPE_CLASS, G_USB_DEVICE_RECIPIENT_INTERFACE, - FU_HID_REPORT_SET, + FU_HID_REPORT_GET, wvalue, priv->interface, - buf, bufsz, - &actual_len, - timeout, + helper->buf, helper->bufsz, + &actual_len, /* actual length */ + helper->timeout, NULL, error)) { - g_prefix_error (error, "failed to SetReport: "); + g_prefix_error (error, "failed to GetReport: "); return FALSE; } - if ((flags & FU_HID_DEVICE_FLAG_ALLOW_TRUNC) == 0 && actual_len != bufsz) { + if (g_getenv ("FU_HID_DEVICE_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "HID::GetReport", helper->buf, actual_len); + if ((helper->flags & FU_HID_DEVICE_FLAG_ALLOW_TRUNC) == 0 && actual_len != helper->bufsz) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, - "wrote %" G_GSIZE_FORMAT ", requested %" G_GSIZE_FORMAT " bytes", - actual_len, bufsz); + "read %" G_GSIZE_FORMAT ", requested %" G_GSIZE_FORMAT " bytes", + actual_len, helper->bufsz); return FALSE; } return TRUE; } +static gboolean +fu_hid_device_get_report_internal_cb (FuDevice *device, gpointer user_data, GError **error) +{ + FuHidDevice *self = FU_HID_DEVICE (device); + FuHidDeviceRetryHelper *helper = (FuHidDeviceRetryHelper *) user_data; + return fu_hid_device_get_report_internal (self, helper, error); +} + /** * fu_hid_device_get_report: * @self: A #FuHidDevice @@ -278,44 +376,30 @@ fu_hid_device_get_report (FuHidDevice *self, FuHidDeviceFlags flags, GError **error) { - FuHidDevicePrivate *priv = GET_PRIVATE (self); - GUsbDevice *usb_device; - gsize actual_len = 0; - guint16 wvalue = (FU_HID_REPORT_TYPE_INPUT << 8) | value; + FuHidDeviceRetryHelper helper; g_return_val_if_fail (FU_HID_DEVICE (self), FALSE); g_return_val_if_fail (buf != NULL, FALSE); g_return_val_if_fail (bufsz != 0, FALSE); - /* special case */ - if (flags & FU_HID_DEVICE_FLAG_IS_FEATURE) - wvalue = (FU_HID_REPORT_TYPE_FEATURE << 8) | value; + /* create helper */ + helper.value = value; + helper.buf = buf; + helper.bufsz = bufsz; + helper.timeout = timeout; + helper.flags = flags; - if (g_getenv ("FU_HID_DEVICE_VERBOSE") != NULL) - fu_common_dump_raw (G_LOG_DOMAIN, "HID::GetReport", buf, actual_len); - usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); - if (!g_usb_device_control_transfer (usb_device, - G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, - G_USB_DEVICE_REQUEST_TYPE_CLASS, - G_USB_DEVICE_RECIPIENT_INTERFACE, - FU_HID_REPORT_GET, - wvalue, priv->interface, - buf, bufsz, - &actual_len, /* actual length */ - timeout, - NULL, error)) { - g_prefix_error (error, "failed to GetReport: "); - return FALSE; + /* special case */ + if (flags & FU_HID_DEVICE_FLAG_RETRY_FAILURE) { + return fu_device_retry (FU_DEVICE (self), + fu_hid_device_get_report_internal_cb, + FU_HID_DEVICE_RETRIES, + &helper, + error); } - if (g_getenv ("FU_HID_DEVICE_VERBOSE") != NULL) - fu_common_dump_raw (G_LOG_DOMAIN, "HID::GetReport", buf, actual_len); - if ((flags & FU_HID_DEVICE_FLAG_ALLOW_TRUNC) == 0 && actual_len != bufsz) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, - "read %" G_GSIZE_FORMAT ", requested %" G_GSIZE_FORMAT " bytes", - actual_len, bufsz); - return FALSE; - } - return TRUE; + + /* just one */ + return fu_hid_device_get_report_internal (self, &helper, error); } static void diff --git a/libfwupdplugin/fu-hid-device.h b/libfwupdplugin/fu-hid-device.h index 2f78f0a2d..d445d4e9d 100644 --- a/libfwupdplugin/fu-hid-device.h +++ b/libfwupdplugin/fu-hid-device.h @@ -28,6 +28,7 @@ struct _FuHidDeviceClass * @FU_HID_DEVICE_FLAG_NONE: No flags set * @FU_HID_DEVICE_FLAG_ALLOW_TRUNC: Allow truncated reads and writes * @FU_HID_DEVICE_FLAG_IS_FEATURE: Use %FU_HID_REPORT_TYPE_FEATURE for wValue + * @FU_HID_DEVICE_FLAG_RETRY_FAILURE: Retry up to 10 times on failure * * Flags used when calling fu_hid_device_get_report() and fu_hid_device_set_report(). **/ @@ -35,6 +36,7 @@ typedef enum { FU_HID_DEVICE_FLAG_NONE = 0, FU_HID_DEVICE_FLAG_ALLOW_TRUNC = 1 << 0, FU_HID_DEVICE_FLAG_IS_FEATURE = 1 << 1, + FU_HID_DEVICE_FLAG_RETRY_FAILURE = 1 << 2, FU_HID_DEVICE_FLAG_LAST } FuHidDeviceFlags; diff --git a/libfwupdplugin/fu-hwids.c b/libfwupdplugin/fu-hwids.c index 11fb1c088..f56ad675e 100644 --- a/libfwupdplugin/fu-hwids.c +++ b/libfwupdplugin/fu-hwids.c @@ -280,21 +280,10 @@ fu_hwids_convert_padded_integer_cb (FuSmbios *smbios, guint8 type, guint8 offset, GError **error) { - g_autoptr(GBytes) data = NULL; - const guint8 *data_raw; - gsize data_sz = 0; - data = fu_smbios_get_data (smbios, type, error); - if (data == NULL) + guint tmp = fu_smbios_get_integer (smbios, type, offset, error); + if (tmp == G_MAXUINT) return NULL; - data_raw = g_bytes_get_data (data, &data_sz); - if (offset >= data_sz) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "offset bigger than data"); - return NULL; - } - return g_strdup_printf ("%02x", data_raw[offset]); + return g_strdup_printf ("%02x", tmp); } static gchar * @@ -302,21 +291,10 @@ fu_hwids_convert_integer_cb (FuSmbios *smbios, guint8 type, guint8 offset, GError **error) { - g_autoptr(GBytes) data = NULL; - const guint8 *data_raw; - gsize data_sz = 0; - data = fu_smbios_get_data (smbios, type, error); - if (data == NULL) + guint tmp = fu_smbios_get_integer (smbios, type, offset, error); + if (tmp == G_MAXUINT) return NULL; - data_raw = g_bytes_get_data (data, &data_sz); - if (offset >= data_sz) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "offset bigger than data"); - return NULL; - } - return g_strdup_printf ("%x", data_raw[offset]); + return g_strdup_printf ("%x", tmp); } /** diff --git a/libfwupdplugin/fu-ihex-firmware.c b/libfwupdplugin/fu-ihex-firmware.c index 98ddb4052..49a9f5095 100644 --- a/libfwupdplugin/fu-ihex-firmware.c +++ b/libfwupdplugin/fu-ihex-firmware.c @@ -30,14 +30,6 @@ struct _FuIhexFirmware { G_DEFINE_TYPE (FuIhexFirmware, fu_ihex_firmware, FU_TYPE_FIRMWARE) -#define DFU_INHX32_RECORD_TYPE_DATA 0x00 -#define DFU_INHX32_RECORD_TYPE_EOF 0x01 -#define DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT 0x02 -#define DFU_INHX32_RECORD_TYPE_START_SEGMENT 0x03 -#define DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR 0x04 -#define DFU_INHX32_RECORD_TYPE_START_LINEAR 0x05 -#define DFU_INHX32_RECORD_TYPE_SIGNATURE 0xfd - /** * fu_ihex_firmware_get_records: * @self: A #FuIhexFirmware @@ -62,36 +54,100 @@ static void fu_ihex_firmware_record_free (FuIhexFirmwareRecord *rcd) { g_string_free (rcd->buf, TRUE); + g_byte_array_unref (rcd->data); g_free (rcd); } G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuIhexFirmwareRecord, fu_ihex_firmware_record_free) static FuIhexFirmwareRecord * -fu_ihex_firmware_record_new (guint ln, const gchar *buf) +fu_ihex_firmware_record_new (guint ln, const gchar *line, + FwupdInstallFlags flags, GError **error) { - FuIhexFirmwareRecord *rcd = g_new0 (FuIhexFirmwareRecord, 1); + g_autoptr(FuIhexFirmwareRecord) rcd = NULL; + guint line_end; + + /* check starting token */ + if (line[0] != ':') { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid starting token: %s", + line); + return NULL; + } + + /* length, 16-bit address, type */ + rcd = g_new0 (FuIhexFirmwareRecord, 1); rcd->ln = ln; - rcd->buf = g_string_new (buf); - return rcd; + rcd->data = g_byte_array_new (); + rcd->buf = g_string_new (line); + rcd->byte_cnt = fu_firmware_strparse_uint8 (line + 1); + rcd->addr = fu_firmware_strparse_uint16 (line + 3); + rcd->record_type = fu_firmware_strparse_uint8 (line + 7); + + /* check there's enough data for the smallest possible record */ + if (rcd->buf->len < 11) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "line incomplete, length: %u", + (guint) rcd->buf->len); + return NULL; + } + + /* position of checksum */ + line_end = 9 + rcd->byte_cnt * 2; + if (line_end > (guint) rcd->buf->len) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "line malformed, length: %u", + line_end); + return NULL; + } + + /* verify checksum */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + guint8 checksum = 0; + for (guint i = 1; i < line_end + 2; i += 2) { + guint8 data_tmp = fu_firmware_strparse_uint8 (line + i); + checksum += data_tmp; + } + if (checksum != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid checksum (0x%02x)", + checksum); + return NULL; + } + } + + /* add data */ + for (guint i = 9; i < line_end; i += 2) { + guint8 tmp_c = fu_firmware_strparse_uint8 (line + i); + fu_byte_array_append_uint8 (rcd->data, tmp_c); + } + return g_steal_pointer (&rcd); } static const gchar * fu_ihex_firmware_record_type_to_string (guint8 record_type) { - if (record_type == DFU_INHX32_RECORD_TYPE_DATA) + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_DATA) return "DATA"; - if (record_type == DFU_INHX32_RECORD_TYPE_EOF) + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_EOF) return "EOF"; - if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT) + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_SEGMENT) return "EXTENDED_SEGMENT"; - if (record_type == DFU_INHX32_RECORD_TYPE_START_SEGMENT) + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_START_SEGMENT) return "START_SEGMENT"; - if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR) + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_LINEAR) return "EXTENDED_LINEAR"; - if (record_type == DFU_INHX32_RECORD_TYPE_START_LINEAR) + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_START_LINEAR) return "ADDR32"; - if (record_type == DFU_INHX32_RECORD_TYPE_SIGNATURE) + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_SIGNATURE) return "SIGNATURE"; return NULL; } @@ -108,9 +164,15 @@ fu_ihex_firmware_tokenize (FuFirmware *firmware, GBytes *fw, for (guint ln = 0; lines[ln] != NULL; ln++) { g_autoptr(FuIhexFirmwareRecord) rcd = NULL; g_strdelimit (lines[ln], "\r\x1a", '\0'); + if (g_str_has_prefix (lines[ln], ";")) + continue; if (lines[ln][0] == '\0') continue; - rcd = fu_ihex_firmware_record_new (ln + 1, lines[ln]); + rcd = fu_ihex_firmware_record_new (ln + 1, lines[ln], flags, error); + if (rcd == NULL) { + g_prefix_error (error, "invalid line %u: ", ln + 1); + return FALSE; + } g_ptr_array_add (self->records, g_steal_pointer (&rcd)); } return TRUE; @@ -133,87 +195,21 @@ fu_ihex_firmware_parse (FuFirmware *firmware, g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL); g_autoptr(GBytes) img_bytes = NULL; g_autoptr(GByteArray) buf = g_byte_array_new (); - g_autoptr(GByteArray) buf_signature = g_byte_array_new (); /* parse records */ for (guint k = 0; k < self->records->len; k++) { FuIhexFirmwareRecord *rcd = g_ptr_array_index (self->records, k); - const gchar *line = rcd->buf->str; - guint32 addr; - guint8 byte_cnt; - guint8 record_type; - guint line_end; + guint16 addr16 = 0; + guint32 addr = rcd->addr + seg_addr + abs_addr; + guint32 len_hole; - /* ignore comments */ - if (g_str_has_prefix (line, ";")) - continue; - - /* ignore blank lines */ - if (rcd->buf->len == 0) - continue; - - /* check starting token */ - if (line[0] != ':') { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid starting token on line %u: %s", - rcd->ln, line); - return FALSE; - } - - /* check there's enough data for the smallest possible record */ - if (rcd->buf->len < 11) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "line %u is incomplete, length %u", - rcd->ln, (guint) rcd->buf->len); - return FALSE; - } - - /* length, 16-bit address, type */ - byte_cnt = fu_firmware_strparse_uint8 (line + 1); - addr = fu_firmware_strparse_uint16 (line + 3); - record_type = fu_firmware_strparse_uint8 (line + 7); - g_debug ("%s:", fu_ihex_firmware_record_type_to_string (record_type)); - g_debug (" addr_start:\t0x%04x", addr); - g_debug (" length:\t0x%02x", byte_cnt); - addr += seg_addr; - addr += abs_addr; + g_debug ("%s:", fu_ihex_firmware_record_type_to_string (rcd->record_type)); + g_debug (" length:\t0x%02x", rcd->data->len); g_debug (" addr:\t0x%08x", addr); - /* position of checksum */ - line_end = 9 + byte_cnt * 2; - if (line_end > (guint) rcd->buf->len) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "line %u malformed, length: %u", - rcd->ln, line_end); - return FALSE; - } - - /* verify checksum */ - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { - guint8 checksum = 0; - for (guint i = 1; i < line_end + 2; i += 2) { - guint8 data_tmp = fu_firmware_strparse_uint8 (line + i); - checksum += data_tmp; - } - if (checksum != 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "line %u has invalid checksum (0x%02x)", - rcd->ln, checksum); - return FALSE; - } - } - /* process different record types */ - switch (record_type) { - case DFU_INHX32_RECORD_TYPE_DATA: + switch (rcd->record_type) { + case FU_IHEX_FIRMWARE_RECORD_TYPE_DATA: /* base address for element */ if (img_addr == G_MAXUINT32) img_addr = addr; @@ -230,37 +226,32 @@ fu_ihex_firmware_parse (FuFirmware *firmware, return FALSE; } - /* parse bytes from line */ - g_debug ("writing data 0x%08x", (guint32) addr); - for (guint i = 9; i < line_end; i += 2) { - /* any holes in the hex record */ - guint32 len_hole = addr - addr_last; - guint8 data_tmp; - if (addr_last > 0 && len_hole > 0x100000) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "hole of 0x%x bytes too large to fill on line %u", - (guint) len_hole, - rcd->ln); - return FALSE; - } - if (addr_last > 0x0 && len_hole > 1) { - g_debug ("filling address 0x%08x to 0x%08x on line %u", - addr_last + 1, addr_last + len_hole - 1, rcd->ln); - for (guint j = 1; j < len_hole; j++) { - /* although 0xff might be clearer, - * we can't write 0xffff to pic14 */ - fu_byte_array_append_uint8 (buf, 0x00); - } - } - /* write into buf */ - data_tmp = fu_firmware_strparse_uint8 (line + i); - fu_byte_array_append_uint8 (buf, (gchar) data_tmp); - addr_last = addr++; + /* any holes in the hex record */ + len_hole = addr - addr_last; + if (addr_last > 0 && len_hole > 0x100000) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "hole of 0x%x bytes too large to fill on line %u", + (guint) len_hole, + rcd->ln); + return FALSE; } + if (addr_last > 0x0 && len_hole > 1) { + g_debug ("filling address 0x%08x to 0x%08x on line %u", + addr_last + 1, addr_last + len_hole - 1, rcd->ln); + for (guint j = 1; j < len_hole; j++) { + /* although 0xff might be clearer, + * we can't write 0xffff to pic14 */ + fu_byte_array_append_uint8 (buf, 0x00); + } + } + addr_last = addr + rcd->data->len - 1; + + /* write into buf */ + g_byte_array_append (buf, rcd->data->data, rcd->data->len); break; - case DFU_INHX32_RECORD_TYPE_EOF: + case FU_IHEX_FIRMWARE_RECORD_TYPE_EOF: if (got_eof) { g_set_error_literal (error, FWUPD_ERROR, @@ -271,28 +262,40 @@ fu_ihex_firmware_parse (FuFirmware *firmware, } got_eof = TRUE; break; - case DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR: - abs_addr = fu_firmware_strparse_uint16 (line + 9) << 16; + case FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_LINEAR: + if (!fu_common_read_uint16_safe (rcd->data->data, rcd->data->len, + 0x0, &addr16, G_BIG_ENDIAN, error)) + return FALSE; + abs_addr = (guint32) addr16 << 16; g_debug (" abs_addr:\t0x%02x on line %u", abs_addr, rcd->ln); break; - case DFU_INHX32_RECORD_TYPE_START_LINEAR: - abs_addr = fu_firmware_strparse_uint32 (line + 9); + case FU_IHEX_FIRMWARE_RECORD_TYPE_START_LINEAR: + if (!fu_common_read_uint32_safe (rcd->data->data, rcd->data->len, + 0x0, &abs_addr, G_BIG_ENDIAN, error)) + return FALSE; g_debug (" abs_addr:\t0x%08x on line %u", abs_addr, rcd->ln); break; - case DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT: + case FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_SEGMENT: + if (!fu_common_read_uint16_safe (rcd->data->data, rcd->data->len, + 0x0, &addr16, G_BIG_ENDIAN, error)) + return FALSE; /* segment base address, so ~1Mb addressable */ - seg_addr = fu_firmware_strparse_uint16 (line + 9) * 16; + seg_addr = (guint32) addr16 * 16; g_debug (" seg_addr:\t0x%08x on line %u", seg_addr, rcd->ln); break; - case DFU_INHX32_RECORD_TYPE_START_SEGMENT: + case FU_IHEX_FIRMWARE_RECORD_TYPE_START_SEGMENT: /* initial content of the CS:IP registers */ - seg_addr = fu_firmware_strparse_uint32 (line + 9); + if (!fu_common_read_uint32_safe (rcd->data->data, rcd->data->len, + 0x0, &seg_addr, G_BIG_ENDIAN, error)) + return FALSE; g_debug (" seg_addr:\t0x%02x on line %u", seg_addr, rcd->ln); break; - case DFU_INHX32_RECORD_TYPE_SIGNATURE: - for (guint i = 9; i < line_end; i += 2) { - guint8 tmp_c = fu_firmware_strparse_uint8 (line + i); - fu_byte_array_append_uint8 (buf_signature, tmp_c); + case FU_IHEX_FIRMWARE_RECORD_TYPE_SIGNATURE: + if (rcd->data->len > 0) { + g_autoptr(GBytes) data_sig = g_bytes_new (rcd->data->data, rcd->data->len); + g_autoptr(FuFirmwareImage) img_sig = fu_firmware_image_new (data_sig); + fu_firmware_image_set_id (img_sig, FU_FIRMWARE_IMAGE_ID_SIGNATURE); + fu_firmware_add_image (firmware, img_sig); } break; default: @@ -303,7 +306,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "invalid ihex record type %i on line %u", - record_type, rcd->ln); + rcd->record_type, rcd->ln); return FALSE; } } @@ -323,14 +326,6 @@ fu_ihex_firmware_parse (FuFirmware *firmware, if (img_addr != G_MAXUINT32) fu_firmware_image_set_addr (img, img_addr); fu_firmware_add_image (firmware, img); - - /* add optional signature */ - if (buf_signature->len > 0) { - g_autoptr(GBytes) data_sig = g_bytes_new (buf_signature->data, buf_signature->len); - g_autoptr(FuFirmwareImage) img_sig = fu_firmware_image_new (data_sig); - fu_firmware_image_set_id (img_sig, FU_FIRMWARE_IMAGE_ID_SIGNATURE); - fu_firmware_add_image (firmware, img_sig); - } return TRUE; } @@ -364,7 +359,7 @@ fu_ihex_firmware_image_to_string (FuFirmwareImage *img, GString *str, GError **e const guint chunk_size = 16; gsize len; guint32 address_offset_last = 0x0; - guint8 record_type = DFU_INHX32_RECORD_TYPE_DATA; + guint8 record_type = FU_IHEX_FIRMWARE_RECORD_TYPE_DATA; g_autoptr(GBytes) bytes = NULL; /* get data */ @@ -375,7 +370,7 @@ fu_ihex_firmware_image_to_string (FuFirmwareImage *img, GString *str, GError **e /* special case */ if (g_strcmp0 (fu_firmware_image_get_id (img), FU_FIRMWARE_IMAGE_ID_SIGNATURE) == 0) - record_type = DFU_INHX32_RECORD_TYPE_SIGNATURE; + record_type = FU_IHEX_FIRMWARE_RECORD_TYPE_SIGNATURE; /* get number of chunks */ data = g_bytes_get_data (bytes, &len); @@ -389,7 +384,7 @@ fu_ihex_firmware_image_to_string (FuFirmwareImage *img, GString *str, GError **e guint8 buf[2]; fu_common_write_uint16 (buf, address_offset, G_BIG_ENDIAN); fu_ihex_firmware_emit_chunk (str, 0x0, - DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR, + FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_LINEAR, buf, 2); address_offset_last = address_offset; } @@ -416,7 +411,7 @@ fu_ihex_firmware_write (FuFirmware *firmware, GError **error) } /* add EOF */ - fu_ihex_firmware_emit_chunk (str, 0x0, DFU_INHX32_RECORD_TYPE_EOF, NULL, 0); + fu_ihex_firmware_emit_chunk (str, 0x0, FU_IHEX_FIRMWARE_RECORD_TYPE_EOF, NULL, 0); return g_bytes_new (str->str, str->len); } diff --git a/libfwupdplugin/fu-ihex-firmware.h b/libfwupdplugin/fu-ihex-firmware.h index a6754dd9b..ef429f647 100644 --- a/libfwupdplugin/fu-ihex-firmware.h +++ b/libfwupdplugin/fu-ihex-firmware.h @@ -14,7 +14,19 @@ G_DECLARE_FINAL_TYPE (FuIhexFirmware, fu_ihex_firmware, FU, IHEX_FIRMWARE, FuFir typedef struct { guint ln; GString *buf; + guint8 byte_cnt; + guint32 addr; + guint8 record_type; + GByteArray *data; } FuIhexFirmwareRecord; +#define FU_IHEX_FIRMWARE_RECORD_TYPE_DATA 0x00 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_EOF 0x01 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_SEGMENT 0x02 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_START_SEGMENT 0x03 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_LINEAR 0x04 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_START_LINEAR 0x05 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_SIGNATURE 0xfd + FuFirmware *fu_ihex_firmware_new (void); GPtrArray *fu_ihex_firmware_get_records (FuIhexFirmware *self); diff --git a/libfwupdplugin/fu-plugin-private.h b/libfwupdplugin/fu-plugin-private.h index a1862440b..bb703fcdd 100644 --- a/libfwupdplugin/fu-plugin-private.h +++ b/libfwupdplugin/fu-plugin-private.h @@ -8,6 +8,7 @@ #include "fu-quirks.h" #include "fu-plugin.h" +#include "fu-security-attrs.h" #include "fu-smbios.h" FuPlugin *fu_plugin_new (void); @@ -89,6 +90,8 @@ gboolean fu_plugin_runner_udev_device_changed (FuPlugin *self, gboolean fu_plugin_runner_device_created (FuPlugin *self, FuDevice *device, GError **error); +void fu_plugin_runner_device_added (FuPlugin *self, + FuDevice *device); void fu_plugin_runner_device_removed (FuPlugin *self, FuDevice *device); void fu_plugin_runner_device_register (FuPlugin *self, @@ -114,6 +117,8 @@ gboolean fu_plugin_runner_clear_results (FuPlugin *self, gboolean fu_plugin_runner_get_results (FuPlugin *self, FuDevice *device, GError **error); +void fu_plugin_runner_add_security_attrs (FuPlugin *self, + FuSecurityAttrs*attrs); gint fu_plugin_name_compare (FuPlugin *plugin1, FuPlugin *plugin2); gint fu_plugin_order_compare (FuPlugin *plugin1, diff --git a/libfwupdplugin/fu-plugin-vfuncs.h b/libfwupdplugin/fu-plugin-vfuncs.h index af89b198e..8b9292092 100644 --- a/libfwupdplugin/fu-plugin-vfuncs.h +++ b/libfwupdplugin/fu-plugin-vfuncs.h @@ -8,6 +8,7 @@ #include "fu-plugin.h" #include "fu-device.h" +#include "fu-security-attrs.h" /** * SECTION:fu-plugin-vfuncs @@ -311,6 +312,17 @@ gboolean fu_plugin_udev_device_added (FuPlugin *plugin, gboolean fu_plugin_udev_device_changed (FuPlugin *plugin, FuUdevDevice *device, GError **error); +/** + * fu_plugin_device_added + * @plugin: A #FuPlugin + * @device: A #FuDevice + * + * Function run when the subclassed device has been added. + * + * Since: 1.5.0 + **/ +void fu_plugin_device_added (FuPlugin *plugin, + FuDevice *dev); /** * fu_plugin_device_removed * @plugin: A #FuPlugin @@ -348,3 +360,14 @@ gboolean fu_plugin_device_created (FuPlugin *plugin, **/ void fu_plugin_device_registered (FuPlugin *plugin, FuDevice *dev); +/** + * fu_plugin_add_security_attrs + * @plugin: A #FuPlugin + * @attrs: A #FuSecurityAttrs + * + * Function that asks plugins to add Host Security Attributes. + * + * Since: 1.5.0 + **/ +void fu_plugin_add_security_attrs (FuPlugin *plugin, + FuSecurityAttrs *attrs); diff --git a/libfwupdplugin/fu-plugin.c b/libfwupdplugin/fu-plugin.c index cdf2ec881..d24f6a740 100644 --- a/libfwupdplugin/fu-plugin.c +++ b/libfwupdplugin/fu-plugin.c @@ -37,11 +37,9 @@ static void fu_plugin_finalize (GObject *object); typedef struct { GModule *module; GUsbContext *usb_ctx; - gboolean enabled; guint order; guint priority; GPtrArray *rules[FU_PLUGIN_RULE_LAST]; - gchar *name; gchar *build_hash; FuHwids *hwids; FuQuirks *quirks; @@ -50,9 +48,9 @@ typedef struct { GPtrArray *udev_subsystems; FuSmbios *smbios; GType device_gtype; - GHashTable *devices; /* platform_id:GObject */ + GHashTable *devices; /* (nullable): platform_id:GObject */ GRWLock devices_mutex; - GHashTable *report_metadata; /* key:value */ + GHashTable *report_metadata; /* (nullable): key:value */ FuPluginData *data; } FuPluginPrivate; @@ -65,12 +63,13 @@ enum { SIGNAL_SET_COLDPLUG_DELAY, SIGNAL_CHECK_SUPPORTED, SIGNAL_ADD_FIRMWARE_GTYPE, + SIGNAL_SECURITY_CHANGED, SIGNAL_LAST }; static guint signals[SIGNAL_LAST] = { 0 }; -G_DEFINE_TYPE_WITH_PRIVATE (FuPlugin, fu_plugin, G_TYPE_OBJECT) +G_DEFINE_TYPE_WITH_PRIVATE (FuPlugin, fu_plugin, FWUPD_TYPE_PLUGIN) #define GET_PRIVATE(o) (fu_plugin_get_instance_private (o)) typedef const gchar *(*FuPluginGetNameFunc) (void); @@ -104,6 +103,8 @@ typedef gboolean (*FuPluginUsbDeviceAddedFunc) (FuPlugin *self, typedef gboolean (*FuPluginUdevDeviceAddedFunc) (FuPlugin *self, FuUdevDevice *device, GError **error); +typedef void (*FuPluginSecurityAttrsFunc) (FuPlugin *self, + FuSecurityAttrs *attrs); /** * fu_plugin_is_open: @@ -135,9 +136,8 @@ fu_plugin_is_open (FuPlugin *self) const gchar * fu_plugin_get_name (FuPlugin *self) { - FuPluginPrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_PLUGIN (self), NULL); - return priv->name; + return fwupd_plugin_get_name (FWUPD_PLUGIN (self)); } /** @@ -152,11 +152,8 @@ fu_plugin_get_name (FuPlugin *self) void fu_plugin_set_name (FuPlugin *self, const gchar *name) { - FuPluginPrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_PLUGIN (self)); - g_return_if_fail (name != NULL); - g_free (priv->name); - priv->name = g_strdup (name); + fwupd_plugin_set_name (FWUPD_PLUGIN (self), name); } /** @@ -216,6 +213,8 @@ fu_plugin_cache_lookup (FuPlugin *self, const gchar *id) g_return_val_if_fail (FU_IS_PLUGIN (self), NULL); g_return_val_if_fail (id != NULL, NULL); g_return_val_if_fail (locker != NULL, NULL); + if (priv->devices == NULL) + return NULL; return g_hash_table_lookup (priv->devices, id); } @@ -237,6 +236,12 @@ fu_plugin_cache_add (FuPlugin *self, const gchar *id, gpointer dev) g_return_if_fail (FU_IS_PLUGIN (self)); g_return_if_fail (id != NULL); g_return_if_fail (locker != NULL); + if (priv->devices == NULL) { + priv->devices = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_object_unref); + } g_hash_table_insert (priv->devices, g_strdup (id), g_object_ref (dev)); } @@ -257,6 +262,8 @@ fu_plugin_cache_remove (FuPlugin *self, const gchar *id) g_return_if_fail (FU_IS_PLUGIN (self)); g_return_if_fail (id != NULL); g_return_if_fail (locker != NULL); + if (priv->devices == NULL) + return; g_hash_table_remove (priv->devices, id); } @@ -351,9 +358,8 @@ fu_plugin_set_usb_context (FuPlugin *self, GUsbContext *usb_ctx) gboolean fu_plugin_get_enabled (FuPlugin *self) { - FuPluginPrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_PLUGIN (self), FALSE); - return priv->enabled; + return !fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED); } /** @@ -368,9 +374,13 @@ fu_plugin_get_enabled (FuPlugin *self) void fu_plugin_set_enabled (FuPlugin *self, gboolean enabled) { - FuPluginPrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_PLUGIN (self)); - priv->enabled = enabled; + if (enabled) { + fwupd_plugin_remove_flag (FWUPD_PLUGIN (self), + FWUPD_PLUGIN_FLAG_DISABLED); + } else { + fu_plugin_add_flag (self, FWUPD_PLUGIN_FLAG_DISABLED); + } } /** @@ -421,23 +431,48 @@ fu_plugin_open (FuPlugin *self, const gchar *filename, GError **error) G_IO_ERROR_FAILED, "failed to open plugin %s: %s", filename, g_module_error ()); + fu_plugin_add_flag (self, FWUPD_PLUGIN_FLAG_FAILED_OPEN); + fu_plugin_add_flag (self, FWUPD_PLUGIN_FLAG_USER_WARNING); return FALSE; } /* set automatically */ - if (priv->name == NULL) - priv->name = fu_plugin_guess_name_from_fn (filename); + if (fu_plugin_get_name (self) == NULL) { + g_autofree gchar *str = fu_plugin_guess_name_from_fn (filename); + fu_plugin_set_name (self, str); + } /* optional */ g_module_symbol (priv->module, "fu_plugin_init", (gpointer *) &func); if (func != NULL) { - g_debug ("performing init() on %s", filename); + g_debug ("init(%s)", filename); func (self); } return TRUE; } +/* order of usefulness to the user */ +static const gchar * +fu_plugin_build_device_update_error (FuPlugin *self) +{ + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_NO_HARDWARE)) + return "Not updatable as required hardware was not found"; + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_LEGACY_BIOS)) + return "Not updatable in legacy BIOS mode"; + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED)) + return "Not updatable as UEFI capsule updates not enabled"; + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED)) + return "Not updatable as requires unlock"; + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED)) + return "Not updatable as efivarfs was not found"; + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND)) + return "Not updatable as UEFI ESP partition not detected"; + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) + return "Not updatable as plugin was disabled"; + return NULL; +} + /** * fu_plugin_device_add: * @self: A #FuPlugin @@ -468,6 +503,21 @@ fu_plugin_device_add (FuPlugin *self, FuDevice *device) return; } + /* proxy to device where required */ + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE)) { + g_debug ("plugin %s has _CLEAR_UPDATABLE, so removing from %s", + fu_plugin_get_name (self), + fu_device_get_id (device)); + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + } + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_USER_WARNING) && + fu_device_get_update_error (device) == NULL) { + const gchar *tmp = fu_plugin_build_device_update_error (self); + g_debug ("setting %s update error to '%s' from %s", + fu_device_get_id (device), tmp, fu_plugin_get_name (self)); + fu_device_set_update_error (device, tmp); + } + g_debug ("emit added from %s: %s", fu_plugin_get_name (self), fu_device_get_id (device)); @@ -555,6 +605,21 @@ fu_plugin_request_recoldplug (FuPlugin *self) g_signal_emit (self, signals[SIGNAL_RECOLDPLUG], 0); } +/** + * fu_plugin_security_changed: + * @self: A #FuPlugin + * + * Informs the daemon that the HSI state may have changed. + * + * Since: 1.5.0 + **/ +void +fu_plugin_security_changed (FuPlugin *self) +{ + g_return_if_fail (FU_IS_PLUGIN (self)); + g_signal_emit (self, signals[SIGNAL_SECURITY_CHANGED], 0); +} + /** * fu_plugin_check_hwid: * @self: A #FuPlugin @@ -955,7 +1020,7 @@ fu_plugin_set_smbios (FuPlugin *self, FuSmbios *smbios) * * Set the minimum time that should be waited in-between the call to * fu_plugin_coldplug_prepare() and fu_plugin_coldplug(). This is usually going - * to be the minimum hardware initialisation time from a datasheet. + * to be the minimum hardware initialization time from a datasheet. * * It is better to use this function rather than using a sleep() in the plugin * itself as then only one delay is done in the daemon rather than waiting for @@ -1024,6 +1089,32 @@ fu_plugin_device_write_firmware (FuPlugin *self, FuDevice *device, locker = fu_device_locker_new (device, error); if (locker == NULL) return FALSE; + + /* back the old firmware up to /var/lib/fwupd */ + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL)) { + g_autoptr(GBytes) fw_old = NULL; + g_autofree gchar *path = NULL; + g_autofree gchar *fn = NULL; + g_autofree gchar *localstatedir = NULL; + + fw_old = fu_device_dump_firmware (device, error); + if (fw_old == NULL) { + g_prefix_error (error, "failed to backup old firmware: "); + return FALSE; + } + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + fn = g_strdup_printf ("%s.bin", fu_device_get_version (device)); + path = g_build_filename (localstatedir, + "backup", + fu_device_get_id (device), + fu_device_get_serial (device) != NULL ? + fu_device_get_serial (device) : + "default", + fn, NULL); + if (!fu_common_set_contents_bytes (path, fw_old, error)) + return FALSE; + } + return fu_device_write_firmware (device, fw, flags, error); } @@ -1085,7 +1176,7 @@ fu_plugin_runner_startup (FuPlugin *self, GError **error) g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1096,11 +1187,11 @@ fu_plugin_runner_startup (FuPlugin *self, GError **error) g_module_symbol (priv->module, "fu_plugin_startup", (gpointer *) &func); if (func == NULL) return TRUE; - g_debug ("performing startup() on %s", priv->name); + g_debug ("startup(%s)", fu_plugin_get_name (self)); if (!func (self, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for startup()", - priv->name); + g_critical ("unset plugin error in startup(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -1108,7 +1199,7 @@ fu_plugin_runner_startup (FuPlugin *self, GError **error) } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to startup using %s: ", - priv->name); + fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -1125,7 +1216,7 @@ fu_plugin_runner_device_generic (FuPlugin *self, FuDevice *device, g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1136,17 +1227,17 @@ fu_plugin_runner_device_generic (FuPlugin *self, FuDevice *device, g_module_symbol (priv->module, symbol_name, (gpointer *) &func); if (func == NULL) { if (device_func != NULL) { - g_debug ("running superclassed %s() on %s", - symbol_name + 10, priv->name); + g_debug ("running superclassed %s(%s)", + symbol_name + 10, fu_plugin_get_name (self)); return device_func (self, device, error); } return TRUE; } - g_debug ("performing %s() on %s", symbol_name + 10, priv->name); + g_debug ("%s(%s)", symbol_name + 10, fu_plugin_get_name (self)); if (!func (self, device, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for %s()", - priv->name, symbol_name + 10); + g_critical ("unset plugin error in %s(%s)", + fu_plugin_get_name (self), symbol_name + 10); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -1154,7 +1245,7 @@ fu_plugin_runner_device_generic (FuPlugin *self, FuDevice *device, } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to %s using %s: ", - symbol_name + 10, priv->name); + symbol_name + 10, fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -1170,7 +1261,7 @@ fu_plugin_runner_flagged_device_generic (FuPlugin *self, FwupdInstallFlags flags g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1181,11 +1272,11 @@ fu_plugin_runner_flagged_device_generic (FuPlugin *self, FwupdInstallFlags flags g_module_symbol (priv->module, symbol_name, (gpointer *) &func); if (func == NULL) return TRUE; - g_debug ("performing %s() on %s", symbol_name + 10, priv->name); + g_debug ("%s(%s)", symbol_name + 10, fu_plugin_get_name (self)); if (!func (self, flags, device, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for %s()", - priv->name, symbol_name + 10); + g_critical ("unset plugin error in %s(%s)", + fu_plugin_get_name (self), symbol_name + 10); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -1193,7 +1284,7 @@ fu_plugin_runner_flagged_device_generic (FuPlugin *self, FwupdInstallFlags flags } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to %s using %s: ", - symbol_name + 10, priv->name); + symbol_name + 10, fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -1209,7 +1300,7 @@ fu_plugin_runner_device_array_generic (FuPlugin *self, GPtrArray *devices, g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1220,11 +1311,11 @@ fu_plugin_runner_device_array_generic (FuPlugin *self, GPtrArray *devices, g_module_symbol (priv->module, symbol_name, (gpointer *) &func); if (func == NULL) return TRUE; - g_debug ("performing %s() on %s", symbol_name + 10, priv->name); + g_debug ("%s(%s)", symbol_name + 10, fu_plugin_get_name (self)); if (!func (self, devices, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for %s()", - priv->name, symbol_name + 10); + g_critical ("unset plugin error in for %s(%s)", + fu_plugin_get_name (self), symbol_name + 10); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -1232,7 +1323,7 @@ fu_plugin_runner_device_array_generic (FuPlugin *self, GPtrArray *devices, } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to %s using %s: ", - symbol_name + 10, priv->name); + symbol_name + 10, fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -1257,7 +1348,7 @@ fu_plugin_runner_coldplug (FuPlugin *self, GError **error) g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1268,18 +1359,18 @@ fu_plugin_runner_coldplug (FuPlugin *self, GError **error) g_module_symbol (priv->module, "fu_plugin_coldplug", (gpointer *) &func); if (func == NULL) return TRUE; - g_debug ("performing coldplug() on %s", priv->name); + g_debug ("coldplug(%s)", fu_plugin_get_name (self)); if (!func (self, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for coldplug()", - priv->name); + g_critical ("unset plugin error in coldplug(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), - "failed to coldplug using %s: ", priv->name); + "failed to coldplug using %s: ", fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -1304,7 +1395,7 @@ fu_plugin_runner_recoldplug (FuPlugin *self, GError **error) g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1315,11 +1406,11 @@ fu_plugin_runner_recoldplug (FuPlugin *self, GError **error) g_module_symbol (priv->module, "fu_plugin_recoldplug", (gpointer *) &func); if (func == NULL) return TRUE; - g_debug ("performing recoldplug() on %s", priv->name); + g_debug ("recoldplug(%s)", fu_plugin_get_name (self)); if (!func (self, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for recoldplug()", - priv->name); + g_critical ("unset plugin error in recoldplug(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -1327,7 +1418,7 @@ fu_plugin_runner_recoldplug (FuPlugin *self, GError **error) } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to recoldplug using %s: ", - priv->name); + fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -1352,7 +1443,7 @@ fu_plugin_runner_coldplug_prepare (FuPlugin *self, GError **error) g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1363,11 +1454,11 @@ fu_plugin_runner_coldplug_prepare (FuPlugin *self, GError **error) g_module_symbol (priv->module, "fu_plugin_coldplug_prepare", (gpointer *) &func); if (func == NULL) return TRUE; - g_debug ("performing coldplug_prepare() on %s", priv->name); + g_debug ("coldplug_prepare(%s)", fu_plugin_get_name (self)); if (!func (self, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for coldplug_prepare()", - priv->name); + g_critical ("unset plugin error in coldplug_prepare(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -1375,7 +1466,7 @@ fu_plugin_runner_coldplug_prepare (FuPlugin *self, GError **error) } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to coldplug_prepare using %s: ", - priv->name); + fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -1400,7 +1491,7 @@ fu_plugin_runner_coldplug_cleanup (FuPlugin *self, GError **error) g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1411,11 +1502,11 @@ fu_plugin_runner_coldplug_cleanup (FuPlugin *self, GError **error) g_module_symbol (priv->module, "fu_plugin_coldplug_cleanup", (gpointer *) &func); if (func == NULL) return TRUE; - g_debug ("performing coldplug_cleanup() on %s", priv->name); + g_debug ("coldplug_cleanup(%s)", fu_plugin_get_name (self)); if (!func (self, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for coldplug_cleanup()", - priv->name); + g_critical ("unset plugin error in coldplug_cleanup(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -1423,7 +1514,7 @@ fu_plugin_runner_coldplug_cleanup (FuPlugin *self, GError **error) } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to coldplug_cleanup using %s: ", - priv->name); + fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -1565,11 +1656,10 @@ fu_plugin_runner_update_detach (FuPlugin *self, FuDevice *device, GError **error gboolean fu_plugin_runner_update_reload (FuPlugin *self, FuDevice *device, GError **error) { - FuPluginPrivate *priv = GET_PRIVATE (self); g_autoptr(FuDeviceLocker) locker = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1579,6 +1669,34 @@ fu_plugin_runner_update_reload (FuPlugin *self, FuDevice *device, GError **error return fu_device_reload (device, error); } +/** + * fu_plugin_runner_add_security_attrs: + * @self: a #FuPlugin + * @attrs: a #FuSecurityAttrs + * + * Runs the `add_security_attrs()` routine for the plugin + * + * Since: 1.5.0 + **/ +void +fu_plugin_runner_add_security_attrs (FuPlugin *self, FuSecurityAttrs *attrs) +{ + FuPluginPrivate *priv = GET_PRIVATE (self); + FuPluginSecurityAttrsFunc func = NULL; + const gchar *symbol_name = "fu_plugin_add_security_attrs"; + + /* no object loaded */ + if (priv->module == NULL) + return; + + /* optional, but gets called even for disabled plugins */ + g_module_symbol (priv->module, symbol_name, (gpointer *) &func); + if (func == NULL) + return; + g_debug ("%s(%s)", symbol_name + 10, fu_plugin_get_name (self)); + func (self, attrs); +} + /** * fu_plugin_add_udev_subsystem: * @self: a #FuPlugin @@ -1594,6 +1712,8 @@ void fu_plugin_add_udev_subsystem (FuPlugin *self, const gchar *subsystem) { FuPluginPrivate *priv = GET_PRIVATE (self); + if (priv->udev_subsystems == NULL) + priv->udev_subsystems = g_ptr_array_new_with_free_func (g_free); for (guint i = 0; i < priv->udev_subsystems->len; i++) { const gchar *subsystem_tmp = g_ptr_array_index (priv->udev_subsystems, i); if (g_strcmp0 (subsystem_tmp, subsystem) == 0) @@ -1691,21 +1811,10 @@ fu_plugin_usb_device_added (FuPlugin *self, FuUsbDevice *device, GError **error) if (locker == NULL) return FALSE; fu_plugin_device_add (self, dev); + fu_plugin_runner_device_added (self, dev); return TRUE; } -static gboolean -fu_plugin_udev_device_changed (FuPlugin *self, FuUdevDevice *device, GError **error) -{ - g_autoptr(FuDeviceLocker) locker = NULL; - - /* open */ - locker = fu_device_locker_new (FU_DEVICE (device), error); - if (locker == NULL) - return FALSE; - return fu_device_rescan (FU_DEVICE (device), error); -} - static gboolean fu_plugin_udev_device_added (FuPlugin *self, FuUdevDevice *device, GError **error) { @@ -1742,6 +1851,7 @@ fu_plugin_udev_device_added (FuPlugin *self, FuUdevDevice *device, GError **erro if (locker == NULL) return FALSE; fu_plugin_device_add (self, FU_DEVICE (dev)); + fu_plugin_runner_device_added (self, dev); return TRUE; } @@ -1765,7 +1875,7 @@ fu_plugin_runner_usb_device_added (FuPlugin *self, FuUsbDevice *device, GError * g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1782,11 +1892,11 @@ fu_plugin_runner_usb_device_added (FuPlugin *self, FuUsbDevice *device, GError * } return TRUE; } - g_debug ("performing usb_device_added() on %s", priv->name); + g_debug ("usb_device_added(%s)", fu_plugin_get_name (self)); if (!func (self, device, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for usb_device_added()", - priv->name); + g_critical ("unset plugin error in usb_device_added(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -1794,7 +1904,7 @@ fu_plugin_runner_usb_device_added (FuPlugin *self, FuUsbDevice *device, GError * } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to add device using on %s: ", - priv->name); + fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -1820,7 +1930,7 @@ fu_plugin_runner_udev_device_added (FuPlugin *self, FuUdevDevice *device, GError g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1837,11 +1947,11 @@ fu_plugin_runner_udev_device_added (FuPlugin *self, FuUdevDevice *device, GError } return TRUE; } - g_debug ("performing udev_device_added() on %s", priv->name); + g_debug ("udev_device_added(%s)", fu_plugin_get_name (self)); if (!func (self, device, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for udev_device_added()", - priv->name); + g_critical ("unset plugin error in udev_device_added(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -1849,7 +1959,7 @@ fu_plugin_runner_udev_device_added (FuPlugin *self, FuUdevDevice *device, GError } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to add device using on %s: ", - priv->name); + fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -1875,7 +1985,7 @@ fu_plugin_runner_udev_device_changed (FuPlugin *self, FuUdevDevice *device, GErr g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -1884,19 +1994,13 @@ fu_plugin_runner_udev_device_changed (FuPlugin *self, FuUdevDevice *device, GErr /* optional */ g_module_symbol (priv->module, "fu_plugin_udev_device_changed", (gpointer *) &func); - if (func == NULL) { - if (priv->device_gtype != G_TYPE_INVALID || - fu_device_get_specialized_gtype (FU_DEVICE (device)) != G_TYPE_INVALID) { - if (!fu_plugin_udev_device_changed (self, device, error)) - return FALSE; - } + if (func == NULL) return TRUE; - } - g_debug ("performing udev_device_changed() on %s", priv->name); + g_debug ("udev_device_changed(%s)", fu_plugin_get_name (self)); if (!func (self, device, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for udev_device_changed()", - priv->name); + g_critical ("unset plugin error in udev_device_changed(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -1904,12 +2008,41 @@ fu_plugin_runner_udev_device_changed (FuPlugin *self, FuUdevDevice *device, GErr } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to change device on %s: ", - priv->name); + fu_plugin_get_name (self)); return FALSE; } return TRUE; } +/** + * fu_plugin_runner_device_added: + * @self: a #FuPlugin + * @device: a #FuDevice + * + * Call the device_added routine for the plugin + * + * Since: 1.5.0 + **/ +void +fu_plugin_runner_device_added (FuPlugin *self, FuDevice *device) +{ + FuPluginPrivate *priv = GET_PRIVATE (self); + FuPluginDeviceRegisterFunc func = NULL; + + /* not enabled */ + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) + return; + if (priv->module == NULL) + return; + + /* optional */ + g_module_symbol (priv->module, "fu_plugin_device_added", (gpointer *) &func); + if (func == NULL) + return; + g_debug ("fu_plugin_device_added(%s)", fu_plugin_get_name (self)); + func (self, device); +} + /** * fu_plugin_runner_device_removed: * @self: a #FuPlugin @@ -1947,7 +2080,7 @@ fu_plugin_runner_device_register (FuPlugin *self, FuDevice *device) FuPluginDeviceRegisterFunc func = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return; if (priv->module == NULL) return; @@ -1959,7 +2092,7 @@ fu_plugin_runner_device_register (FuPlugin *self, FuDevice *device) /* optional */ g_module_symbol (priv->module, "fu_plugin_device_registered", (gpointer *) &func); if (func != NULL) { - g_debug ("performing fu_plugin_device_registered() on %s", priv->name); + g_debug ("fu_plugin_device_registered(%s)", fu_plugin_get_name (self)); func (self, device); } } @@ -1983,7 +2116,7 @@ fu_plugin_runner_device_created (FuPlugin *self, FuDevice *device, GError **erro FuPluginDeviceFunc func = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; if (priv->module == NULL) return TRUE; @@ -1992,7 +2125,7 @@ fu_plugin_runner_device_created (FuPlugin *self, FuDevice *device, GError **erro g_module_symbol (priv->module, "fu_plugin_device_created", (gpointer *) &func); if (func == NULL) return TRUE; - g_debug ("performing fu_plugin_device_created() on %s", priv->name); + g_debug ("fu_plugin_device_created(%s)", fu_plugin_get_name (self)); return func (self, device, error); } @@ -2021,7 +2154,7 @@ fu_plugin_runner_verify (FuPlugin *self, g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -2046,12 +2179,12 @@ fu_plugin_runner_verify (FuPlugin *self, return FALSE; /* run vfunc */ - g_debug ("performing verify() on %s", priv->name); + g_debug ("verify(%s)", fu_plugin_get_name (self)); if (!func (self, device, flags, &error_local)) { g_autoptr(GError) error_attach = NULL; if (error_local == NULL) { - g_critical ("unset error in plugin %s for verify()", - priv->name); + g_critical ("unset plugin error in verify(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -2059,7 +2192,7 @@ fu_plugin_runner_verify (FuPlugin *self, } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to verify using %s: ", - priv->name); + fu_plugin_get_name (self)); /* make the device "work" again, but don't prefix the error */ if (!fu_plugin_runner_device_generic (self, device, "fu_plugin_update_attach", @@ -2191,7 +2324,7 @@ fu_plugin_runner_update (FuPlugin *self, g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) { + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) { g_debug ("plugin not enabled, skipping"); return TRUE; } @@ -2205,15 +2338,15 @@ fu_plugin_runner_update (FuPlugin *self, /* optional */ g_module_symbol (priv->module, "fu_plugin_update", (gpointer *) &update_func); if (update_func == NULL) { - g_debug ("running superclassed write_firmware() on %s", priv->name); + g_debug ("superclassed write_firmware(%s)", fu_plugin_get_name (self)); return fu_plugin_device_write_firmware (self, device, blob_fw, flags, error); } /* online */ if (!update_func (self, device, blob_fw, flags, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for update()", - priv->name); + g_critical ("unset plugin error in update(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -2256,7 +2389,7 @@ fu_plugin_runner_clear_results (FuPlugin *self, FuDevice *device, GError **error g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -2267,11 +2400,11 @@ fu_plugin_runner_clear_results (FuPlugin *self, FuDevice *device, GError **error g_module_symbol (priv->module, "fu_plugin_clear_results", (gpointer *) &func); if (func == NULL) return TRUE; - g_debug ("performing clear_result() on %s", priv->name); + g_debug ("clear_result(%s)", fu_plugin_get_name (self)); if (!func (self, device, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for clear_result()", - priv->name); + g_critical ("unset plugin error in clear_result(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -2279,7 +2412,7 @@ fu_plugin_runner_clear_results (FuPlugin *self, FuDevice *device, GError **error } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to clear_result using %s: ", - priv->name); + fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -2305,7 +2438,7 @@ fu_plugin_runner_get_results (FuPlugin *self, FuDevice *device, GError **error) g_autoptr(GError) error_local = NULL; /* not enabled */ - if (!priv->enabled) + if (fu_plugin_has_flag (self, FWUPD_PLUGIN_FLAG_DISABLED)) return TRUE; /* no object loaded */ @@ -2316,11 +2449,11 @@ fu_plugin_runner_get_results (FuPlugin *self, FuDevice *device, GError **error) g_module_symbol (priv->module, "fu_plugin_get_results", (gpointer *) &func); if (func == NULL) return TRUE; - g_debug ("performing get_results() on %s", priv->name); + g_debug ("get_results(%s)", fu_plugin_get_name (self)); if (!func (self, device, &error_local)) { if (error_local == NULL) { - g_critical ("unset error in plugin %s for get_results()", - priv->name); + g_critical ("unset plugin error in get_results(%s)", + fu_plugin_get_name (self)); g_set_error_literal (&error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -2328,7 +2461,7 @@ fu_plugin_runner_get_results (FuPlugin *self, FuDevice *device, GError **error) } g_propagate_prefixed_error (error, g_steal_pointer (&error_local), "failed to get_results using %s: ", - priv->name); + fu_plugin_get_name (self)); return FALSE; } return TRUE; @@ -2421,6 +2554,8 @@ void fu_plugin_add_rule (FuPlugin *self, FuPluginRule rule, const gchar *name) { FuPluginPrivate *priv = fu_plugin_get_instance_private (self); + if (priv->rules[rule] == NULL) + priv->rules[rule] = g_ptr_array_new_with_free_func (g_free); g_ptr_array_add (priv->rules[rule], g_strdup (name)); g_signal_emit (self, signals[SIGNAL_RULES_CHANGED], 0); } @@ -2432,7 +2567,7 @@ fu_plugin_add_rule (FuPlugin *self, FuPluginRule rule, const gchar *name) * * Gets the plugin IDs that should be run after this plugin. * - * Returns: (element-type utf8) (transfer none): the list of plugin names, e.g. ['appstream'] + * Returns: (element-type utf8) (transfer none) (nullable): the list of plugin names, e.g. ['appstream'] * * Since: 1.0.0 **/ @@ -2460,6 +2595,8 @@ gboolean fu_plugin_has_rule (FuPlugin *self, FuPluginRule rule, const gchar *name) { FuPluginPrivate *priv = fu_plugin_get_instance_private (self); + if (priv->rules[rule] == NULL) + return FALSE; for (guint i = 0; i < priv->rules[rule]->len; i++) { const gchar *tmp = g_ptr_array_index (priv->rules[rule], i); if (g_strcmp0 (tmp, name) == 0) @@ -2486,6 +2623,12 @@ void fu_plugin_add_report_metadata (FuPlugin *self, const gchar *key, const gchar *value) { FuPluginPrivate *priv = fu_plugin_get_instance_private (self); + if (priv->report_metadata == NULL) { + priv->report_metadata = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_free); + } g_hash_table_insert (priv->report_metadata, g_strdup (key), g_strdup (value)); } @@ -2495,7 +2638,7 @@ fu_plugin_add_report_metadata (FuPlugin *self, const gchar *key, const gchar *va * * Returns the list of additional metadata to be added when filing a report. * - * Returns: (transfer none): the map of report metadata + * Returns: (transfer none) (nullable): the map of report metadata * * Since: 1.0.4 **/ @@ -2571,9 +2714,7 @@ fu_plugin_get_config_value_boolean (FuPlugin *self, const gchar *key) gint fu_plugin_name_compare (FuPlugin *plugin1, FuPlugin *plugin2) { - FuPluginPrivate *priv1 = fu_plugin_get_instance_private (plugin1); - FuPluginPrivate *priv2 = fu_plugin_get_instance_private (plugin2); - return g_strcmp0 (priv1->name, priv2->name); + return g_strcmp0 (fu_plugin_get_name (plugin1), fu_plugin_get_name (plugin2)); } /** @@ -2628,6 +2769,12 @@ fu_plugin_class_init (FuPluginClass *klass) G_STRUCT_OFFSET (FuPluginClass, recoldplug), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + signals[SIGNAL_SECURITY_CHANGED] = + g_signal_new ("security-changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FuPluginClass, security_changed), + NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); signals[SIGNAL_SET_COLDPLUG_DELAY] = g_signal_new ("set-coldplug-delay", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, @@ -2658,14 +2805,7 @@ static void fu_plugin_init (FuPlugin *self) { FuPluginPrivate *priv = GET_PRIVATE (self); - priv->enabled = TRUE; - priv->udev_subsystems = g_ptr_array_new_with_free_func (g_free); - priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) g_object_unref); g_rw_lock_init (&priv->devices_mutex); - priv->report_metadata = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - for (guint i = 0; i < FU_PLUGIN_RULE_LAST; i++) - priv->rules[i] = g_ptr_array_new_with_free_func (g_free); } static void @@ -2675,18 +2815,21 @@ fu_plugin_finalize (GObject *object) FuPluginPrivate *priv = GET_PRIVATE (self); FuPluginInitFunc func = NULL; + g_rw_lock_clear (&priv->devices_mutex); + /* optional */ if (priv->module != NULL) { g_module_symbol (priv->module, "fu_plugin_destroy", (gpointer *) &func); if (func != NULL) { - g_debug ("performing destroy() on %s", priv->name); + g_debug ("destroy(%s)", fu_plugin_get_name (self)); func (self); } } - for (guint i = 0; i < FU_PLUGIN_RULE_LAST; i++) - g_ptr_array_unref (priv->rules[i]); - + for (guint i = 0; i < FU_PLUGIN_RULE_LAST; i++) { + if (priv->rules[i] != NULL) + g_ptr_array_unref (priv->rules[i]); + } if (priv->usb_ctx != NULL) g_object_unref (priv->usb_ctx); if (priv->hwids != NULL) @@ -2701,18 +2844,20 @@ fu_plugin_finalize (GObject *object) g_hash_table_unref (priv->runtime_versions); if (priv->compile_versions != NULL) g_hash_table_unref (priv->compile_versions); - g_hash_table_unref (priv->devices); - g_hash_table_unref (priv->report_metadata); - g_rw_lock_clear (&priv->devices_mutex); + if (priv->report_metadata != NULL) + g_hash_table_unref (priv->report_metadata); + if (priv->devices != NULL) + g_hash_table_unref (priv->devices); g_free (priv->build_hash); - g_free (priv->name); g_free (priv->data); /* Must happen as the last step to avoid prematurely * freeing memory held by the plugin */ -#ifndef RUNNING_ON_VALGRIND +#ifdef RUNNING_ON_VALGRIND + if (priv->module != NULL && RUNNING_ON_VALGRIND == 0) +#else if (priv->module != NULL) - g_module_close (priv->module); #endif + g_module_close (priv->module); G_OBJECT_CLASS (fu_plugin_parent_class)->finalize (object); } diff --git a/libfwupdplugin/fu-plugin.h b/libfwupdplugin/fu-plugin.h index 65625e2df..0b232eb8d 100644 --- a/libfwupdplugin/fu-plugin.h +++ b/libfwupdplugin/fu-plugin.h @@ -22,13 +22,18 @@ #include "fu-udev-device.h" #endif #include "fwupd-common.h" +#include "fwupd-plugin.h" #define FU_TYPE_PLUGIN (fu_plugin_get_type ()) -G_DECLARE_DERIVABLE_TYPE (FuPlugin, fu_plugin, FU, PLUGIN, GObject) +G_DECLARE_DERIVABLE_TYPE (FuPlugin, fu_plugin, FU, PLUGIN, FwupdPlugin) + +#define fu_plugin_get_flags(p) fwupd_plugin_get_flags(FWUPD_PLUGIN(p)) +#define fu_plugin_has_flag(p,f) fwupd_plugin_has_flag(FWUPD_PLUGIN(p),f) +#define fu_plugin_add_flag(p,f) fwupd_plugin_add_flag(FWUPD_PLUGIN(p),f) struct _FuPluginClass { - GObjectClass parent_class; + FwupdPluginClass parent_class; /* signals */ void (* device_added) (FuPlugin *self, FuDevice *device); @@ -49,8 +54,9 @@ struct _FuPluginClass gboolean (* add_firmware_gtype) (FuPlugin *self, const gchar *id, GType gtype); + void (* security_changed) (FuPlugin *self); /*< private >*/ - gpointer padding[21]; + gpointer padding[20]; }; /** @@ -95,9 +101,11 @@ const gchar *fu_plugin_get_name (FuPlugin *self); FuPluginData *fu_plugin_get_data (FuPlugin *self); FuPluginData *fu_plugin_alloc_data (FuPlugin *self, gsize data_sz); -gboolean fu_plugin_get_enabled (FuPlugin *self); +gboolean fu_plugin_get_enabled (FuPlugin *self) +G_DEPRECATED_FOR(fu_plugin_has_flag); void fu_plugin_set_enabled (FuPlugin *self, - gboolean enabled); + gboolean enabled) +G_DEPRECATED_FOR(fu_plugin_add_flag); void fu_plugin_set_build_hash (FuPlugin *self, const gchar *build_hash); GUsbContext *fu_plugin_get_usb_context (FuPlugin *self); @@ -108,6 +116,7 @@ void fu_plugin_device_remove (FuPlugin *self, void fu_plugin_device_register (FuPlugin *self, FuDevice *device); void fu_plugin_request_recoldplug (FuPlugin *self); +void fu_plugin_security_changed (FuPlugin *self); void fu_plugin_set_coldplug_delay (FuPlugin *self, guint duration); void fu_plugin_set_device_gtype (FuPlugin *self, diff --git a/libfwupdplugin/fu-quirks.c b/libfwupdplugin/fu-quirks.c index 0125f422c..1ed3cf669 100644 --- a/libfwupdplugin/fu-quirks.c +++ b/libfwupdplugin/fu-quirks.c @@ -25,7 +25,7 @@ * SECTION:fu-quirks * @short_description: device quirks * - * Quirks can be used to modify device behaviour. + * Quirks can be used to modify device behavior. * When fwupd is installed in long-term support distros it's very hard to * backport new versions as new hardware is released. * @@ -149,10 +149,8 @@ fu_quirks_add_quirks_for_path (FuQuirks *self, XbBuilder *builder, /* add valid files to the array */ path_hw = g_build_filename (path, "quirks.d", NULL); - if (!g_file_test (path_hw, G_FILE_TEST_EXISTS)) { - g_debug ("no %s, skipping", path_hw); + if (!g_file_test (path_hw, G_FILE_TEST_EXISTS)) return TRUE; - } dir = g_dir_open (path_hw, 0, error); if (dir == NULL) return FALSE; @@ -229,7 +227,7 @@ fu_quirks_check_silo (FuQuirks *self, GError **error) cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG); xmlbfn = g_build_filename (cachedirpkg, "quirks.xmlb", NULL); file = g_file_new_for_path (xmlbfn); - if (g_getenv ("XMLB_VERBOSE") != NULL) { + if (g_getenv ("FWUPD_XMLB_VERBOSE") != NULL) { xb_builder_set_profile_flags (builder, XB_SILO_PROFILE_FLAG_XPATH | XB_SILO_PROFILE_FLAG_DEBUG); diff --git a/libfwupdplugin/fu-quirks.h b/libfwupdplugin/fu-quirks.h index 763c365e6..49be5c236 100644 --- a/libfwupdplugin/fu-quirks.h +++ b/libfwupdplugin/fu-quirks.h @@ -48,6 +48,7 @@ gboolean fu_quirks_lookup_by_id_iter (FuQuirks *self, #define FU_QUIRKS_SUMMARY "Summary" #define FU_QUIRKS_ICON "Icon" #define FU_QUIRKS_NAME "Name" +#define FU_QUIRKS_BRANCH "Branch" #define FU_QUIRKS_GUID "Guid" #define FU_QUIRKS_COUNTERPART_GUID "CounterpartGuid" #define FU_QUIRKS_PARENT_GUID "ParentGuid" diff --git a/libfwupdplugin/fu-security-attrs-private.h b/libfwupdplugin/fu-security-attrs-private.h new file mode 100644 index 000000000..fc435fc42 --- /dev/null +++ b/libfwupdplugin/fu-security-attrs-private.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +/** + * FuSecurityAttrsFlags: + * @FU_SECURITY_ATTRS_FLAG_NONE: No flags set + * @FU_SECURITY_ATTRS_FLAG_ADD_VERSION: Add the daemon version to the HSI string + * + * The flags to use when calculating an HSI version. + **/ +typedef enum { + FU_SECURITY_ATTRS_FLAG_NONE = 0, + FU_SECURITY_ATTRS_FLAG_ADD_VERSION = 1 << 0, + /*< private >*/ + FU_SECURITY_ATTRS_FLAG_LAST +} FuSecurityAttrsFlags; + +#include "fu-security-attrs.h" + +FuSecurityAttrs *fu_security_attrs_new (void); +gchar *fu_security_attrs_calculate_hsi (FuSecurityAttrs *self, + FuSecurityAttrsFlags flags); +void fu_security_attrs_depsolve (FuSecurityAttrs *self); +GVariant *fu_security_attrs_to_variant (FuSecurityAttrs *self); +GPtrArray *fu_security_attrs_get_all (FuSecurityAttrs *self); diff --git a/libfwupdplugin/fu-security-attrs.c b/libfwupdplugin/fu-security-attrs.c new file mode 100644 index 000000000..b35835f95 --- /dev/null +++ b/libfwupdplugin/fu-security-attrs.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuSecurityAttrs" + +#include "config.h" + +#include + +#include "fu-security-attrs-private.h" + +struct _FuSecurityAttrs { + GObject parent_instance; + GPtrArray *attrs; +}; + +/* probably sane to *not* make this part of the ABI */ +#define FWUPD_SECURITY_ATTR_ID_DOC_URL "https://fwupd.github.io/hsi.html" + +G_DEFINE_TYPE (FuSecurityAttrs, fu_security_attrs, G_TYPE_OBJECT) + +static void +fu_security_attrs_finalize (GObject *obj) +{ + FuSecurityAttrs *self = FU_SECURITY_ATTRS (obj); + g_ptr_array_unref (self->attrs); + G_OBJECT_CLASS (fu_security_attrs_parent_class)->finalize (obj); +} + +static void +fu_security_attrs_class_init (FuSecurityAttrsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_security_attrs_finalize; +} + +static void +fu_security_attrs_init (FuSecurityAttrs *self) +{ + self->attrs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); +} + +/** + * fu_security_attrs_append: + * @self: A #FuSecurityAttrs + * @attr: a #FwupdSecurityAttr + * + * Adds a #FwupdSecurityAttr to the array. + * + * Since: 1.5.0 + **/ +void +fu_security_attrs_append (FuSecurityAttrs *self, FwupdSecurityAttr *attr) +{ + g_return_if_fail (FU_IS_SECURITY_ATTRS (self)); + g_return_if_fail (FWUPD_IS_SECURITY_ATTR (attr)); + + /* sanity check */ + if (fwupd_security_attr_get_plugin (attr) == NULL) { + g_warning ("%s has no plugin set", + fwupd_security_attr_get_appstream_id (attr)); + } + + /* sanity check, and correctly prefix the URLs with the current mirror */ + if (fwupd_security_attr_get_url (attr) == NULL) { + g_autofree gchar *url = NULL; + url = g_strdup_printf ("%s#%s", + FWUPD_SECURITY_ATTR_ID_DOC_URL, + fwupd_security_attr_get_appstream_id (attr)); + fwupd_security_attr_set_url (attr, url); + } else if (g_str_has_prefix (fwupd_security_attr_get_url (attr), "#")) { + g_autofree gchar *url = NULL; + url = g_strdup_printf ("%s%s", + FWUPD_SECURITY_ATTR_ID_DOC_URL, + fwupd_security_attr_get_url (attr)); + fwupd_security_attr_set_url (attr, url); + } + g_ptr_array_add (self->attrs, g_object_ref (attr)); +} + +/** + * fu_security_attrs_to_variant: + * @self: A #FuSecurityAttrs + * + * Converts the #FwupdSecurityAttr objects into a variant array. + * + * Returns: a #GVariant or %NULL + * + * Since: 1.5.0 + **/ +GVariant * +fu_security_attrs_to_variant (FuSecurityAttrs *self) +{ + GVariantBuilder builder; + + g_return_val_if_fail (FU_IS_SECURITY_ATTRS (self), NULL); + g_return_val_if_fail (self->attrs->len > 0, NULL); + g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + + for (guint i = 0; i < self->attrs->len; i++) { + FwupdSecurityAttr *security_attr = g_ptr_array_index (self->attrs, i); + GVariant *tmp = fwupd_security_attr_to_variant (security_attr); + g_variant_builder_add_value (&builder, tmp); + } + return g_variant_new ("(aa{sv})", &builder); +} + +/** + * fu_security_attrs_get_all: + * @self: A #FuSecurityAttrs + * + * Gets all the attributes in the object. + * + * Returns: (transfer container) (element-type FwupdSecurityAttr): attributes + * + * Since: 1.5.0 + **/ +GPtrArray * +fu_security_attrs_get_all (FuSecurityAttrs *self) +{ + g_return_val_if_fail (FU_IS_SECURITY_ATTRS (self), NULL); + return g_ptr_array_ref (self->attrs); +} + +/** + * fu_security_attrs_remove_all: + * @self: A #FuSecurityAttrs + * + * Removes all the attributes in the object. + * + * Since: 1.5.0 + **/ +void +fu_security_attrs_remove_all (FuSecurityAttrs *self) +{ + g_return_if_fail (FU_IS_SECURITY_ATTRS (self)); + return g_ptr_array_set_size (self->attrs, 0); +} + +/** + * fu_security_attrs_calculate_hsi: + * @self: A #FuSecurityAttrs + * @flags: Flags to use while calcuating the HSI + * + * Calculates the HSI string from the appended attributes. + * + * Returns: (transfer full): a string or %NULL + * + * Since: 1.5.0 + **/ +gchar * +fu_security_attrs_calculate_hsi (FuSecurityAttrs *self, + FuSecurityAttrsFlags flags) +{ + guint hsi_number = 0; + FwupdSecurityAttrFlags attr_flags = FWUPD_SECURITY_ATTR_FLAG_NONE; + GString *str = g_string_new ("HSI:"); + const FwupdSecurityAttrFlags hpi_suffixes[] = { + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES, + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION, + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE, + FWUPD_SECURITY_ATTR_FLAG_NONE, + }; + + g_return_val_if_fail (FU_IS_SECURITY_ATTRS (self), NULL); + + /* find the highest HSI number where there are no failures and at least + * one success */ + for (guint j = 1; j <= FWUPD_SECURITY_ATTR_LEVEL_LAST; j++) { + gboolean success_cnt = 0; + gboolean failure_cnt = 0; + for (guint i = 0; i < self->attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index (self->attrs, i); + if (fwupd_security_attr_get_level (attr) != j) + continue; + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) + success_cnt++; + else if (!fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) + failure_cnt++; + } + + /* abort */ + if (failure_cnt > 0) { + hsi_number = j - 1; + break; + } + + /* we matched at least one thing on this level */ + if (success_cnt > 0) + hsi_number = j; + } + + /* get a logical OR of the runtime flags */ + for (guint i = 0; i < self->attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index (self->attrs, i); + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) + continue; + /* positive things */ + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES) || + fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION)) { + if (!fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) + continue; + } + /* negative things */ + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE)) { + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) + continue; + } + attr_flags |= fwupd_security_attr_get_flags (attr); + } + + g_string_append_printf (str, "%u", hsi_number); + if (attr_flags & (FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES | + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION | + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE)) { + g_string_append (str, "+"); + for (guint j = 0; hpi_suffixes[j] != FWUPD_SECURITY_ATTR_FLAG_NONE; j++) { + if (attr_flags & hpi_suffixes[j]) + g_string_append (str, fwupd_security_attr_flag_to_suffix (hpi_suffixes[j])); + } + } + + if (flags & FU_SECURITY_ATTRS_FLAG_ADD_VERSION) { + g_string_append_printf (str, " (v%d.%d.%d)", + FWUPD_MAJOR_VERSION, + FWUPD_MINOR_VERSION, + FWUPD_MICRO_VERSION); + } + + return g_string_free (str, FALSE); +} + +static gchar * +fu_security_attrs_get_sort_key (FwupdSecurityAttr *attr) +{ + GString *str = g_string_new (NULL); + + /* level */ + g_string_append_printf (str, "%u", fwupd_security_attr_get_level (attr)); + + /* success -> fail -> obsoletes */ + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { + g_string_append (str, "0"); + } else if (!fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS) && + !fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { + g_string_append (str, "1"); + } else { + g_string_append (str, "9"); + } + + /* prefer name, but fallback to appstream-id for tests */ + if (fwupd_security_attr_get_name (attr) != NULL) { + g_string_append (str, fwupd_security_attr_get_name (attr)); + } else { + g_string_append (str, fwupd_security_attr_get_appstream_id (attr)); + } + return g_string_free (str, FALSE); +} + +static gint +fu_security_attrs_sort_cb (gconstpointer item1, gconstpointer item2) +{ + FwupdSecurityAttr *attr1 = *((FwupdSecurityAttr **) item1); + FwupdSecurityAttr *attr2 = *((FwupdSecurityAttr **) item2); + g_autofree gchar *sort1 = fu_security_attrs_get_sort_key (attr1); + g_autofree gchar *sort2 = fu_security_attrs_get_sort_key (attr2); + return g_strcmp0 (sort1, sort2); +} + +/** + * fu_security_attrs_depsolve: + * @self: A #FuSecurityAttrs + * + * Marks any attributes with %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED that have been + * defined as obsoleted by other attributes. + * + * It is only required to call this function once, and should be done when all + * attributes have been added. This will also sort the attrs. + * + * Since: 1.5.0 + **/ +void +fu_security_attrs_depsolve (FuSecurityAttrs *self) +{ + g_autoptr(GHashTable) attrs_by_id = NULL; + + g_return_if_fail (FU_IS_SECURITY_ATTRS (self)); + + /* make hash of ID -> object */ + attrs_by_id = g_hash_table_new (g_str_hash, g_str_equal); + for (guint i = 0; i < self->attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index (self->attrs, i); + g_hash_table_insert (attrs_by_id, + (gpointer) fwupd_security_attr_get_appstream_id (attr), + (gpointer) attr); + } + + /* set flat where required */ + for (guint i = 0; i < self->attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index (self->attrs, i); + GPtrArray *obsoletes = fwupd_security_attr_get_obsoletes (attr); + for (guint j = 0; j < obsoletes->len; j++) { + const gchar *obsolete = g_ptr_array_index (obsoletes, j); + FwupdSecurityAttr *attr_tmp = g_hash_table_lookup (attrs_by_id, obsolete); + + /* by AppStream ID */ + if (attr_tmp != NULL) { + g_debug ("security attr %s obsoleted by %s", obsolete, + fwupd_security_attr_get_appstream_id (attr_tmp)); + fwupd_security_attr_add_flag (attr_tmp, + FWUPD_SECURITY_ATTR_FLAG_OBSOLETED); + } + + /* by plugin name */ + for (guint k = 0; k < self->attrs->len; k++) { + attr_tmp = g_ptr_array_index (self->attrs, k); + if (g_strcmp0 (obsolete, fwupd_security_attr_get_plugin (attr_tmp)) == 0) { + g_debug ("security attr %s obsoleted by %s", obsolete, + fwupd_security_attr_get_appstream_id (attr_tmp)); + fwupd_security_attr_add_flag (attr_tmp, + FWUPD_SECURITY_ATTR_FLAG_OBSOLETED); + } + } + } + } + + /* sort */ + g_ptr_array_sort (self->attrs, fu_security_attrs_sort_cb); +} + +/** + * fu_security_attrs_new: + * + * Returns: a #FuSecurityAttrs + * + * Since: 1.5.0 + **/ +FuSecurityAttrs * +fu_security_attrs_new (void) +{ + return g_object_new (FU_TYPE_SECURITY_ATTRS, NULL); +} diff --git a/libfwupdplugin/fu-security-attrs.h b/libfwupdplugin/fu-security-attrs.h new file mode 100644 index 000000000..a65a6d7ff --- /dev/null +++ b/libfwupdplugin/fu-security-attrs.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-security-attr-private.h" + +#define FU_TYPE_SECURITY_ATTRS (fu_security_attrs_get_type ()) + +G_DECLARE_FINAL_TYPE (FuSecurityAttrs, fu_security_attrs, FU, SECURITY_ATTRS, GObject) + +void fu_security_attrs_append (FuSecurityAttrs *self, + FwupdSecurityAttr *attr); +void fu_security_attrs_remove_all (FuSecurityAttrs *self); diff --git a/libfwupdplugin/fu-self-test.c b/libfwupdplugin/fu-self-test.c index cfda8babb..20de92455 100644 --- a/libfwupdplugin/fu-self-test.c +++ b/libfwupdplugin/fu-self-test.c @@ -14,6 +14,7 @@ #include "fu-device-private.h" #include "fu-plugin-private.h" +#include "fu-security-attrs-private.h" #include "fu-smbios-private.h" static GMainLoop *_test_loop = NULL; @@ -106,6 +107,33 @@ fu_archive_cab_func (void) g_assert_null (data_tmp); } +static void +fu_common_byte_array_func (void) +{ + g_autoptr(GByteArray) array = g_byte_array_new (); + + fu_byte_array_append_uint8 (array, (guint8) 'h'); + fu_byte_array_append_uint8 (array, (guint8) 'e'); + fu_byte_array_append_uint8 (array, (guint8) 'l'); + fu_byte_array_append_uint8 (array, (guint8) 'l'); + fu_byte_array_append_uint8 (array, (guint8) 'o'); + g_assert_cmpint (array->len, ==, 5); + g_assert_cmpint (memcmp (array->data, "hello", array->len), ==, 0); + + fu_byte_array_set_size (array, 10); + g_assert_cmpint (array->len, ==, 10); + g_assert_cmpint (memcmp (array->data, "hello\0\0\0\0\0", array->len), ==, 0); +} + +static void +fu_common_crc_func (void) +{ + guint8 buf[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 }; + g_assert_cmpint (fu_common_crc8 (buf, sizeof(buf)), ==, 0x7A); + g_assert_cmpint (fu_common_crc16 (buf, sizeof(buf)), ==, 0x4DF1); + g_assert_cmpint (fu_common_crc32 (buf, sizeof(buf)), ==, 0x40EFAB9E); +} + static void fu_common_string_append_kv_func (void) { @@ -193,14 +221,14 @@ fu_device_metadata_func (void) g_assert_false (fu_device_get_metadata_boolean (device, "unknown")); /* integer */ - fu_device_set_metadata_integer (device, "dum", 12345); - g_assert_cmpstr (fu_device_get_metadata (device, "dum"), ==, "12345"); - g_assert_cmpint (fu_device_get_metadata_integer (device, "dum"), ==, 12345); + fu_device_set_metadata_integer (device, "bam", 12345); + g_assert_cmpstr (fu_device_get_metadata (device, "bam"), ==, "12345"); + g_assert_cmpint (fu_device_get_metadata_integer (device, "bam"), ==, 12345); g_assert_cmpint (fu_device_get_metadata_integer (device, "unknown"), ==, G_MAXUINT); /* broken integer */ - fu_device_set_metadata (device, "dum", "123junk"); - g_assert_cmpint (fu_device_get_metadata_integer (device, "dum"), ==, G_MAXUINT); + fu_device_set_metadata (device, "bam", "123junk"); + g_assert_cmpint (fu_device_get_metadata_integer (device, "bam"), ==, G_MAXUINT); fu_device_set_metadata (device, "huge", "4294967296"); /* not 32 bit */ g_assert_cmpint (fu_device_get_metadata_integer (device, "huge"), ==, G_MAXUINT); } @@ -265,6 +293,31 @@ fu_smbios3_func (void) g_assert_cmpstr (str, ==, "Dell Inc."); } +static void +fu_smbios_dt_func (void) +{ + const gchar *str; + gboolean ret; + g_autofree gchar *path = NULL; + g_autoptr(FuSmbios) smbios = NULL; + g_autoptr(GError) error = NULL; + + path = g_build_filename (TESTDATADIR_SRC, "devicetree", "base", NULL); + smbios = fu_smbios_new (); + ret = fu_smbios_setup_from_path (smbios, path, &error); + g_assert_no_error (error); + g_assert (ret); + if (g_getenv ("VERBOSE") != NULL) { + g_autofree gchar *dump = fu_smbios_to_string (smbios); + g_debug ("%s", dump); + } + + /* get vendor */ + str = fu_smbios_get_string (smbios, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x04, &error); + g_assert_no_error (error); + g_assert_cmpstr (str, ==, "Hughski Limited"); +} + static void fu_hwids_func (void) { @@ -1012,6 +1065,19 @@ fu_device_poll_func (void) g_assert_cmpint (fu_device_get_metadata_integer (device, "cnt"), ==, cnt); } +static void +fu_device_func (void) +{ + g_autoptr(FuDevice) device = fu_device_new (); + g_autoptr(GPtrArray) possible_plugins = NULL; + + /* only add one plugin name of the same type */ + fu_device_add_possible_plugin (device, "test"); + fu_device_add_possible_plugin (device, "test"); + possible_plugins = fu_device_get_possible_plugins (device); + g_assert_cmpint (possible_plugins->len, ==, 1); +} + static void fu_device_flags_func (void) { @@ -1279,6 +1345,7 @@ fu_common_vercmp_func (void) /* same */ g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.3"), ==, 0); g_assert_cmpint (fu_common_vercmp ("001.002.003", "001.002.003"), ==, 0); + g_assert_cmpint (fu_common_vercmp_full ("0x00000002", "0x2", FWUPD_VERSION_FORMAT_HEX), ==, 0); /* upgrade and downgrade */ g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.4"), <, 0); @@ -1538,6 +1605,75 @@ fu_firmware_srec_tokenization_func (void) g_assert_cmpint (rcd->buf->data[0], ==, 0x50); } +static void +fu_firmware_build_func (void) +{ + gboolean ret; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = fu_firmware_new (); + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) blob2 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + g_autoptr(XbNode) n = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *buf = + "\n" + "\n" + " 1.2.3\n" + " \n" + " 4.5.6\n" + " header\n" + " 456\n" + " 0x456\n" + " aGVsbG8=\n" + " \n" + " \n" + " 7.8.9\n" + " header\n" + " 789\n" + " 0x789\n" + " \n" + "\n"; + blob = g_bytes_new_static (buf, strlen (buf)); + g_assert_no_error (error); + g_assert_nonnull (blob); + + /* parse XML */ + ret = xb_builder_source_load_bytes (source, blob, XB_BUILDER_SOURCE_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert (ret); + xb_builder_import_source (builder, source); + silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (silo); + n = xb_silo_query_first (silo, "firmware", &error); + g_assert_no_error (error); + g_assert_nonnull (n); + + /* build object */ + ret = fu_firmware_build (firmware, n, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpstr (fu_firmware_get_version (firmware), ==, "1.2.3"); + + /* verify image */ + img = fu_firmware_get_image_by_id (firmware, "header", &error); + g_assert_no_error (error); + g_assert_nonnull (img); + g_assert_cmpstr (fu_firmware_image_get_version (img), ==, "4.5.6"); + g_assert_cmpint (fu_firmware_image_get_idx (img), ==, 456); + g_assert_cmpint (fu_firmware_image_get_addr (img), ==, 0x456); + blob2 = fu_firmware_image_write (img, &error); + g_assert_no_error (error); + g_assert_nonnull (blob2); + g_assert_cmpint (g_bytes_get_size (blob2), ==, 5); + str = g_strndup (g_bytes_get_data (blob2, NULL), g_bytes_get_size (blob2)); + g_assert_cmpstr (str, ==, "hello"); +} + static void fu_firmware_dfu_func (void) { @@ -1582,17 +1718,20 @@ fu_firmware_dfu_func (void) static void fu_firmware_func (void) { + gboolean ret; g_autoptr(FuFirmware) firmware = fu_firmware_new (); g_autoptr(FuFirmwareImage) img1 = fu_firmware_image_new (NULL); g_autoptr(FuFirmwareImage) img2 = fu_firmware_image_new (NULL); g_autoptr(FuFirmwareImage) img_id = NULL; g_autoptr(FuFirmwareImage) img_idx = NULL; g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) images = NULL; g_autofree gchar *str = NULL; fu_firmware_image_set_addr (img1, 0x200); fu_firmware_image_set_idx (img1, 13); fu_firmware_image_set_id (img1, "primary"); + fu_firmware_image_set_filename (img1, "BIOS.bin"); fu_firmware_add_image (firmware, img1); fu_firmware_image_set_addr (img2, 0x400); fu_firmware_image_set_idx (img2, 23); @@ -1627,10 +1766,69 @@ fu_firmware_func (void) " ID: primary\n" " Index: 0xd\n" " Address: 0x200\n" + " Filename: BIOS.bin\n" " FuFirmwareImage:\n" " ID: secondary\n" " Index: 0x17\n" " Address: 0x400\n"); + + ret = fu_firmware_remove_image_by_idx (firmware, 0xd, &error); + g_assert_no_error (error); + g_assert_true (ret); + ret = fu_firmware_remove_image_by_id (firmware, "secondary", &error); + g_assert_no_error (error); + g_assert_true (ret); + images = fu_firmware_get_images (firmware); + g_assert_nonnull (images); + g_assert_cmpint (images->len, ==, 0); + ret = fu_firmware_remove_image_by_id (firmware, "NOTGOINGTOEXIST", &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_false (ret); +} + +static void +fu_firmware_dedupe_func (void) +{ + g_autoptr(FuFirmware) firmware = fu_firmware_new (); + g_autoptr(FuFirmwareImage) img1 = fu_firmware_image_new (NULL); + g_autoptr(FuFirmwareImage) img1_old = fu_firmware_image_new (NULL); + g_autoptr(FuFirmwareImage) img2 = fu_firmware_image_new (NULL); + g_autoptr(FuFirmwareImage) img2_old = fu_firmware_image_new (NULL); + g_autoptr(FuFirmwareImage) img_id = NULL; + g_autoptr(FuFirmwareImage) img_idx = NULL; + g_autoptr(GError) error = NULL; + + fu_firmware_add_flag (firmware, FU_FIRMWARE_FLAG_DEDUPE_ID); + fu_firmware_add_flag (firmware, FU_FIRMWARE_FLAG_DEDUPE_IDX); + + fu_firmware_image_set_idx (img1_old, 13); + fu_firmware_image_set_id (img1_old, "DAVE"); + fu_firmware_add_image (firmware, img1_old); + + fu_firmware_image_set_idx (img1, 13); + fu_firmware_image_set_id (img1, "primary"); + fu_firmware_add_image (firmware, img1); + + + fu_firmware_image_set_idx (img2_old, 123456); + fu_firmware_image_set_id (img2_old, "secondary"); + fu_firmware_add_image (firmware, img2_old); + + fu_firmware_image_set_idx (img2, 23); + fu_firmware_image_set_id (img2, "secondary"); + fu_firmware_add_image (firmware, img2); + + img_id = fu_firmware_get_image_by_id (firmware, "primary", &error); + g_assert_no_error (error); + g_assert_nonnull (img_id); + g_assert_cmpint (fu_firmware_image_get_idx (img_id), ==, 13); + g_assert_cmpstr (fu_firmware_image_get_id (img_id), ==, "primary"); + + img_idx = fu_firmware_get_image_by_idx (firmware, 23, &error); + g_assert_no_error (error); + g_assert_nonnull (img_idx); + g_assert_cmpint (fu_firmware_image_get_idx (img_idx), ==, 23); + g_assert_cmpstr (fu_firmware_image_get_id (img_idx), ==, "secondary"); } static void @@ -1639,18 +1837,31 @@ fu_efivar_func (void) gboolean ret; gsize sz = 0; guint32 attr = 0; + guint64 total; g_autofree guint8 *data = NULL; g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) names = NULL; /* check supported */ ret = fu_efivar_supported (&error); g_assert_no_error (error); g_assert_true (ret); + /* check we can get the space used */ + total = fu_efivar_space_used (&error); + g_assert_no_error (error); + g_assert_cmpint (total, ==, 0x2000); + /* check existing keys */ g_assert_false (fu_efivar_exists (FU_EFIVAR_GUID_EFI_GLOBAL, "NotGoingToExist")); g_assert_true (fu_efivar_exists (FU_EFIVAR_GUID_EFI_GLOBAL, "SecureBoot")); + /* list a few keys */ + names = fu_efivar_get_names (FU_EFIVAR_GUID_EFI_GLOBAL, &error); + g_assert_no_error (error); + g_assert_nonnull (names); + g_assert_cmpint (names->len, ==, 2); + /* write and read a key */ ret = fu_efivar_set_data (FU_EFIVAR_GUID_EFI_GLOBAL, "Test", (guint8 *) "1", 1, @@ -1781,6 +1992,108 @@ fu_device_retry_hardware_func (void) g_assert_cmpint (helper.cnt_failed, ==, 2); } +static void +fu_security_attrs_hsi_func (void) +{ + g_autofree gchar *hsi1 = NULL; + g_autofree gchar *hsi2 = NULL; + g_autofree gchar *hsi3 = NULL; + g_autofree gchar *hsi4 = NULL; + g_autofree gchar *hsi5 = NULL; + g_autofree gchar *hsi6 = NULL; + g_autofree gchar *hsi7 = NULL; + g_autofree gchar *hsi8 = NULL; + g_autofree gchar *expected_hsi8 = NULL; + g_autoptr(FuSecurityAttrs) attrs = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* no attrs */ + attrs = fu_security_attrs_new (); + hsi1 = fu_security_attrs_calculate_hsi (attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr (hsi1, ==, "HSI:0"); + + /* just success from HSI:1 */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE); + fwupd_security_attr_set_plugin (attr, "test"); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_url (attr, "http://test"); + fu_security_attrs_append (attrs, attr); + hsi2 = fu_security_attrs_calculate_hsi (attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr (hsi2, ==, "HSI:1"); + g_clear_object (&attr); + + /* add failed from HSI:2, so still HSI:1 */ + attr = fwupd_security_attr_new ("org.fwupd.hsi.PRX"); + fwupd_security_attr_set_plugin (attr, "test"); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + fwupd_security_attr_set_url (attr, "http://test"); + fu_security_attrs_append (attrs, attr); + hsi3 = fu_security_attrs_calculate_hsi (attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr (hsi3, ==, "HSI:1"); + g_clear_object (&attr); + + /* add attr from HSI:3, obsoleting the failure */ + attr = fwupd_security_attr_new ("org.fwupd.hsi.BIOSGuard"); + fwupd_security_attr_set_plugin (attr, "test"); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_add_obsolete (attr, "org.fwupd.hsi.PRX"); + fwupd_security_attr_set_url (attr, "http://test"); + fu_security_attrs_append (attrs, attr); + fu_security_attrs_depsolve (attrs); + hsi4 = fu_security_attrs_calculate_hsi (attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr (hsi4, ==, "HSI:3"); + g_clear_object (&attr); + + /* add taint that was fine */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS); + fwupd_security_attr_set_plugin (attr, "test"); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fwupd_security_attr_set_url (attr, "http://test"); + fu_security_attrs_append (attrs, attr); + hsi5 = fu_security_attrs_calculate_hsi (attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr (hsi5, ==, "HSI:3"); + g_clear_object (&attr); + + /* add updates and attestation */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_FWUPD_UPDATES); + fwupd_security_attr_set_plugin (attr, "test"); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_url (attr, "http://test"); + fu_security_attrs_append (attrs, attr); + hsi6 = fu_security_attrs_calculate_hsi (attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr (hsi6, ==, "HSI:3+UA"); + g_clear_object (&attr); + + /* add issue that was uncool */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP); + fwupd_security_attr_set_plugin (attr, "test"); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fwupd_security_attr_set_url (attr, "http://test"); + fu_security_attrs_append (attrs, attr); + hsi7 = fu_security_attrs_calculate_hsi (attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr (hsi7, ==, "HSI:3+UA!"); + g_clear_object (&attr); + + /* show version in the attribute */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP); + fwupd_security_attr_set_plugin (attr, "test"); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fwupd_security_attr_set_url (attr, "http://test"); + fu_security_attrs_append (attrs, attr); + hsi8 = fu_security_attrs_calculate_hsi (attrs, FU_SECURITY_ATTRS_FLAG_ADD_VERSION); + expected_hsi8 = g_strdup_printf ("HSI:3+UA! (v%d.%d.%d)", + FWUPD_MAJOR_VERSION, + FWUPD_MINOR_VERSION, + FWUPD_MICRO_VERSION); + g_assert_cmpstr (hsi8, ==, expected_hsi8); + g_clear_object (&attr); +} + int main (int argc, char **argv) { @@ -1796,11 +2109,14 @@ main (int argc, char **argv) g_setenv ("FWUPD_OFFLINE_TRIGGER", "/tmp/fwupd-self-test/system-update", TRUE); g_setenv ("FWUPD_LOCALSTATEDIR", "/tmp/fwupd-self-test/var", TRUE); + g_test_add_func ("/fwupd/security-attrs{hsi}", fu_security_attrs_hsi_func); g_test_add_func ("/fwupd/plugin{delay}", fu_plugin_delay_func); g_test_add_func ("/fwupd/plugin{quirks}", fu_plugin_quirks_func); g_test_add_func ("/fwupd/plugin{quirks-performance}", fu_plugin_quirks_performance_func); g_test_add_func ("/fwupd/plugin{quirks-device}", fu_plugin_quirks_device_func); g_test_add_func ("/fwupd/chunk", fu_chunk_func); + g_test_add_func ("/fwupd/common{byte-array}", fu_common_byte_array_func); + g_test_add_func ("/fwupd/common{crc}", fu_common_crc_func); g_test_add_func ("/fwupd/common{string-append-kv}", fu_common_string_append_kv_func); g_test_add_func ("/fwupd/common{version-guess-format}", fu_common_version_guess_format_func); g_test_add_func ("/fwupd/common{version}", fu_common_version_func); @@ -1823,7 +2139,10 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/hwids", fu_hwids_func); g_test_add_func ("/fwupd/smbios", fu_smbios_func); g_test_add_func ("/fwupd/smbios3", fu_smbios3_func); + g_test_add_func ("/fwupd/smbios{dt}", fu_smbios_dt_func); g_test_add_func ("/fwupd/firmware", fu_firmware_func); + g_test_add_func ("/fwupd/firmware{dedupe}", fu_firmware_dedupe_func); + g_test_add_func ("/fwupd/firmware{build}", fu_firmware_build_func); g_test_add_func ("/fwupd/firmware{ihex}", fu_firmware_ihex_func); g_test_add_func ("/fwupd/firmware{ihex-offset}", fu_firmware_ihex_offset_func); g_test_add_func ("/fwupd/firmware{ihex-signed}", fu_firmware_ihex_signed_func); @@ -1832,6 +2151,7 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/firmware{dfu}", fu_firmware_dfu_func); g_test_add_func ("/fwupd/archive{invalid}", fu_archive_invalid_func); g_test_add_func ("/fwupd/archive{cab}", fu_archive_cab_func); + g_test_add_func ("/fwupd/device", fu_device_func); g_test_add_func ("/fwupd/device{flags}", fu_device_flags_func); g_test_add_func ("/fwupd/device{parent}", fu_device_parent_func); g_test_add_func ("/fwupd/device{incorporate}", fu_device_incorporate_func); diff --git a/libfwupdplugin/fu-smbios.c b/libfwupdplugin/fu-smbios.c index c1e9cdee4..1b5d09df9 100644 --- a/libfwupdplugin/fu-smbios.c +++ b/libfwupdplugin/fu-smbios.c @@ -64,12 +64,71 @@ typedef struct __attribute__((packed)) { typedef struct { guint8 type; guint16 handle; - GBytes *data; + GByteArray *buf; GPtrArray *strings; } FuSmbiosItem; G_DEFINE_TYPE (FuSmbios, fu_smbios, G_TYPE_OBJECT) +static void +fu_smbios_convert_dt_string (FuSmbios *self, guint8 type, guint8 offset, + const gchar *path, const gchar *subpath) +{ + FuSmbiosItem *item = g_ptr_array_index (self->items, type); + gsize bufsz = 0; + g_autofree gchar *fn = g_build_filename (path, subpath, NULL); + g_autofree gchar *buf = NULL; + + /* not found */ + if (!g_file_get_contents (fn, &buf, &bufsz, NULL)) + return; + + /* add to strtab */ + g_ptr_array_add (item->strings, g_strndup (buf, bufsz)); + for (guint i = item->buf->len; i < (guint) offset + 1; i++) + fu_byte_array_append_uint8 (item->buf, 0x0); + item->buf->data[offset] = item->strings->len; +} + +static gboolean +fu_smbios_setup_from_path_dt (FuSmbios *self, const gchar *path, GError **error) +{ + /* add all four faked structures */ + for (guint i = 0; i < FU_SMBIOS_STRUCTURE_TYPE_LAST; i++) { + FuSmbiosItem *item = g_new0 (FuSmbiosItem, 1); + item->type = i; + item->buf = g_byte_array_new (); + item->strings = g_ptr_array_new_with_free_func (g_free); + g_ptr_array_add (self->items, item); + } + + /* DMI:Manufacturer */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x04, + path, "vendor"); + + /* DMI:Family */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x1a, + path, "model-name"); + + /* DMI:ProductName */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x05, + path, "model"); + + /* DMI:BiosVersion */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_BIOS, 0x05, + path, "ibm,firmware-versions/version"); + + /* DMI:BaseboardManufacturer */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, 0x04, + path, "vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor"); + + /* DMI:BaseboardProduct */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, 0x05, + path, "vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number"); + + return TRUE; +} + static gboolean fu_smbios_setup_from_data (FuSmbios *self, const guint8 *buf, gsize sz, GError **error) { @@ -93,8 +152,9 @@ fu_smbios_setup_from_data (FuSmbios *self, const guint8 *buf, gsize sz, GError * item = g_new0 (FuSmbiosItem, 1); item->type = str->type; item->handle = GUINT16_FROM_LE (str->handle); - item->data = g_bytes_new (buf + i, str->len); + item->buf = g_byte_array_sized_new (str->len); item->strings = g_ptr_array_new_with_free_func (g_free); + g_byte_array_append (item->buf, buf + i, str->len); g_ptr_array_add (self->items, item); /* jump to the end of the struct */ @@ -135,6 +195,17 @@ fu_smbios_setup_from_file (FuSmbios *self, const gchar *filename, GError **error { gsize sz = 0; g_autofree gchar *buf = NULL; + g_autofree gchar *basename = NULL; + + g_return_val_if_fail (FU_IS_SMBIOS (self), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + + /* use a heuristic */ + basename = g_path_get_basename (filename); + if (g_strcmp0 (basename, "base") == 0) + return fu_smbios_setup_from_path_dt (self, filename, error); + + /* DMI blob */ if (!g_file_get_contents (filename, &buf, &sz, error)) return FALSE; return fu_smbios_setup_from_data (self, (guint8 *) buf, sz, error); @@ -229,20 +300,8 @@ fu_smbios_parse_ep64 (FuSmbios *self, const gchar *buf, gsize sz, GError **error return TRUE; } -/** - * fu_smbios_setup_from_path: - * @self: A #FuSmbios - * @path: A path, e.g. `/sys/firmware/dmi/tables` - * @error: A #GError or %NULL - * - * Reads all the SMBIOS values from a specific path. - * - * Returns: %TRUE for success - * - * Since: 1.0.0 - **/ -gboolean -fu_smbios_setup_from_path (FuSmbios *self, const gchar *path, GError **error) +static gboolean +fu_smbios_setup_from_path_dmi (FuSmbios *self, const gchar *path, GError **error) { gsize sz = 0; g_autofree gchar *dmi_fn = NULL; @@ -304,6 +363,33 @@ fu_smbios_setup_from_path (FuSmbios *self, const gchar *path, GError **error) return fu_smbios_setup_from_data (self, (guint8 *) dmi_raw, sz, error); } +/** + * fu_smbios_setup_from_path: + * @self: A #FuSmbios + * @path: A path, e.g. `/sys/firmware/dmi/tables` + * @error: A #GError or %NULL + * + * Reads all the SMBIOS values from a specific path. + * + * Returns: %TRUE for success + * + * Since: 1.0.0 + **/ +gboolean +fu_smbios_setup_from_path (FuSmbios *self, const gchar *path, GError **error) +{ + g_autofree gchar *basename = NULL; + + g_return_val_if_fail (FU_IS_SMBIOS (self), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + /* use a heuristic */ + basename = g_path_get_basename (path); + if (g_strcmp0 (basename, "base") == 0) + return fu_smbios_setup_from_path_dt (self, path, error); + return fu_smbios_setup_from_path_dmi (self, path, error); +} + /** * fu_smbios_setup: * @self: A #FuSmbios @@ -319,11 +405,30 @@ gboolean fu_smbios_setup (FuSmbios *self, GError **error) { g_autofree gchar *path = NULL; + g_autofree gchar *path_dt = NULL; g_autofree gchar *sysfsfwdir = NULL; + g_return_val_if_fail (FU_IS_SMBIOS (self), FALSE); + sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + + /* DMI */ path = g_build_filename (sysfsfwdir, "dmi", "tables", NULL); - return fu_smbios_setup_from_path (self, path, error); + if (g_file_test (path, G_FILE_TEST_EXISTS)) + return fu_smbios_setup_from_path (self, path, error); + + /* DT */ + path_dt = g_build_filename (sysfsfwdir, "devicetree", "base", NULL); + if (g_file_test (path_dt, G_FILE_TEST_EXISTS)) + return fu_smbios_setup_from_path (self, path_dt, error); + + /* neither found */ + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "neither SMBIOS or DT found"); + return FALSE; + } /** @@ -348,8 +453,7 @@ fu_smbios_to_string (FuSmbios *self) for (guint i = 0; i < self->items->len; i++) { FuSmbiosItem *item = g_ptr_array_index (self->items, i); g_string_append_printf (str, "Type: %02x\n", item->type); - g_string_append_printf (str, " Length: %" G_GSIZE_FORMAT "\n", - g_bytes_get_size (item->data)); + g_string_append_printf (str, " Length: %u\n", item->buf->len); g_string_append_printf (str, " Handle: 0x%04x\n", item->handle); for (guint j = 0; j < item->strings->len; j++) { const gchar *tmp = g_ptr_array_index (item->strings, j); @@ -395,7 +499,54 @@ fu_smbios_get_data (FuSmbios *self, guint8 type, GError **error) "no structure with type %02x", type); return NULL; } - return g_bytes_ref (item->data); + return g_bytes_new (item->buf->data, item->buf->len); +} + +/** + * fu_smbios_get_integer: + * @self: A #FuSmbios + * @type: A structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS + * @offset: A structure offset + * @error: A #GError or %NULL + * + * Reads an integer value from the SMBIOS string table of a specific structure. + * + * The @type and @offset can be referenced from the DMTF SMBIOS specification: + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf + * + * Returns: an integer, or %G_MAXUINT if invalid or not found + * + * Since: 1.5.0 + **/ +guint +fu_smbios_get_integer (FuSmbios *self, guint8 type, guint8 offset, GError **error) +{ + FuSmbiosItem *item; + + g_return_val_if_fail (FU_IS_SMBIOS (self), 0); + + /* get item */ + item = fu_smbios_get_item_for_type (self, type); + if (item == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no structure with type %02x", type); + return G_MAXUINT; + } + + /* check offset valid */ + if (offset >= item->buf->len) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "offset bigger than size %u", + item->buf->len); + return G_MAXUINT; + } + + /* success */ + return item->buf->data[offset]; } /** @@ -418,8 +569,6 @@ const gchar * fu_smbios_get_string (FuSmbios *self, guint8 type, guint8 offset, GError **error) { FuSmbiosItem *item; - const guint8 *data; - gsize sz; g_return_val_if_fail (FU_IS_SMBIOS (self), NULL); @@ -434,15 +583,15 @@ fu_smbios_get_string (FuSmbios *self, guint8 type, guint8 offset, GError **error } /* check offset valid */ - data = g_bytes_get_data (item->data, &sz); - if (offset >= sz) { + if (offset >= item->buf->len) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "offset bigger than size %" G_GSIZE_FORMAT, sz); + "offset bigger than size %u", + item->buf->len); return NULL; } - if (data[offset] == 0x00) { + if (item->buf->data[offset] == 0x00) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, @@ -451,21 +600,21 @@ fu_smbios_get_string (FuSmbios *self, guint8 type, guint8 offset, GError **error } /* check string index valid */ - if (data[offset] > item->strings->len) { + if (item->buf->data[offset] > item->strings->len) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "index larger than string table %u", - data[offset]); + item->strings->len); return NULL; } - return g_ptr_array_index (item->strings, data[offset] - 1); + return g_ptr_array_index (item->strings, item->buf->data[offset] - 1); } static void fu_smbios_item_free (FuSmbiosItem *item) { - g_bytes_unref (item->data); + g_byte_array_unref (item->buf); g_ptr_array_unref (item->strings); g_free (item); } diff --git a/libfwupdplugin/fu-smbios.h b/libfwupdplugin/fu-smbios.h index e9ef02c74..87c0192b9 100644 --- a/libfwupdplugin/fu-smbios.h +++ b/libfwupdplugin/fu-smbios.h @@ -18,6 +18,47 @@ FuSmbios *fu_smbios_new (void); #define FU_SMBIOS_STRUCTURE_TYPE_SYSTEM 0x01 #define FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD 0x02 #define FU_SMBIOS_STRUCTURE_TYPE_CHASSIS 0x03 +#define FU_SMBIOS_STRUCTURE_TYPE_LAST 0x04 + +typedef enum { + FU_SMBIOS_CHASSIS_KIND_OTHER = 0x01, + FU_SMBIOS_CHASSIS_KIND_UNKNOWN = 0x02, + FU_SMBIOS_CHASSIS_KIND_DESKTOP = 0x03, + FU_SMBIOS_CHASSIS_KIND_LOW_PROFILE_DESKTOP = 0x04, + FU_SMBIOS_CHASSIS_KIND_PIZZA_BOX = 0x05, + FU_SMBIOS_CHASSIS_KIND_MINI_TOWER = 0x06, + FU_SMBIOS_CHASSIS_KIND_TOWER = 0x07, + FU_SMBIOS_CHASSIS_KIND_PORTABLE = 0x08, + FU_SMBIOS_CHASSIS_KIND_LAPTOP = 0x09, + FU_SMBIOS_CHASSIS_KIND_NOTEBOOK = 0x0A, + FU_SMBIOS_CHASSIS_KIND_HAND_HELD = 0x0B, + FU_SMBIOS_CHASSIS_KIND_DOCKING_STATION = 0x0C, + FU_SMBIOS_CHASSIS_KIND_ALL_IN_ONE = 0x0D, + FU_SMBIOS_CHASSIS_KIND_SUB_NOTEBOOK = 0x0E, + FU_SMBIOS_CHASSIS_KIND_SPACE_SAVING = 0x0F, + FU_SMBIOS_CHASSIS_KIND_LUNCH_BOX = 0x10, + FU_SMBIOS_CHASSIS_KIND_MAIN_SERVER = 0x11, + FU_SMBIOS_CHASSIS_KIND_EXPANSION = 0x12, + FU_SMBIOS_CHASSIS_KIND_SUBCHASSIS = 0x13, + FU_SMBIOS_CHASSIS_KIND_BUS_EXPANSION = 0x14, + FU_SMBIOS_CHASSIS_KIND_PERIPHERAL = 0x15, + FU_SMBIOS_CHASSIS_KIND_RAID = 0x16, + FU_SMBIOS_CHASSIS_KIND_RACK_MOUNT = 0x17, + FU_SMBIOS_CHASSIS_KIND_SEALED_CASE_PC = 0x18, + FU_SMBIOS_CHASSIS_KIND_MULTI_SYSTEM = 0x19, + FU_SMBIOS_CHASSIS_KIND_COMPACT_PCI = 0x1A, + FU_SMBIOS_CHASSIS_KIND_ADVANCED_TCA = 0x1B, + FU_SMBIOS_CHASSIS_KIND_BLADE = 0x1C, + FU_SMBIOS_CHASSIS_KIND_TABLET = 0x1E, + FU_SMBIOS_CHASSIS_KIND_CONVERTIBLE = 0x1F, + FU_SMBIOS_CHASSIS_KIND_DETACHABLE = 0x20, + FU_SMBIOS_CHASSIS_KIND_IOT_GATEWAY = 0x21, + FU_SMBIOS_CHASSIS_KIND_EMBEDDED_PC = 0x22, + FU_SMBIOS_CHASSIS_KIND_MINI_PC = 0x23, + FU_SMBIOS_CHASSIS_KIND_STICK_PC = 0x24, + /*< private >*/ + FU_SMBIOS_CHASSIS_KIND_LAST, +} FuSmbiosChassisKind; gchar *fu_smbios_to_string (FuSmbios *self); @@ -25,6 +66,10 @@ const gchar *fu_smbios_get_string (FuSmbios *self, guint8 type, guint8 offset, GError **error); +guint fu_smbios_get_integer (FuSmbios *self, + guint8 type, + guint8 offset, + GError **error); GBytes *fu_smbios_get_data (FuSmbios *self, guint8 type, GError **error); diff --git a/libfwupdplugin/fu-srec-firmware.c b/libfwupdplugin/fu-srec-firmware.c index 8a12810c9..d42dfcedf 100644 --- a/libfwupdplugin/fu-srec-firmware.c +++ b/libfwupdplugin/fu-srec-firmware.c @@ -142,7 +142,7 @@ fu_srec_firmware_tokenize (FuFirmware *firmware, GBytes *fw, } /* checksum check */ - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { guint8 rec_csum = 0; guint8 rec_csum_expected; for (guint8 i = 0; i < rec_count; i++) diff --git a/libfwupdplugin/fu-udev-device.c b/libfwupdplugin/fu-udev-device.c index fe1608c64..f79cc0d35 100644 --- a/libfwupdplugin/fu-udev-device.c +++ b/libfwupdplugin/fu-udev-device.c @@ -11,7 +11,7 @@ #include #include #ifdef HAVE_ERRNO_H -#include +#include #endif #ifdef HAVE_IOCTL_H #include @@ -39,6 +39,8 @@ typedef struct GUdevDevice *udev_device; guint32 vendor; guint32 model; + guint32 subsystem_vendor; + guint32 subsystem_model; guint8 revision; gchar *subsystem; gchar *device_file; @@ -76,8 +78,11 @@ static guint signals[SIGNAL_LAST] = { 0 }; void fu_udev_device_emit_changed (FuUdevDevice *self) { + g_autoptr(GError) error = NULL; g_return_if_fail (FU_IS_UDEV_DEVICE (self)); g_debug ("FuUdevDevice emit changed"); + if (!fu_device_rescan (FU_DEVICE (self), &error)) + g_debug ("%s", error->message); g_signal_emit (self, signals[SIGNAL_CHANGED], 0); } @@ -190,23 +195,37 @@ fu_udev_device_set_device_file (FuUdevDevice *self, const gchar *device_file) g_object_notify (G_OBJECT (self), "device-file"); } +#ifdef HAVE_GUDEV static const gchar * fu_udev_device_get_vendor_fallback (GUdevDevice *udev_device) { -#ifdef HAVE_GUDEV const gchar *tmp; - tmp = g_udev_device_get_property (udev_device, "FWUPD_VENDOR"); - if (tmp != NULL) - return tmp; tmp = g_udev_device_get_property (udev_device, "ID_VENDOR_FROM_DATABASE"); if (tmp != NULL) return tmp; tmp = g_udev_device_get_property (udev_device, "ID_VENDOR"); if (tmp != NULL) return tmp; -#endif return NULL; } +#endif + +#ifdef HAVE_GUDEV +static gboolean +fu_udev_device_probe_i2c_dev (FuUdevDevice *self, GError **error) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + const gchar *name = g_udev_device_get_sysfs_attr (priv->udev_device, "name"); + if (name != NULL) { + g_autofree gchar *devid = NULL; + g_autofree gchar *name_safe = g_strdup (name); + g_strdelimit (name_safe, " /\\\"", '-'); + devid = g_strdup_printf ("I2C\\NAME_%s", name_safe); + fu_device_add_instance_id (FU_DEVICE (self), devid); + } + return TRUE; +} +#endif static gboolean fu_udev_device_probe (FuDevice *device, GError **error) @@ -229,6 +248,8 @@ fu_udev_device_probe (FuDevice *device, GError **error) priv->vendor = fu_udev_device_get_sysfs_attr_as_uint32 (priv->udev_device, "vendor"); priv->model = fu_udev_device_get_sysfs_attr_as_uint32 (priv->udev_device, "device"); priv->revision = fu_udev_device_get_sysfs_attr_as_uint8 (priv->udev_device, "revision"); + priv->subsystem_vendor = fu_udev_device_get_sysfs_attr_as_uint32 (priv->udev_device, "subsystem_vendor"); + priv->subsystem_model = fu_udev_device_get_sysfs_attr_as_uint32 (priv->udev_device, "subsystem_device"); #ifdef HAVE_GUDEV /* fallback to the parent */ @@ -239,6 +260,8 @@ fu_udev_device_probe (FuDevice *device, GError **error) priv->vendor = fu_udev_device_get_sysfs_attr_as_uint32 (udev_parent, "vendor"); priv->model = fu_udev_device_get_sysfs_attr_as_uint32 (udev_parent, "device"); priv->revision = fu_udev_device_get_sysfs_attr_as_uint8 (udev_parent, "revision"); + priv->subsystem_vendor = fu_udev_device_get_sysfs_attr_as_uint32 (udev_parent, "subsystem_vendor"); + priv->subsystem_model = fu_udev_device_get_sysfs_attr_as_uint32 (udev_parent, "subsystem_device"); } /* hidraw helpfully encodes the information in a different place */ @@ -286,9 +309,7 @@ fu_udev_device_probe (FuDevice *device, GError **error) /* set model */ if (fu_device_get_name (device) == NULL) { - tmp = g_udev_device_get_property (priv->udev_device, "FWUPD_MODEL"); - if (tmp == NULL) - tmp = g_udev_device_get_property (priv->udev_device, "ID_MODEL_FROM_DATABASE"); + tmp = g_udev_device_get_property (priv->udev_device, "ID_MODEL_FROM_DATABASE"); if (tmp == NULL) tmp = g_udev_device_get_property (priv->udev_device, "ID_MODEL"); if (tmp == NULL) @@ -348,6 +369,22 @@ fu_udev_device_probe (FuDevice *device, GError **error) } /* add GUIDs in order of priority */ + if (priv->vendor != 0x0000 && priv->model != 0x0000 && + priv->subsystem_vendor != 0x0000 && priv->subsystem_model != 0x0000) { + g_autofree gchar *devid1 = NULL; + g_autofree gchar *devid2 = NULL; + devid1 = g_strdup_printf ("%s\\VEN_%04X&DEV_%04X&SUBSYS_%04X%04X&REV_%02X", + subsystem, + priv->vendor, priv->model, + priv->subsystem_vendor, priv->subsystem_model, + priv->revision); + fu_device_add_instance_id (device, devid1); + devid2 = g_strdup_printf ("%s\\VEN_%04X&DEV_%04X&SUBSYS_%04X%04X", + subsystem, + priv->vendor, priv->model, + priv->subsystem_vendor, priv->subsystem_model); + fu_device_add_instance_id (device, devid2); + } if (priv->vendor != 0x0000 && priv->model != 0x0000) { g_autofree gchar *devid = NULL; devid = g_strdup_printf ("%s\\VEN_%04X&DEV_%04X&REV_%02X", @@ -368,12 +405,42 @@ fu_udev_device_probe (FuDevice *device, GError **error) FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); } + /* add device class */ + tmp = g_udev_device_get_sysfs_attr (priv->udev_device, "class"); + if (tmp != NULL && g_str_has_prefix (tmp, "0x")) { + g_autofree gchar *class_id = g_utf8_strup (tmp + 2, -1); + g_autofree gchar *devid = NULL; + devid = g_strdup_printf ("%s\\VEN_%04X&CLASS_%s", + subsystem, + priv->vendor, + class_id); + fu_device_add_instance_id_full (device, devid, + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + } + + /* add the driver */ + tmp = g_udev_device_get_driver (priv->udev_device); + if (tmp != NULL) { + g_autofree gchar *devid = NULL; + devid = g_strdup_printf ("%s\\DRIVER_%s", + subsystem, + tmp); + fu_device_add_instance_id_full (device, devid, + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + } + /* add subsystem to match in plugins */ if (subsystem != NULL) { fu_device_add_instance_id_full (device, subsystem, FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); } + /* i2c devices all expose a name */ + if (g_strcmp0 (g_udev_device_get_subsystem (priv->udev_device), "i2c-dev") == 0) { + if (!fu_udev_device_probe_i2c_dev (self, error)) + return FALSE; + } + /* determine if we're wired internally */ parent_i2c = g_udev_device_get_parent_with_subsystem (priv->udev_device, "i2c", NULL); @@ -397,13 +464,28 @@ fu_udev_device_set_dev (FuUdevDevice *self, GUdevDevice *udev_device) FuUdevDevicePrivate *priv = GET_PRIVATE (self); #ifdef HAVE_GUDEV const gchar *summary; - g_autoptr(GUdevDevice) parent = NULL; #endif g_return_if_fail (FU_IS_UDEV_DEVICE (self)); - /* set new device */ +#ifdef HAVE_GUDEV + /* the net subsystem is not a real hardware class */ + if (udev_device != NULL && + g_strcmp0 (g_udev_device_get_subsystem (udev_device), "net") == 0) { + g_autoptr(GUdevDevice) udev_device_phys = NULL; + udev_device_phys = g_udev_device_get_parent (udev_device); + g_set_object (&priv->udev_device, udev_device_phys); + fu_device_set_metadata (FU_DEVICE (self), + "ParentSubsystem", + g_udev_device_get_subsystem (udev_device)); + } else { + g_set_object (&priv->udev_device, udev_device); + } +#else g_set_object (&priv->udev_device, udev_device); +#endif + + /* set new device */ if (priv->udev_device == NULL) return; #ifdef HAVE_GUDEV @@ -413,6 +495,7 @@ fu_udev_device_set_dev (FuUdevDevice *self, GUdevDevice *udev_device) /* try to get one line summary */ summary = g_udev_device_get_sysfs_attr (priv->udev_device, "description"); if (summary == NULL) { + g_autoptr(GUdevDevice) parent = NULL; parent = g_udev_device_get_parent (priv->udev_device); if (parent != NULL) summary = g_udev_device_get_sysfs_attr (parent, "description"); @@ -453,6 +536,120 @@ fu_udev_device_get_slot_depth (FuUdevDevice *self, const gchar *subsystem) return 0; } +#ifndef _WIN32 +static gchar * +fu_udev_device_get_bind_id (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + if (g_strcmp0 (fu_udev_device_get_subsystem (self), "pci") == 0) + return g_strdup (g_udev_device_get_property (priv->udev_device, "PCI_SLOT_NAME")); + if (g_strcmp0 (fu_udev_device_get_subsystem (self), "hid") == 0) + return g_strdup (g_udev_device_get_property (priv->udev_device, "HID_PHYS")); + if (g_strcmp0 (fu_udev_device_get_subsystem (self), "usb") == 0) + return g_path_get_basename (g_udev_device_get_sysfs_path (priv->udev_device)); + return NULL; +} +#endif + +static gboolean +fu_udev_device_unbind_driver (FuDevice *device, GError **error) +{ +#ifndef _WIN32 + FuUdevDevice *self = FU_UDEV_DEVICE (device); + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_autofree gchar *bind_id = NULL; + g_autofree gchar *fn = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GOutputStream) stream = NULL; + + /* is already unbound */ + fn = g_build_filename (g_udev_device_get_sysfs_path (priv->udev_device), + "driver", "unbind", NULL); + if (!g_file_test (fn, G_FILE_TEST_EXISTS)) + return TRUE; + + /* write bus ID to file */ + bind_id = fu_udev_device_get_bind_id (self); + if (bind_id == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "bind-id not set for subsystem %s", + priv->subsystem); + return FALSE; + } + file = g_file_new_for_path (fn); + stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, + G_FILE_CREATE_NONE, NULL, error)); + if (stream == NULL) + return FALSE; + return g_output_stream_write_all (stream, bind_id, strlen (bind_id), + NULL, NULL, error); +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "driver unbinding not supported on Windows"); + return FALSE; +#endif +} + +static gboolean +fu_udev_device_bind_driver (FuDevice *device, + const gchar *subsystem, + const gchar *driver, + GError **error) +{ +#ifndef _WIN32 + FuUdevDevice *self = FU_UDEV_DEVICE (device); + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_autofree gchar *bind_id = NULL; + g_autofree gchar *driver_safe = g_strdup (driver); + g_autofree gchar *fn = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GOutputStream) stream = NULL; + + /* copy the logic from modprobe */ + g_strdelimit (driver_safe, "-", '_'); + + /* driver exists */ + fn = g_strdup_printf ("/sys/module/%s/drivers/%s:%s/bind", + driver_safe, subsystem, driver_safe); + if (!g_file_test (fn, G_FILE_TEST_EXISTS)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot bind with %s:%s", + subsystem, driver); + return FALSE; + } + + /* write bus ID to file */ + bind_id = fu_udev_device_get_bind_id (self); + if (bind_id == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "bind-id not set for subsystem %s", + priv->subsystem); + return FALSE; + } + file = g_file_new_for_path (fn); + stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, + G_FILE_CREATE_NONE, NULL, error)); + if (stream == NULL) + return FALSE; + return g_output_stream_write_all (stream, bind_id, strlen (bind_id), + NULL, NULL, error); +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "driver binding not supported on Windows"); + return FALSE; +#endif +} + static void fu_udev_device_incorporate (FuDevice *self, FuDevice *donor) { @@ -546,6 +743,29 @@ fu_udev_device_get_sysfs_path (FuUdevDevice *self) return NULL; } +/** + * fu_udev_device_get_number: + * @self: A #FuUdevDevice + * + * Gets the device number, if any. + * + * Returns: integer, 0 if the data is unavailable, or %G_MAXUINT64 if the + * feature is not available + * + * Since: 1.5.0 + **/ +guint64 +fu_udev_device_get_number (FuUdevDevice *self) +{ +#ifdef HAVE_GUDEV + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), 0); + if (priv->udev_device != NULL) + return fu_common_strtoull (g_udev_device_get_number (priv->udev_device)); +#endif + return G_MAXUINT64; +} + /** * fu_udev_device_get_vendor: * @self: A #FuUdevDevice @@ -582,6 +802,42 @@ fu_udev_device_get_model (FuUdevDevice *self) return priv->model; } +/** + * fu_udev_device_get_subsystem_vendor: + * @self: A #FuUdevDevice + * + * Gets the device subsystem vendor code. + * + * Returns: a vendor code, or 0 if unset or invalid + * + * Since: 1.5.0 + **/ +guint32 +fu_udev_device_get_subsystem_vendor (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), 0x0000); + return priv->subsystem_vendor; +} + +/** + * fu_udev_device_get_subsystem_model: + * @self: A #FuUdevDevice + * + * Gets the device subsystem model code. + * + * Returns: a vendor code, or 0 if unset or invalid + * + * Since: 1.5.0 + **/ +guint32 +fu_udev_device_get_subsystem_model (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), 0x0000); + return priv->subsystem_model; +} + /** * fu_udev_device_get_revision: * @self: A #FuUdevDevice @@ -824,6 +1080,15 @@ fu_udev_device_set_flags (FuUdevDevice *self, FuUdevDeviceFlags flags) FuUdevDevicePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_UDEV_DEVICE (self)); priv->flags = flags; + +#ifdef HAVE_GUDEV + /* overwrite */ + if (flags & FU_UDEV_DEVICE_FLAG_USE_CONFIG) { + g_free (priv->device_file); + priv->device_file = g_build_filename (g_udev_device_get_sysfs_path (priv->udev_device), + "config", NULL); + } +#endif } static gboolean @@ -844,6 +1109,10 @@ fu_udev_device_open (FuDevice *device, GError **error) } else { flags = O_RDONLY; } +#ifdef O_NONBLOCK + if (priv->flags & FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK) + flags |= O_NONBLOCK; +#endif priv->fd = g_open (priv->device_file, flags, 0); if (priv->fd < 0) { g_set_error (error, @@ -970,7 +1239,6 @@ fu_udev_device_pread_full (FuUdevDevice *self, goffset port, FuUdevDevicePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), FALSE); - g_return_val_if_fail (port != 0x0, FALSE); g_return_val_if_fail (buf != NULL, FALSE); g_return_val_if_fail (priv->fd > 0, FALSE); @@ -1016,7 +1284,6 @@ fu_udev_device_pwrite_full (FuUdevDevice *self, goffset port, FuUdevDevicePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), FALSE); - g_return_val_if_fail (port != 0x0, FALSE); g_return_val_if_fail (priv->fd > 0, FALSE); #ifdef HAVE_PWRITE @@ -1331,6 +1598,8 @@ fu_udev_device_class_init (FuUdevDeviceClass *klass) device_class->open = fu_udev_device_open; device_class->close = fu_udev_device_close; device_class->to_string = fu_udev_device_to_string; + device_class->bind_driver = fu_udev_device_bind_driver; + device_class->unbind_driver = fu_udev_device_unbind_driver; signals[SIGNAL_CHANGED] = g_signal_new ("changed", diff --git a/libfwupdplugin/fu-udev-device.h b/libfwupdplugin/fu-udev-device.h index d77e7c40e..f5698e1cc 100644 --- a/libfwupdplugin/fu-udev-device.h +++ b/libfwupdplugin/fu-udev-device.h @@ -41,6 +41,7 @@ struct _FuUdevDeviceClass * @FU_UDEV_DEVICE_FLAG_OPEN_READ: Open the device read-only * @FU_UDEV_DEVICE_FLAG_OPEN_WRITE: Open the device write-only * @FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT: Get the vendor ID fallback from the parent + * @FU_UDEV_DEVICE_FLAG_USE_CONFIG: Read and write from the device config * * Flags used when opening the device using fu_device_open(). **/ @@ -49,6 +50,8 @@ typedef enum { FU_UDEV_DEVICE_FLAG_OPEN_READ = 1 << 0, FU_UDEV_DEVICE_FLAG_OPEN_WRITE = 1 << 1, FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT = 1 << 2, + FU_UDEV_DEVICE_FLAG_USE_CONFIG = 1 << 3, + FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK = 1 << 4, /*< private >*/ FU_UDEV_DEVICE_FLAG_LAST } FuUdevDeviceFlags; @@ -60,7 +63,10 @@ const gchar *fu_udev_device_get_sysfs_path (FuUdevDevice *self); const gchar *fu_udev_device_get_subsystem (FuUdevDevice *self); guint32 fu_udev_device_get_vendor (FuUdevDevice *self); guint32 fu_udev_device_get_model (FuUdevDevice *self); +guint32 fu_udev_device_get_subsystem_vendor (FuUdevDevice *self); +guint32 fu_udev_device_get_subsystem_model (FuUdevDevice *self); guint8 fu_udev_device_get_revision (FuUdevDevice *self); +guint64 fu_udev_device_get_number (FuUdevDevice *self); guint fu_udev_device_get_slot_depth (FuUdevDevice *self, const gchar *subsystem); gboolean fu_udev_device_set_physical_id (FuUdevDevice *self, diff --git a/libfwupdplugin/fu-usb-device.c b/libfwupdplugin/fu-usb-device.c index 0337652b1..fc6f6ef31 100644 --- a/libfwupdplugin/fu-usb-device.c +++ b/libfwupdplugin/fu-usb-device.c @@ -570,6 +570,40 @@ fu_usb_device_incorporate (FuDevice *self, FuDevice *donor) fu_usb_device_get_dev (FU_USB_DEVICE (donor))); } +static gboolean +fu_udev_device_bind_driver (FuDevice *device, + const gchar *subsystem, + const gchar *driver, + GError **error) +{ + FuUsbDevice *self = FU_USB_DEVICE (device); + g_autoptr(GUdevDevice) dev = NULL; + g_autoptr(FuUdevDevice) udev_device = NULL; + + /* use udev for this */ + dev = fu_usb_device_find_udev_device (self, error); + if (dev == NULL) + return FALSE; + udev_device = fu_udev_device_new (dev); + return fu_device_bind_driver (FU_DEVICE (udev_device), + subsystem, driver, error); +} + +static gboolean +fu_udev_device_unbind_driver (FuDevice *device, GError **error) +{ + FuUsbDevice *self = FU_USB_DEVICE (device); + g_autoptr(GUdevDevice) dev = NULL; + g_autoptr(FuUdevDevice) udev_device = NULL; + + /* use udev for this */ + dev = fu_usb_device_find_udev_device (self, error); + if (dev == NULL) + return FALSE; + udev_device = fu_udev_device_new (dev); + return fu_device_unbind_driver (FU_DEVICE (udev_device), error); +} + /** * fu_usb_device_new: * @usb_device: A #GUsbDevice @@ -602,6 +636,8 @@ fu_usb_device_class_init (FuUsbDeviceClass *klass) device_class->close = fu_usb_device_close; device_class->probe = fu_usb_device_probe; device_class->incorporate = fu_usb_device_incorporate; + device_class->bind_driver = fu_udev_device_bind_driver; + device_class->unbind_driver = fu_udev_device_unbind_driver; pspec = g_param_spec_object ("usb-device", NULL, NULL, G_USB_TYPE_DEVICE, diff --git a/libfwupdplugin/fu-volume-private.h b/libfwupdplugin/fu-volume-private.h index 88d6f6699..d1c8f99c3 100644 --- a/libfwupdplugin/fu-volume-private.h +++ b/libfwupdplugin/fu-volume-private.h @@ -7,9 +7,8 @@ #pragma once -#include +#include #include "fu-volume.h" -FuVolume *fu_volume_new_from_proxy (GDBusProxy *proxy); FuVolume *fu_volume_new_from_mount_path (const gchar *mount_path); diff --git a/libfwupdplugin/fu-volume.c b/libfwupdplugin/fu-volume.c index 889173186..55ef98762 100644 --- a/libfwupdplugin/fu-volume.c +++ b/libfwupdplugin/fu-volume.c @@ -23,10 +23,19 @@ struct _FuVolume { GObject parent_instance; - GDBusProxy *proxy; + GDBusProxy *proxy_blk; + GDBusProxy *proxy_fs; gchar *mount_path; /* only when mounted ourselves */ }; +enum { + PROP_0, + PROP_MOUNT_PATH, + PROP_PROXY_BLOCK, + PROP_PROXY_FILESYSTEM, + PROP_LAST +}; + G_DEFINE_TYPE (FuVolume, fu_volume, G_TYPE_OBJECT) static void @@ -34,16 +43,82 @@ fu_volume_finalize (GObject *obj) { FuVolume *self = FU_VOLUME (obj); g_free (self->mount_path); - if (self->proxy != NULL) - g_object_unref (self->proxy); + if (self->proxy_blk != NULL) + g_object_unref (self->proxy_blk); + if (self->proxy_fs != NULL) + g_object_unref (self->proxy_fs); G_OBJECT_CLASS (fu_volume_parent_class)->finalize (obj); } +static void +fu_volume_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + FuVolume *self = FU_VOLUME (object); + switch (prop_id) { + case PROP_MOUNT_PATH: + g_value_set_string (value, self->mount_path); + break; + case PROP_PROXY_BLOCK: + g_value_set_object (value, self->proxy_blk); + break; + case PROP_PROXY_FILESYSTEM: + g_value_set_object (value, self->proxy_fs); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fu_volume_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + FuVolume *self = FU_VOLUME (object); + switch (prop_id) { + case PROP_MOUNT_PATH: + self->mount_path = g_value_dup_string (value); + break; + case PROP_PROXY_BLOCK: + self->proxy_blk = g_value_dup_object (value); + break; + case PROP_PROXY_FILESYSTEM: + self->proxy_fs = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void fu_volume_class_init (FuVolumeClass *klass) { + GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_volume_finalize; + object_class->get_property = fu_volume_get_property; + object_class->set_property = fu_volume_set_property; + + pspec = g_param_spec_object ("proxy-block", NULL, NULL, G_TYPE_DBUS_PROXY, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_PROXY_BLOCK, pspec); + + pspec = g_param_spec_object ("proxy-filesystem", NULL, NULL, G_TYPE_DBUS_PROXY, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_PROXY_FILESYSTEM, pspec); + + pspec = g_param_spec_string ("mount-path", NULL, NULL, NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_MOUNT_PATH, pspec); } static void @@ -65,7 +140,11 @@ const gchar * fu_volume_get_id (FuVolume *self) { g_return_val_if_fail (FU_IS_VOLUME (self), NULL); - return g_dbus_proxy_get_object_path (self->proxy); + if (self->proxy_fs != NULL) + return g_dbus_proxy_get_object_path (self->proxy_fs); + if (self->proxy_blk != NULL) + return g_dbus_proxy_get_object_path (self->proxy_blk); + return NULL; } /** @@ -81,9 +160,8 @@ fu_volume_get_id (FuVolume *self) gchar * fu_volume_get_mount_point (FuVolume *self) { - const gchar **mountpoints = NULL; + g_autofree const gchar **mountpoints = NULL; g_autoptr(GVariant) val = NULL; - g_autoptr(GError) error_local = NULL; g_return_val_if_fail (FU_IS_VOLUME (self), NULL); @@ -92,7 +170,9 @@ fu_volume_get_mount_point (FuVolume *self) return g_strdup (self->mount_path); /* something else mounted it */ - val = g_dbus_proxy_get_cached_property (self->proxy, "MountPoints"); + if (self->proxy_fs == NULL) + return NULL; + val = g_dbus_proxy_get_cached_property (self->proxy_fs, "MountPoints"); if (val == NULL) return NULL; mountpoints = g_variant_get_bytestring_array (val, NULL); @@ -165,6 +245,33 @@ fu_volume_is_mounted (FuVolume *self) return mount_point != NULL; } +/** + * fu_volume_is_encrypted: + * @self: a @FuVolume + * + * Checks if the VOLUME is currently encrypted. + * + * Returns: %TRUE for success + * + * Since: 1.5.1 + **/ +gboolean +fu_volume_is_encrypted (FuVolume *self) +{ + g_autoptr(GVariant) val = NULL; + + g_return_val_if_fail (FU_IS_VOLUME (self), FALSE); + + if (self->proxy_blk == NULL) + return FALSE; + val = g_dbus_proxy_get_cached_property (self->proxy_blk, "CryptoBackingDevice"); + if (val == NULL) + return FALSE; + if (g_strcmp0 (g_variant_get_string (val, NULL), "/") == 0) + return FALSE; + return TRUE; +} + /** * fu_volume_mount: * @self: a @FuVolume @@ -185,12 +292,12 @@ fu_volume_mount (FuVolume *self, GError **error) g_return_val_if_fail (FU_IS_VOLUME (self), FALSE); /* device from the self tests */ - if (self->proxy == NULL) + if (self->proxy_fs == NULL) return TRUE; g_debug ("mounting %s", fu_volume_get_id (self)); g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); - val = g_dbus_proxy_call_sync (self->proxy, + val = g_dbus_proxy_call_sync (self->proxy_fs, "Mount", g_variant_new ("(a{sv})", &builder), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); @@ -220,12 +327,12 @@ fu_volume_unmount (FuVolume *self, GError **error) g_return_val_if_fail (FU_IS_VOLUME (self), FALSE); /* device from the self tests */ - if (self->proxy == NULL) + if (self->proxy_fs == NULL) return TRUE; g_debug ("unmounting %s", fu_volume_get_id (self)); g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); - val = g_dbus_proxy_call_sync (self->proxy, + val = g_dbus_proxy_call_sync (self->proxy_fs, "Unmount", g_variant_new ("(a{sv})", &builder), G_DBUS_CALL_FLAGS_NONE, @@ -261,16 +368,6 @@ fu_volume_locker (FuVolume *self, GError **error) error); } -/* private */ -FuVolume * -fu_volume_new_from_proxy (GDBusProxy *proxy) -{ - g_autoptr(FuVolume) self = g_object_new (FU_TYPE_VOLUME, NULL); - g_return_val_if_fail (proxy != NULL, NULL); - g_set_object (&self->proxy, proxy); - return g_steal_pointer (&self); -} - /* private */ FuVolume * fu_volume_new_from_mount_path (const gchar *mount_path) diff --git a/libfwupdplugin/fu-volume.h b/libfwupdplugin/fu-volume.h index 5d1340799..0f12edf9c 100644 --- a/libfwupdplugin/fu-volume.h +++ b/libfwupdplugin/fu-volume.h @@ -22,6 +22,7 @@ gboolean fu_volume_check_free_space (FuVolume *self, guint64 required, GError **error); gboolean fu_volume_is_mounted (FuVolume *self); +gboolean fu_volume_is_encrypted (FuVolume *self); gchar *fu_volume_get_mount_point (FuVolume *self); gboolean fu_volume_mount (FuVolume *self, GError **error); diff --git a/libfwupdplugin/fwupdplugin.h b/libfwupdplugin/fwupdplugin.h index f9be82b6f..41be7e17c 100644 --- a/libfwupdplugin/fwupdplugin.h +++ b/libfwupdplugin/fwupdplugin.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index f827ec4c7..a58bcc015 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -609,3 +609,73 @@ LIBFWUPDPLUGIN_1.4.6 { fu_volume_unmount; local: *; } LIBFWUPDPLUGIN_1.4.5; + +LIBFWUPDPLUGIN_1.4.7 { + global: + fu_efivar_get_names; + local: *; +} LIBFWUPDPLUGIN_1.4.6; + +LIBFWUPDPLUGIN_1.5.0 { + global: + fu_byte_array_set_size; + fu_common_cpuid; + fu_common_crc16; + fu_common_crc32; + fu_common_crc32_full; + fu_common_crc8; + fu_common_filename_glob; + fu_common_is_cpu_intel; + fu_device_bind_driver; + fu_device_dump_firmware; + fu_device_report_metadata_post; + fu_device_report_metadata_pre; + fu_device_sleep_with_progress; + fu_device_unbind_driver; + fu_efivar_get_data_bytes; + fu_efivar_secure_boot_enabled_full; + fu_efivar_set_data_bytes; + fu_firmware_add_flag; + fu_firmware_build; + fu_firmware_flag_from_string; + fu_firmware_flag_to_string; + fu_firmware_has_flag; + fu_firmware_image_build; + fu_firmware_image_get_bytes; + fu_firmware_image_get_filename; + fu_firmware_image_get_offset; + fu_firmware_image_parse; + fu_firmware_image_set_filename; + fu_firmware_image_set_offset; + fu_firmware_remove_image; + fu_firmware_remove_image_by_id; + fu_firmware_remove_image_by_idx; + fu_fmap_firmware_get_type; + fu_fmap_firmware_new; + fu_plugin_runner_add_security_attrs; + fu_plugin_runner_device_added; + fu_plugin_security_changed; + fu_security_attrs_append; + fu_security_attrs_calculate_hsi; + fu_security_attrs_depsolve; + fu_security_attrs_get_all; + fu_security_attrs_get_type; + fu_security_attrs_new; + fu_security_attrs_remove_all; + fu_security_attrs_to_variant; + fu_smbios_get_integer; + fu_udev_device_get_number; + fu_udev_device_get_subsystem_model; + fu_udev_device_get_subsystem_vendor; + local: *; +} LIBFWUPDPLUGIN_1.4.7; + +LIBFWUPDPLUGIN_1.5.1 { + global: + fu_common_get_volume_by_device; + fu_common_get_volume_by_devnum; + fu_device_add_possible_plugin; + fu_efivar_space_used; + fu_volume_is_encrypted; + local: *; +} LIBFWUPDPLUGIN_1.5.0; diff --git a/libfwupdplugin/meson.build b/libfwupdplugin/meson.build index 09e7e07f7..ba8e6bc4d 100644 --- a/libfwupdplugin/meson.build +++ b/libfwupdplugin/meson.build @@ -13,11 +13,13 @@ fwupdplugin_src = [ 'fu-firmware.c', 'fu-firmware-common.c', 'fu-firmware-image.c', + 'fu-fmap-firmware.c', 'fu-hwids.c', 'fu-ihex-firmware.c', 'fu-io-channel.c', 'fu-plugin.c', 'fu-quirks.c', + 'fu-security-attrs.c', 'fu-smbios.c', 'fu-srec-firmware.c', 'fu-efivar.c', @@ -42,11 +44,13 @@ fwupdplugin_headers = [ 'fu-firmware.h', 'fu-firmware-common.h', 'fu-firmware-image.h', + 'fu-fmap-firmware.h', 'fu-hwids.h', 'fu-ihex-firmware.h', 'fu-io-channel.h', 'fu-plugin.h', 'fu-quirks.h', + 'fu-security-attrs.h', 'fu-smbios.h', 'fu-srec-firmware.h', 'fu-efivar.h', @@ -75,6 +79,7 @@ fwupdplugin_headers_private = [ fu_hash, 'fu-device-private.h', 'fu-plugin-private.h', + 'fu-security-attrs-private.h', 'fu-smbios-private.h', 'fu-usb-device-private.h', ] diff --git a/meson.build b/meson.build index 8b2206f60..1996f1cb4 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('fwupd', 'c', - version : '1.4.6', + version : '1.5.1', license : 'LGPL-2.1+', meson_version : '>=0.47.0', default_options : ['warning_level=2', 'c_std=c99'], @@ -124,6 +124,7 @@ test_link_args = [ '-Wl,-z,relro', '-Wl,-z,defs', '-Wl,-z,now', + '-Wl,-z,ibt,-z,shstk', ] foreach arg: test_link_args if cc.has_link_argument(arg) @@ -170,10 +171,14 @@ if host_machine.system() == 'windows' sysconfdir = get_option('sysconfdir') localstatedir = get_option('localstatedir') datadir = get_option('datadir') + installed_test_bindir = get_option('libexecdir') + installed_test_datadir = get_option('datadir') else datadir = join_paths(prefix, get_option('datadir')) sysconfdir = join_paths(prefix, get_option('sysconfdir')) localstatedir = join_paths(prefix, get_option('localstatedir')) + installed_test_bindir = join_paths(libexecdir, 'installed-tests', meson.project_name()) + installed_test_datadir = join_paths(datadir, 'installed-tests', meson.project_name()) endif mandir = join_paths(prefix, get_option('mandir')) localedir = join_paths(prefix, get_option('localedir')) @@ -196,7 +201,7 @@ else gudev = dependency('', required : false) endif libxmlb = dependency('xmlb', version : '>= 0.1.13', fallback : ['libxmlb', 'libxmlb_dep']) -gusb = dependency('gusb', version : '>= 0.2.9', fallback : ['gusb', 'gusb_dep']) +gusb = dependency('gusb', version : '>= 0.3.5', fallback : ['gusb', 'gusb_dep']) sqlite = dependency('sqlite3') libarchive = dependency('libarchive') endif @@ -205,11 +210,16 @@ libjsonglib = dependency('json-glib-1.0', version : '>= 1.1.1') valgrind = dependency('valgrind', required: false) soup = dependency('libsoup-2.4', version : '>= 2.51.92') if build_daemon - polkit = dependency('polkit-gobject-1', version : '>= 0.103') - if polkit.version().version_compare('>= 0.114') - conf.set('HAVE_POLKIT_0_114', '1') - endif + if get_option('polkit') + polkit = dependency('polkit-gobject-1', version : '>= 0.103') + conf.set('HAVE_POLKIT', '1') + if polkit.version().version_compare('>= 0.114') + conf.set('HAVE_POLKIT_0_114', '1') + endif conf.set_quoted ('POLKIT_ACTIONDIR', polkit.get_pkgconfig_variable('actiondir')) + else + warning('Polkit is disabled, the daemon will allow ALL client actions') + endif udevdir = get_option('udevdir') if udevdir == '' udev = dependency('udev') @@ -221,10 +231,6 @@ libgcab = dependency('libgcab-1.0', version : '>= 1.0', fallback : ['gcab', 'gca gcab = find_program('gcab', required : true) bashcomp = dependency('bash-completion', required: false) python3 = find_program('python3') -tpm2tss = dependency('tss2-esys', version : '>= 2.0', required: false) -if tpm2tss.found() - conf.set('HAVE_TSS2', '1') -endif platform_deps = [] if get_option('default_library') != 'static' @@ -237,35 +243,52 @@ if valgrind.found() conf.set('HAVE_VALGRIND', '1') endif -if build_standalone and get_option('plugin_redfish') - efivar = dependency('efivar') -endif - if build_standalone and get_option('plugin_altos') libelf = dependency('libelf') endif +host_cpu = host_machine.cpu_family() + if cc.has_header('sys/utsname.h') conf.set('HAVE_UTSNAME_H', '1') endif if cc.has_header('sys/ioctl.h') conf.set('HAVE_IOCTL_H', '1') endif -if cc.has_header('sys/errno.h') +if cc.has_header('errno.h') conf.set('HAVE_ERRNO_H', '1') endif +if cc.has_header('sys/socket.h') + conf.set('HAVE_SOCKET_H', '1') +endif +if cc.has_header('linux/ethtool.h') + conf.set('HAVE_ETHTOOL_H', '1') +endif +if cc.has_header('sys/mman.h') + conf.set('HAVE_MMAN_H', '1') +endif if cc.has_header('poll.h') conf.set('HAVE_POLL_H', '1') endif if cc.has_header('fnmatch.h') conf.set('HAVE_FNMATCH_H', '1') endif +if cc.has_header('cpuid.h') and (host_cpu == 'x86' or host_cpu == 'x86_64') + conf.set('HAVE_CPUID_H', '1') +else + if get_option('plugin_msr') + error('cpuid.h is required for -Dplugin_msr=true') + endif +endif if cc.has_function('getuid') conf.set('HAVE_GETUID', '1') endif if cc.has_function('realpath') conf.set('HAVE_REALPATH', '1') endif +if cc.has_function('sigaction') + conf.set('HAVE_SIGACTION', '1') +endif if cc.has_header_symbol('locale.h', 'LC_MESSAGES') conf.set('HAVE_LC_MESSAGES', '1') endif @@ -273,16 +296,17 @@ if cc.has_function('pwrite', args : '-D_XOPEN_SOURCE') conf.set('HAVE_PWRITE', '1') endif -if build_standalone and get_option('plugin_tpm') and not tpm2tss.found() - error('tss2-esys is required for -Dplugin_tpm=true') +if build_standalone and get_option('tpm') + tpm2tss = dependency('tss2-esys', version : '>= 2.0') + conf.set('HAVE_TSS2', '1') +else + tpm2tss = dependency('', required: false) endif if build_standalone and get_option('plugin_uefi') cairo = dependency('cairo') fontconfig = cc.find_library('fontconfig') freetype = cc.find_library('freetype') - efivar = dependency('efivar', version : '>= 33') - conf.set_quoted('EFIVAR_LIBRARY_VERSION', efivar.version()) efiboot = dependency('efiboot') objcopy = find_program ('objcopy') readelf = find_program ('readelf') @@ -291,17 +315,18 @@ if build_standalone and get_option('plugin_uefi') efi_app_location = join_paths(libexecdir, 'fwupd', 'efi') conf.set_quoted ('EFI_APP_LOCATION', efi_app_location) - efi_arch = host_machine.cpu_family() - if efi_arch == 'x86' + conf.set('EFI_OBJCOPY', get_option('efi-objcopy')) + + if host_cpu == 'x86' EFI_MACHINE_TYPE_NAME = 'ia32' gnu_efi_arch = 'ia32' - elif efi_arch == 'x86_64' + elif host_cpu == 'x86_64' EFI_MACHINE_TYPE_NAME = 'x64' gnu_efi_arch = 'x86_64' - elif efi_arch == 'arm' + elif host_cpu == 'arm' EFI_MACHINE_TYPE_NAME = 'arm' gnu_efi_arch = 'arm' - elif efi_arch == 'aarch64' + elif host_cpu == 'aarch64' EFI_MACHINE_TYPE_NAME = 'aa64' gnu_efi_arch = 'aarch64' else @@ -317,7 +342,6 @@ endif if build_standalone and get_option('plugin_dell') libsmbios_c = dependency('libsmbios_c', version : '>= 2.4.0') - efivar = dependency('efivar') conf.set('HAVE_DELL', '1') if not get_option('plugin_uefi') error('plugin_dell also needs plugin_uefi to work') @@ -418,6 +442,7 @@ if build_standalone plugin_deps += soup plugin_deps += libarchive plugin_deps += gudev + plugin_deps += libjsonglib endif root_incdir = include_directories('.') @@ -427,7 +452,7 @@ if get_option('gtkdoc') subdir('docs') endif subdir('libfwupd') -if build_daemon +if build_daemon and get_option('polkit') subdir('policy') endif if build_standalone @@ -452,3 +477,17 @@ if makensis.found() join_paths(meson.source_root(), 'contrib', 'setup-win32.nsi'), ]) endif + +codespell = find_program('codespell', required : false) +if codespell.found() +run_target('codespell', + command: [ + codespell, + meson.source_root(), + '--write-changes', + '--builtin', 'en-GB_to_en-US', + '--skip', '*.po,*.csv,*.svg,*.p7c,subprojects,.git,pcrs,build*', + '--ignore-words-list', 'conexant,Conexant,gir,GIR,hsi,HSI,cancelled,Cancelled', + ] +) +endif diff --git a/meson_options.txt b/meson_options.txt index 41ba066d7..0a0e28536 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -7,6 +7,7 @@ option('introspection', type : 'boolean', value : true, description : 'generate option('lvfs', type : 'boolean', value : true, description : 'enable LVFS remotes') option('man', type : 'boolean', value : true, description : 'enable man pages') option('gudev', type : 'boolean', value : true, description : 'enable GUdev support') +option('polkit', type: 'boolean', value : true, description : 'enable PolKit support in daemon') option('plugin_altos', type : 'boolean', value : true, description : 'enable altos support') option('plugin_amt', type : 'boolean', value : true, description : 'enable Intel AMT support') option('plugin_dell', type : 'boolean', value : true, description : 'enable Dell-specific support') @@ -15,19 +16,21 @@ option('plugin_emmc', type : 'boolean', value : true, description : 'enable eMMC option('plugin_synaptics', type: 'boolean', value: true, description : 'enable Synaptics MST hub support') option('plugin_thunderbolt', type : 'boolean', value : true, description : 'enable Thunderbolt support') option('plugin_redfish', type : 'boolean', value : true, description : 'enable Redfish support') -option('plugin_tpm', type : 'boolean', value : true, description : 'enable TPM support') option('plugin_uefi', type : 'boolean', value : true, description : 'enable UEFI support') option('plugin_nvme', type : 'boolean', value : true, description : 'enable NVMe support') option('plugin_modem_manager', type : 'boolean', value : false, description : 'enable ModemManager support') +option('plugin_msr', type : 'boolean', value : true, description : 'enable MSR support') option('plugin_flashrom', type : 'boolean', value : false, description : 'enable libflashrom support') option('plugin_coreboot', type : 'boolean', value : true, description : 'enable coreboot support') option('systemd', type : 'boolean', value : true, description : 'enable systemd support') option('systemd_root_prefix', type: 'string', value: '', description: 'Directory to base systemd’s installation directories on') option('elogind', type : 'boolean', value : false, description : 'enable elogind support') option('tests', type : 'boolean', value : true, description : 'enable tests') +option('tpm', type : 'boolean', value : true, description : 'enable TPM support') option('udevdir', type: 'string', value: '', description: 'Directory for udev rules') option('efi-cc', type : 'string', value : 'gcc', description : 'the compiler to use for EFI modules') option('efi-ld', type : 'string', value : 'ld', description : 'the linker to use for EFI modules') +option('efi-objcopy', type : 'string', value : 'objcopy', description : 'the objcopy utility to use for EFI modules') option('efi-libdir', type : 'string', description : 'path to the EFI lib directory') option('efi-ldsdir', type : 'string', description : 'path to the EFI lds directory') option('efi-includedir', type : 'string', value : '/usr/include/efi', description : 'path to the EFI header directory') diff --git a/plugins/acpi-dmar/README.md b/plugins/acpi-dmar/README.md new file mode 100644 index 000000000..226fabbc4 --- /dev/null +++ b/plugins/acpi-dmar/README.md @@ -0,0 +1,12 @@ +DMA Protection +============== + +Introduction +------------ + +This plugin checks if DMA remapping for Thunderbolt devices is available. The +result will be stored in an security attribute for HSI. + +External interface access +------------------------- +This plugin requires read access to `/sys/firmware/acpi/tables`. diff --git a/plugins/acpi-dmar/fu-acpi-dmar.c b/plugins/acpi-dmar/fu-acpi-dmar.c new file mode 100644 index 000000000..29c2fc8f0 --- /dev/null +++ b/plugins/acpi-dmar/fu-acpi-dmar.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-common.h" +#include "fu-acpi-dmar.h" + +struct _FuAcpiDmar { + GObject parent_instance; + gboolean opt_in; +}; + +G_DEFINE_TYPE (FuAcpiDmar, fu_acpi_dmar, G_TYPE_OBJECT) + +#define DMAR_DMA_CTRL_PLATFORM_OPT_IN_FLAG 0x4 + +FuAcpiDmar * +fu_acpi_dmar_new (GBytes *blob, GError **error) +{ + FuAcpiDmar *self = g_object_new (FU_TYPE_ACPI_DMAR, NULL); + gchar creator_id[5] = { '\0' }; + gchar oem_table_id[9] = { '\0' }; + gchar signature[5] = { '\0' }; + gsize bufsz = 0; + guint8 flags = 0; + const guint8 *buf = g_bytes_get_data (blob, &bufsz); + + /* parse table */ + if (!fu_memcpy_safe ((guint8 *) signature, sizeof(signature), 0x0, /* dst */ + buf, bufsz, 0x00, /* src */ + sizeof(signature) - 1, error)) + return NULL; + if (strcmp (signature, "DMAR") != 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Not a DMAR table, got %s", + signature); + return NULL; + } + if (!fu_memcpy_safe ((guint8 *) oem_table_id, sizeof(oem_table_id), 0x0,/* dst */ + buf, bufsz, 0x10, /* src */ + sizeof(oem_table_id) - 1, error)) + return NULL; + g_debug ("OemTableId: %s", oem_table_id); + if (!fu_memcpy_safe ((guint8 *) creator_id, sizeof(creator_id), 0x0, /* dst */ + buf, bufsz, 0x1c, /* src */ + sizeof(creator_id) - 1, error)) + return NULL; + g_debug ("CreatorId: %s", creator_id); + if (!fu_memcpy_safe (&flags, sizeof(flags), 0x0, /* dst */ + buf, bufsz, 0x25, /* src */ + sizeof(flags), error)) + return NULL; + g_debug ("Flags: 0x%02x", flags); + self->opt_in = (flags & DMAR_DMA_CTRL_PLATFORM_OPT_IN_FLAG) > 0; + return self; +} + +gboolean +fu_acpi_dmar_get_opt_in (FuAcpiDmar *self) +{ + g_return_val_if_fail (FU_IS_ACPI_DMAR (self), FALSE); + return self->opt_in; +} + +static void +fu_acpi_dmar_class_init (FuAcpiDmarClass *klass) +{ +} + +static void +fu_acpi_dmar_init (FuAcpiDmar *self) +{ +} diff --git a/plugins/acpi-dmar/fu-acpi-dmar.h b/plugins/acpi-dmar/fu-acpi-dmar.h new file mode 100644 index 000000000..57dcc0f8e --- /dev/null +++ b/plugins/acpi-dmar/fu-acpi-dmar.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ACPI_DMAR (fu_acpi_dmar_get_type ()) +G_DECLARE_FINAL_TYPE (FuAcpiDmar, fu_acpi_dmar, FU, ACPI_DMAR, GObject) + +FuAcpiDmar *fu_acpi_dmar_new (GBytes *blob, + GError **error); +gboolean fu_acpi_dmar_get_opt_in (FuAcpiDmar *self); diff --git a/plugins/acpi-dmar/fu-plugin-acpi-dmar.c b/plugins/acpi-dmar/fu-plugin-acpi-dmar.c new file mode 100644 index 000000000..0826a0c6e --- /dev/null +++ b/plugins/acpi-dmar/fu-plugin-acpi-dmar.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" +#include "fu-acpi-dmar.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + g_autofree gchar *fn = NULL; + g_autofree gchar *path = NULL; + g_autoptr(FuAcpiDmar) dmar = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error_local = NULL; + + /* only Intel */ + if (!fu_common_is_cpu_intel ()) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_ACPI_DMAR); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL); + fu_security_attrs_append (attrs, attr); + + /* load DMAR table */ + path = fu_common_get_path (FU_PATH_KIND_ACPI_TABLES); + fn = g_build_filename (path, "DMAR", NULL); + blob = fu_common_get_contents_bytes (fn, &error_local); + if (blob == NULL) { + g_debug ("failed to load %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + dmar = fu_acpi_dmar_new (blob, &error_local); + if (dmar == NULL) { + g_warning ("failed to parse %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (!fu_acpi_dmar_get_opt_in (dmar)) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} diff --git a/plugins/acpi-dmar/fu-self-test.c b/plugins/acpi-dmar/fu-self-test.c new file mode 100644 index 000000000..0c8871f69 --- /dev/null +++ b/plugins/acpi-dmar/fu-self-test.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-common.h" + +#include "fu-acpi-dmar.h" + +static void +fu_acpi_dmar_opt_in_func (void) +{ + const gchar *ci = g_getenv ("CI_NETWORK"); + g_autoptr(FuAcpiDmar) dmar = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) blob = NULL; + g_autofree gchar *fn = NULL; + + fn = g_test_build_filename (G_TEST_DIST, "tests", "DMAR", NULL); + if (!g_file_test (fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing DMAR"); + return; + } + blob = fu_common_get_contents_bytes (fn, &error); + g_assert_no_error (error); + g_assert_nonnull (blob); + dmar = fu_acpi_dmar_new (blob, &error); + g_assert_no_error (error); + g_assert_nonnull (dmar); + g_assert_true (fu_acpi_dmar_get_opt_in (dmar)); +} + +static void +fu_acpi_dmar_opt_out_func (void) +{ + const gchar *ci = g_getenv ("CI_NETWORK"); + g_autoptr(FuAcpiDmar) dmar = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) blob = NULL; + g_autofree gchar *fn = NULL; + + fn = g_test_build_filename (G_TEST_DIST, "tests", "DMAR-OPTOUT", NULL); + if (!g_file_test (fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing DMAR-OPTOUT"); + return; + } + blob = fu_common_get_contents_bytes (fn, &error); + g_assert_no_error (error); + g_assert_nonnull (blob); + dmar = fu_acpi_dmar_new (blob, &error); + g_assert_no_error (error); + g_assert_nonnull (dmar); + g_assert_false (fu_acpi_dmar_get_opt_in (dmar)); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func ("/acpi-dmar/opt-in", fu_acpi_dmar_opt_in_func); + g_test_add_func ("/acpi-dmar/opt-out", fu_acpi_dmar_opt_out_func); + + return g_test_run (); +} diff --git a/plugins/acpi-dmar/meson.build b/plugins/acpi-dmar/meson.build new file mode 100644 index 000000000..ae1400b02 --- /dev/null +++ b/plugins/acpi-dmar/meson.build @@ -0,0 +1,53 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginAcpiDmar"'] + +shared_module('fu_plugin_acpi_dmar', + fu_hash, + sources : [ + 'fu-plugin-acpi-dmar.c', + 'fu-acpi-dmar.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) + +if get_option('tests') + testdatadirs = environment() + testdatadirs.set('G_TEST_SRCDIR', meson.current_source_dir()) + testdatadirs.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'acpi-dmar-self-test', + fu_hash, + sources : [ + 'fu-self-test.c', + 'fu-acpi-dmar.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + dependencies : [ + plugin_deps, + ], + link_with : [ + fwupd, + fwupdplugin, + ], + install : true, + install_dir : installed_test_bindir, + ) + test('acpi-dmar-self-test', e, env : testdatadirs) # added to installed-tests +endif diff --git a/plugins/acpi-facp/README.md b/plugins/acpi-facp/README.md new file mode 100644 index 000000000..885d6368a --- /dev/null +++ b/plugins/acpi-facp/README.md @@ -0,0 +1,12 @@ +ACPI FACP +========= + +Introduction +------------ + +This plugin checks if S2I sleep is available. The result will be stored in an +security attribute for HSI. + +External interface access +------------------------- +This plugin requires read access to `/sys/firmware/acpi/tables`. diff --git a/plugins/acpi-facp/fu-acpi-facp.c b/plugins/acpi-facp/fu-acpi-facp.c new file mode 100644 index 000000000..9aa776f51 --- /dev/null +++ b/plugins/acpi-facp/fu-acpi-facp.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-common.h" +#include "fu-acpi-facp.h" + +struct _FuAcpiFacp { + GObject parent_instance; + gboolean get_s2i; +}; + +G_DEFINE_TYPE (FuAcpiFacp, fu_acpi_facp, G_TYPE_OBJECT) + +#define LOW_POWER_S0_IDLE_CAPABLE (1 << 21) + +FuAcpiFacp * +fu_acpi_facp_new (GBytes *blob, GError **error) +{ + FuAcpiFacp *self = g_object_new (FU_TYPE_ACPI_FACP, NULL); + gsize bufsz = 0; + guint32 flags = 0; + const guint8 *buf = g_bytes_get_data (blob, &bufsz); + + /* parse table */ + if (!fu_common_read_uint32_safe (buf, bufsz, 0x70, &flags, G_LITTLE_ENDIAN, error)) + return NULL; + g_debug ("Flags: 0x%04x", flags); + self->get_s2i = (flags & LOW_POWER_S0_IDLE_CAPABLE) > 0; + return self; +} + +gboolean +fu_acpi_facp_get_s2i (FuAcpiFacp *self) +{ + g_return_val_if_fail (FU_IS_ACPI_FACP (self), FALSE); + return self->get_s2i; +} + +static void +fu_acpi_facp_class_init (FuAcpiFacpClass *klass) +{ +} + +static void +fu_acpi_facp_init (FuAcpiFacp *self) +{ +} diff --git a/plugins/acpi-facp/fu-acpi-facp.h b/plugins/acpi-facp/fu-acpi-facp.h new file mode 100644 index 000000000..f143c233c --- /dev/null +++ b/plugins/acpi-facp/fu-acpi-facp.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ACPI_FACP (fu_acpi_facp_get_type ()) +G_DECLARE_FINAL_TYPE (FuAcpiFacp, fu_acpi_facp, FU, ACPI_FACP, GObject) + +FuAcpiFacp *fu_acpi_facp_new (GBytes *blob, + GError **error); +gboolean fu_acpi_facp_get_s2i (FuAcpiFacp *self); diff --git a/plugins/acpi-facp/fu-plugin-acpi-facp.c b/plugins/acpi-facp/fu-plugin-acpi-facp.c new file mode 100644 index 000000000..e0fef9189 --- /dev/null +++ b/plugins/acpi-facp/fu-plugin-acpi-facp.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" +#include "fu-acpi-facp.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + g_autofree gchar *fn = NULL; + g_autofree gchar *path = NULL; + g_autoptr(FuAcpiFacp) facp = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error_local = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL); + fu_security_attrs_append (attrs, attr); + + /* load FACP table */ + path = fu_common_get_path (FU_PATH_KIND_ACPI_TABLES); + fn = g_build_filename (path, "FACP", NULL); + blob = fu_common_get_contents_bytes (fn, &error_local); + if (blob == NULL) { + g_warning ("failed to load %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + facp = fu_acpi_facp_new (blob, &error_local); + if (facp == NULL) { + g_warning ("failed to parse %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (!fu_acpi_facp_get_s2i (facp)) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} diff --git a/plugins/acpi-facp/fu-self-test.c b/plugins/acpi-facp/fu-self-test.c new file mode 100644 index 000000000..e00248760 --- /dev/null +++ b/plugins/acpi-facp/fu-self-test.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-common.h" + +#include "fu-acpi-facp.h" + +static void +fu_acpi_facp_s2i_disabled_func (void) +{ + const gchar *ci = g_getenv ("CI_NETWORK"); + g_autofree gchar *fn = NULL; + g_autoptr(FuAcpiFacp) facp = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + fn = g_test_build_filename (G_TEST_DIST, "tests", "FACP", NULL); + if (!g_file_test (fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing FACP"); + return; + } + blob = fu_common_get_contents_bytes (fn, &error); + g_assert_no_error (error); + g_assert_nonnull (blob); + facp = fu_acpi_facp_new (blob, &error); + g_assert_no_error (error); + g_assert_nonnull (facp); + g_assert_false (fu_acpi_facp_get_s2i (facp)); +} + +static void +fu_acpi_facp_s2i_enabled_func (void) +{ + const gchar *ci = g_getenv ("CI_NETWORK"); + g_autofree gchar *fn = NULL; + g_autoptr(FuAcpiFacp) facp = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + fn = g_test_build_filename (G_TEST_DIST, "tests", "FACP-S2I", NULL); + if (!g_file_test (fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing FACP-S2I"); + return; + } + blob = fu_common_get_contents_bytes (fn, &error); + g_assert_no_error (error); + g_assert_nonnull (blob); + facp = fu_acpi_facp_new (blob, &error); + g_assert_no_error (error); + g_assert_nonnull (facp); + g_assert_true (fu_acpi_facp_get_s2i (facp)); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func ("/acpi-facp/s2i{disabled}", fu_acpi_facp_s2i_disabled_func); + g_test_add_func ("/acpi-facp/s2i{enabled}", fu_acpi_facp_s2i_enabled_func); + return g_test_run (); +} diff --git a/plugins/acpi-facp/meson.build b/plugins/acpi-facp/meson.build new file mode 100644 index 000000000..71ec8f0ab --- /dev/null +++ b/plugins/acpi-facp/meson.build @@ -0,0 +1,53 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginAcpiFacp"'] + +shared_module('fu_plugin_acpi_facp', + fu_hash, + sources : [ + 'fu-plugin-acpi-facp.c', + 'fu-acpi-facp.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) + +if get_option('tests') + testdatadirs = environment() + testdatadirs.set('G_TEST_SRCDIR', meson.current_source_dir()) + testdatadirs.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'acpi-facp-self-test', + fu_hash, + sources : [ + 'fu-self-test.c', + 'fu-acpi-facp.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + dependencies : [ + plugin_deps, + ], + link_with : [ + fwupd, + fwupdplugin, + ], + install : true, + install_dir : installed_test_bindir, + ) + test('acpi-facp-self-test', e, env : testdatadirs) # added to installed-tests +endif diff --git a/plugins/altos/README.md b/plugins/altos/README.md index a05734451..d8d28895c 100644 --- a/plugins/altos/README.md +++ b/plugins/altos/README.md @@ -67,3 +67,7 @@ Command: `W $addr\n` where `$addr` is a memory address `0x8001000->0x8008000` Command: `v\n` The device will reboot into application mode. This is typically performed after flashing firmware completes successfully. + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/altos/fu-altos-device.c b/plugins/altos/fu-altos-device.c index ab3381df9..55180dad1 100644 --- a/plugins/altos/fu-altos-device.c +++ b/plugins/altos/fu-altos-device.c @@ -381,13 +381,12 @@ fu_altos_device_write_firmware (FuDevice *device, return TRUE; } -static FuFirmware * -fu_altos_device_read_firmware (FuDevice *device, GError **error) +static GBytes * +fu_altos_device_dump_firmware (FuDevice *device, GError **error) { FuAltosDevice *self = FU_ALTOS_DEVICE (device); guint flash_len; g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GBytes) fw = NULL; g_autoptr(GString) buf = g_string_new (NULL); /* check kind */ @@ -441,8 +440,7 @@ fu_altos_device_read_firmware (FuDevice *device, GError **error) } /* success */ - fw = g_bytes_new (buf->str, buf->len); - return fu_firmware_new_from_bytes (fw); + return g_bytes_new (buf->str, buf->len); } static gboolean @@ -577,6 +575,6 @@ fu_altos_device_class_init (FuAltosDeviceClass *klass) klass_device->probe = fu_altos_device_probe; klass_device->prepare_firmware = fu_altos_device_prepare_firmware; klass_device->write_firmware = fu_altos_device_write_firmware; - klass_device->read_firmware = fu_altos_device_read_firmware; + klass_device->dump_firmware = fu_altos_device_dump_firmware; object_class->finalize = fu_altos_device_finalize; } diff --git a/plugins/amt/README.md b/plugins/amt/README.md index e53d9d736..55ad2003c 100644 --- a/plugins/amt/README.md +++ b/plugins/amt/README.md @@ -25,3 +25,7 @@ Vendor ID Security ------------------ The device is not upgradable and thus requires no vendor ID set. + +External interface access +------------------------- +This plugin requires read only access to `/dev/mei0`. diff --git a/plugins/ata/README.md b/plugins/ata/README.md index 97240c8da..f9dbc87d5 100644 --- a/plugins/ata/README.md +++ b/plugins/ata/README.md @@ -47,3 +47,7 @@ This plugin uses the following plugin-specific quirks: |------------------------|-------------------------------------------|-----------------------| | `AtaTransferBlocks` | Blocks to transfer, or `0xffff` for max | 1.2.4 | | `AtaTransferMode` | The transfer mode, `0x3`, `0x7` or `0xe` | 1.2.4 | + +External interface access +------------------------- +This plugin requires the `SG_IO` ioctl interface. diff --git a/plugins/ata/ata.quirk b/plugins/ata/ata.quirk index 21a95cc0f..3fa5df14f 100644 --- a/plugins/ata/ata.quirk +++ b/plugins/ata/ata.quirk @@ -10,6 +10,10 @@ VendorId = ATA:0x1179 Vendor = Samsung VendorId = ATA:0x144D +[DeviceInstanceId=OUI\000120] +Vendor = Corsair +VendorId = ATA:0x1987 + [DeviceInstanceId=OUI\0004cf] Vendor = Seagate VendorId = ATA:0x1BB1 @@ -74,3 +78,6 @@ VendorId = ATA:0x1CC1 Vendor = SanDisk VendorId = ATA:0x15B7 +[DeviceInstanceId=OUI\e83a97] +Vendor = Toshiba +VendorId = ATA:0x1179 diff --git a/plugins/ata/fu-ata-device.c b/plugins/ata/fu-ata-device.c index 85d9191ba..0bb2782c3 100644 --- a/plugins/ata/fu-ata-device.c +++ b/plugins/ata/fu-ata-device.c @@ -548,10 +548,11 @@ fu_ata_device_command (FuAtaDevice *self, struct ata_tf *tf, SG_IO, (guint8 *) &io_hdr, NULL, error)) return FALSE; - g_debug ("ATA_%u status=0x%x, host_status=0x%x, driver_status=0x%x", - io_hdr.cmd_len, io_hdr.status, io_hdr.host_status, io_hdr.driver_status); - if (g_getenv ("FWUPD_ATA_VERBOSE") != NULL) + if (g_getenv ("FWUPD_ATA_VERBOSE") != NULL) { + g_debug ("ATA_%u status=0x%x, host_status=0x%x, driver_status=0x%x", + io_hdr.cmd_len, io_hdr.status, io_hdr.host_status, io_hdr.driver_status); fu_common_dump_raw (G_LOG_DOMAIN, "SB", sb, sizeof(sb)); + } /* error check */ if (io_hdr.status && io_hdr.status != SG_CHECK_CONDITION) { @@ -584,8 +585,12 @@ fu_ata_device_command (FuAtaDevice *self, struct ata_tf *tf, tf->lbah = sb[8 + 11]; tf->dev = sb[8 + 12]; tf->status = sb[8 + 13]; - g_debug ("ATA_%u stat=%02x err=%02x nsect=%02x lbal=%02x lbam=%02x lbah=%02x dev=%02x", - io_hdr.cmd_len, tf->status, tf->error, tf->nsect, tf->lbal, tf->lbam, tf->lbah, tf->dev); + if (g_getenv ("FWUPD_ATA_VERBOSE") != NULL) { + g_debug ("ATA_%u stat=%02x err=%02x nsect=%02x lbal=%02x " + "lbam=%02x lbah=%02x dev=%02x", + io_hdr.cmd_len, tf->status, tf->error, tf->nsect, tf->lbal, + tf->lbam, tf->lbah, tf->dev); + } /* io error */ if (tf->status & (ATA_STAT_ERR | ATA_STAT_DRQ)) { diff --git a/plugins/ata/fu-self-test.c b/plugins/ata/fu-self-test.c index 038dd585c..bbc1b0f5f 100644 --- a/plugins/ata/fu-self-test.c +++ b/plugins/ata/fu-self-test.c @@ -16,12 +16,17 @@ fu_ata_id_func (void) { gboolean ret; gsize sz; + const gchar *ci = g_getenv ("CI_NETWORK"); g_autofree gchar *data = NULL; g_autofree gchar *path = NULL; g_autoptr(FuAtaDevice) dev = NULL; g_autoptr(GError) error = NULL; - path = g_build_filename (TESTDATADIR, "StarDrive-SBFM61.2.bin", NULL); + path = g_test_build_filename (G_TEST_DIST, "tests", "StarDrive-SBFM61.2.bin", NULL); + if (!g_file_test (path, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing StarDrive-SBFM61.2.bin"); + return; + } ret = g_file_get_contents (path, &data, &sz, &error); g_assert_no_error (error); g_assert (ret); @@ -40,13 +45,18 @@ fu_ata_oui_func (void) { gboolean ret; gsize sz; + const gchar *ci = g_getenv ("CI_NETWORK"); g_autofree gchar *data = NULL; g_autofree gchar *path = NULL; g_autofree gchar *str = NULL; g_autoptr(FuAtaDevice) dev = NULL; g_autoptr(GError) error = NULL; - path = g_build_filename (TESTDATADIR, "Samsung SSD 860 EVO 500GB.bin", NULL); + path = g_test_build_filename (G_TEST_DIST, "tests", "Samsung SSD 860 EVO 500GB.bin", NULL); + if (!g_file_test (path, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing Samsung SSD 860 EVO 500GB.bin"); + return; + } ret = g_file_get_contents (path, &data, &sz, &error); g_assert_no_error (error); g_assert (ret); diff --git a/plugins/ata/meson.build b/plugins/ata/meson.build index 8444bb8a7..f32b97fe3 100644 --- a/plugins/ata/meson.build +++ b/plugins/ata/meson.build @@ -37,8 +37,9 @@ shared_module('fu_plugin_ata', ) if get_option('tests') - testdatadir = join_paths(meson.current_source_dir(), 'tests') - cargs += '-DTESTDATADIR="' + testdatadir + '"' + testdatadirs = environment() + testdatadirs.set('G_TEST_SRCDIR', meson.current_source_dir()) + testdatadirs.set('G_TEST_BUILDDIR', meson.current_build_dir()) e = executable( 'ata-self-test', fu_hash, @@ -58,7 +59,8 @@ if get_option('tests') fwupd, fwupdplugin, ], - c_args : cargs + install : true, + install_dir : installed_test_bindir, ) - test('ata-self-test', e) + test('ata-self-test', e, env : testdatadirs) # added to installed-tests endif diff --git a/plugins/ata/tests/Samsung SSD 860 EVO 500GB.bin b/plugins/ata/tests/Samsung SSD 860 EVO 500GB.bin deleted file mode 100644 index cc6a339bf..000000000 Binary files a/plugins/ata/tests/Samsung SSD 860 EVO 500GB.bin and /dev/null differ diff --git a/plugins/ata/tests/StarDrive-SBFM61.2.bin b/plugins/ata/tests/StarDrive-SBFM61.2.bin deleted file mode 100644 index 201cceca2..000000000 Binary files a/plugins/ata/tests/StarDrive-SBFM61.2.bin and /dev/null differ diff --git a/plugins/bcm57xx/README.md b/plugins/bcm57xx/README.md new file mode 100644 index 000000000..f8e85f1c1 --- /dev/null +++ b/plugins/bcm57xx/README.md @@ -0,0 +1,32 @@ +BCM57xx Support +=============== + +Introduction +------------ + +This plugin updates BCM57xx wired network adaptors from Broadcom using a +reverse-engineered flashing protocol. It is designed to be used with the +clean-room reimplementation of the BCM5719 firmware found here: +https://github.com/meklort/bcm5719-fw + +Protocol +-------- +BCM57xx devices support a custom `com.broadcom.bcm57xx` protocol which is +implemented as ioctls like ethtool does. + +GUID Generation +--------------- + +These devices use the standard PCI instance IDs, for example: + + * `PCI\VEN_14E4&DEV_1657` + * `PCI\VEN_14E4&DEV_1657&SUBSYS_17AA222E` + +Vendor ID Security +------------------ + +The vendor ID is set from the PCI vendor, in this instance set to `PCI:0x14E4` + +External interface access +------------------------- +This plugin requires the `SIOCETHTOOL` ioctl interface. diff --git a/plugins/bcm57xx/bcm57xx.quirk b/plugins/bcm57xx/bcm57xx.quirk new file mode 100644 index 000000000..25d6b17b2 --- /dev/null +++ b/plugins/bcm57xx/bcm57xx.quirk @@ -0,0 +1,9 @@ +# Broadcom BCM5719 +[DeviceInstanceId=PCI\VEN_14E4&DEV_1657] +Plugin = bcm57xx + +# Dell PCI card +[DeviceInstanceId=PCI\VEN_14E4&DEV_1657&SUBSYS_14E41904] +FirmwareSize = 0x80000 +[DeviceInstanceId=PCI\VEN_14E4&DEV_1657&SUBSYS_94E41904] +FirmwareSize = 0x80000 diff --git a/plugins/bcm57xx/fu-bcm57xx-common.c b/plugins/bcm57xx/fu-bcm57xx-common.c new file mode 100644 index 000000000..42434c661 --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-common.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 Evan Lojewski + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: GPL-2+ + */ + +#include "config.h" + +#include "fu-common.h" + +#include "fu-bcm57xx-common.h" + +guint32 +fu_bcm57xx_nvram_crc (const guint8 *buf, gsize bufsz) +{ + return GUINT32_FROM_BE(fu_common_crc32 (buf, bufsz)); +} + +gboolean +fu_bcm57xx_verify_crc (GBytes *fw, GError **error) +{ + guint32 crc_actual; + guint32 crc_file = 0; + gsize bufsz = 0x0; + const guint8 *buf = g_bytes_get_data (fw, &bufsz); + + /* expected */ + if (!fu_common_read_uint32_safe (buf, bufsz, bufsz - sizeof(guint32), + &crc_file, G_BIG_ENDIAN, error)) + return FALSE; + + /* reality */ + crc_actual = fu_bcm57xx_nvram_crc (buf, bufsz - sizeof(guint32)); + if (crc_actual != crc_file) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid CRC, expected 0x%08x got: 0x%08x", + (guint) crc_file, (guint) crc_actual); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_bcm57xx_verify_magic (GBytes *fw, gsize offset, GError **error) +{ + guint32 magic = 0; + gsize bufsz = 0x0; + const guint8 *buf = g_bytes_get_data (fw, &bufsz); + + /* hardcoded value */ + if (!fu_common_read_uint32_safe (buf, bufsz, offset, &magic, G_BIG_ENDIAN, error)) + return FALSE; + if (magic != BCM_NVRAM_MAGIC) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid magic, got: 0x%x", + (guint) magic); + return FALSE; + } + + /* success */ + return TRUE; +} + +void +fu_bcm57xx_veritem_free (Bcm57xxVeritem *veritem) +{ + g_free (veritem->branch); + g_free (veritem->version); + g_free (veritem); +} + +Bcm57xxVeritem * +fu_bcm57xx_veritem_new (const guint8 *buf, gsize bufsz) +{ + g_autofree gchar *tmp = NULL; + g_autoptr(Bcm57xxVeritem) veritem = g_new0 (Bcm57xxVeritem, 1); + struct { + const gchar *prefix; + const gchar *branch; + FwupdVersionFormat verfmt; + } data[] = { + { "5719-v", BCM_FW_BRANCH_UNKNOWN, FWUPD_VERSION_FORMAT_PAIR }, + { "stage1-", BCM_FW_BRANCH_OSS_FIRMWARE, FWUPD_VERSION_FORMAT_TRIPLET }, + { NULL, NULL, 0 } + }; + + /* do not assume this is NUL terminated */ + tmp = g_strndup ((const gchar *) buf, bufsz); + if (tmp == NULL || tmp[0] == '\0') + return NULL; + + /* use prefix to define object */ + for (guint i = 0; data[i].prefix != NULL; i++) { + if (g_str_has_prefix (tmp, data[i].prefix)) { + veritem->version = g_strdup (tmp + strlen (data[i].prefix)); + veritem->branch = g_strdup (data[i].branch); + veritem->verfmt = data[i].verfmt; + return g_steal_pointer (&veritem); + } + } + veritem->verfmt = FWUPD_VERSION_FORMAT_UNKNOWN; + veritem->version = g_strdup (tmp); + return g_steal_pointer (&veritem); +} diff --git a/plugins/bcm57xx/fu-bcm57xx-common.h b/plugins/bcm57xx/fu-bcm57xx-common.h new file mode 100644 index 000000000..aadbb45a7 --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-common.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +#define BCM_VENDOR_BROADCOM 0x14E4 + +#define BCM_FW_BRANCH_UNKNOWN NULL +#define BCM_FW_BRANCH_OSS_FIRMWARE "oss-firmware" + +#define BCM_FIRMWARE_SIZE 0x40000 /* x2 for Dell */ +#define BCM_PHYS_ADDR_DEFAULT 0x08003800 + +#define BCM_NVRAM_MAGIC 0x669955AA + +/* offsets into NVMRAM */ +#define BCM_NVRAM_HEADER_BASE 0x00 +#define BCM_NVRAM_DIRECTORY_BASE 0x14 +#define BCM_NVRAM_INFO_BASE 0x74 +#define BCM_NVRAM_VPD_BASE 0x100 +#define BCM_NVRAM_INFO2_BASE 0x200 +#define BCM_NVRAM_STAGE1_BASE 0x28c + +#define BCM_NVRAM_HEADER_MAGIC 0x00 +#define BCM_NVRAM_HEADER_PHYS_ADDR 0x04 +#define BCM_NVRAM_HEADER_SIZE_WRDS 0x08 +#define BCM_NVRAM_HEADER_OFFSET 0x0C +#define BCM_NVRAM_HEADER_CRC 0x10 +#define BCM_NVRAM_HEADER_SZ 0x14 + +#define BCM_NVRAM_INFO_MAC_ADDR0 0x00 +#define BCM_NVRAM_INFO_VENDOR 0x2C +#define BCM_NVRAM_INFO_DEVICE 0x2E +#define BCM_NVRAM_INFO_SZ 0x8C + +#define BCM_NVRAM_DIRECTORY_ADDR 0x00 +#define BCM_NVRAM_DIRECTORY_SIZE_WRDS 0x04 +#define BCM_NVRAM_DIRECTORY_OFFSET 0x08 +#define BCM_NVRAM_DIRECTORY_SZ 0x0c + +#define BCM_NVRAM_VPD_SZ 0x100 + +#define BCM_NVRAM_INFO2_SZ 0x8c + +#define BCM_NVRAM_STAGE1_VERADDR 0x08 +#define BCM_NVRAM_STAGE1_VERSION 0x0C + +typedef struct { + gchar *branch; + gchar *version; + FwupdVersionFormat verfmt; +} Bcm57xxVeritem; + +guint32 fu_bcm57xx_nvram_crc (const guint8 *buf, + gsize bufsz); +gboolean fu_bcm57xx_verify_crc (GBytes *fw, + GError **error); +gboolean fu_bcm57xx_verify_magic (GBytes *fw, + gsize offset, + GError **error); + +/* parses stage1 version */ +void fu_bcm57xx_veritem_free (Bcm57xxVeritem *veritem); +Bcm57xxVeritem *fu_bcm57xx_veritem_new (const guint8 *buf, + gsize bufsz); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(Bcm57xxVeritem, fu_bcm57xx_veritem_free) diff --git a/plugins/bcm57xx/fu-bcm57xx-device.c b/plugins/bcm57xx/fu-bcm57xx-device.c new file mode 100644 index 000000000..b06cc66bd --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-device.c @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2018-2020 Evan Lojewski + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: GPL-2+ + */ + +#include "config.h" + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_ETHTOOL_H +#include +#include +#include +#endif +#ifdef HAVE_IOCTL_H +#include +#endif +#ifdef HAVE_SOCKET_H +#include +#endif + +#include "fu-chunk.h" +#include "fu-common.h" + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-device.h" +#include "fu-bcm57xx-recovery-device.h" +#include "fu-bcm57xx-firmware.h" +#include "fu-bcm57xx-dict-image.h" + +#define FU_BCM57XX_BLOCK_SZ 0x4000 /* 16kb */ + +struct _FuBcm57xxDevice { + FuUdevDevice parent_instance; + FuBcm57xxRecoveryDevice *recovery; + gchar *ethtool_iface; + int ethtool_fd; +}; + +G_DEFINE_TYPE (FuBcm57xxDevice, fu_bcm57xx_device, FU_TYPE_UDEV_DEVICE) + +static void +fu_bcm57xx_device_to_string (FuUdevDevice *device, guint idt, GString *str) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device); + fu_common_string_append_kv (str, idt, "EthtoolIface", self->ethtool_iface); +} + +static gboolean +fu_bcm57xx_device_probe (FuUdevDevice *device, GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device); + g_autofree gchar *fn = NULL; + g_autoptr(GPtrArray) ifaces = NULL; + + /* only enumerate number 0 */ + if (fu_udev_device_get_number (device) != 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "only device 0 supported on multi-device card"); + return FALSE; + } + + /* we need this even for non-recovery to reset APE */ + fu_device_set_quirks (FU_DEVICE (self->recovery), + fu_device_get_quirks (FU_DEVICE (self))); + fu_device_incorporate (FU_DEVICE (self->recovery), FU_DEVICE (self)); + if (!fu_device_probe (FU_DEVICE (self->recovery), error)) + return FALSE; + + /* only if has an interface */ + fn = g_build_filename (fu_udev_device_get_sysfs_path (device), "net", NULL); + if (!g_file_test (fn, G_FILE_TEST_EXISTS)) { + g_debug ("waiting for net devices to appear"); + g_usleep (50 * 1000); + } + ifaces = fu_common_filename_glob (fn, "en*", NULL); + if (ifaces == NULL || ifaces->len == 0) { + fu_device_add_child (FU_DEVICE (self), FU_DEVICE (self->recovery)); + } else { + self->ethtool_iface = g_path_get_basename (g_ptr_array_index (ifaces, 0)); + } + + /* success */ + return fu_udev_device_set_physical_id (device, "pci", error); +} + +static gboolean +fu_bcm57xx_device_nvram_write (FuBcm57xxDevice *self, + guint32 address, + const guint8 *buf, + gsize bufsz, + GError **error) +{ +#ifdef HAVE_ETHTOOL_H + gsize eepromsz; + gint rc = -1; + struct ifreq ifr = { 0 }; + g_autofree struct ethtool_eeprom *eeprom = NULL; + + /* failed to load tg3 */ + if (self->ethtool_iface == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as ethtool interface disabled"); + return FALSE; + } + + /* sanity check */ + if (address + bufsz > fu_device_get_firmware_size_max (FU_DEVICE (self))) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "tried to read outside of EEPROM size [0x%x]", + (guint) fu_device_get_firmware_size_max (FU_DEVICE (self))); + return FALSE; + } + + /* write EEPROM (NVRAM) data */ + eepromsz = sizeof(struct ethtool_eeprom) + bufsz; + eeprom = (struct ethtool_eeprom *) g_malloc0 (eepromsz); + eeprom->cmd = ETHTOOL_SEEPROM; + eeprom->magic = BCM_NVRAM_MAGIC; + eeprom->len = bufsz; + eeprom->offset = address; + memcpy (eeprom->data, buf, eeprom->len); + strncpy (ifr.ifr_name, self->ethtool_iface, IFNAMSIZ - 1); + ifr.ifr_data = (char *) eeprom; +#ifdef HAVE_IOCTL_H + rc = ioctl (self->ethtool_fd, SIOCETHTOOL, &ifr); +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif + if (rc < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cannot write eeprom [%i]", rc); + return FALSE; + } + + /* success */ + return TRUE; +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif +} + +static gboolean +fu_bcm57xx_device_nvram_read (FuBcm57xxDevice *self, + guint32 address, + guint8 *buf, + gsize bufsz, + GError **error) +{ +#ifdef HAVE_ETHTOOL_H + gsize eepromsz; + gint rc = -1; + struct ifreq ifr = { 0 }; + g_autofree struct ethtool_eeprom *eeprom = NULL; + + /* failed to load tg3 */ + if (self->ethtool_iface == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as ethtool interface disabled"); + return FALSE; + } + + /* sanity check */ + if (address + bufsz > fu_device_get_firmware_size_max (FU_DEVICE (self))) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "tried to read outside of EEPROM size [0x%x]", + (guint) fu_device_get_firmware_size_max (FU_DEVICE (self))); + return FALSE; + } + + /* read EEPROM (NVRAM) data */ + eepromsz = sizeof(struct ethtool_eeprom) + bufsz; + eeprom = (struct ethtool_eeprom *) g_malloc0 (eepromsz); + eeprom->cmd = ETHTOOL_GEEPROM; + eeprom->len = bufsz; + eeprom->offset = address; + strncpy (ifr.ifr_name, self->ethtool_iface, IFNAMSIZ - 1); + ifr.ifr_data = (char *) eeprom; +#ifdef HAVE_IOCTL_H + rc = ioctl (self->ethtool_fd, SIOCETHTOOL, &ifr); +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif + if (rc < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cannot read eeprom [%i]", rc); + return FALSE; + } + + /* copy back data */ + if (!fu_memcpy_safe (buf, bufsz, 0x0, /* dst */ + (guint8 *) eeprom, eepromsz, /* src */ + G_STRUCT_OFFSET(struct ethtool_eeprom, data), + bufsz, error)) + return FALSE; + + /* success */ + return TRUE; +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif +} + +static gboolean +fu_bcm57xx_device_nvram_check (FuBcm57xxDevice *self, GError **error) +{ +#ifdef HAVE_ETHTOOL_H + gint rc = -1; + struct ethtool_drvinfo drvinfo = { 0 }; + struct ifreq ifr = { 0 }; + + /* failed to load tg3 */ + if (self->ethtool_iface == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as ethtool interface disabled"); + return FALSE; + } + + /* get driver info */ + drvinfo.cmd = ETHTOOL_GDRVINFO; + strncpy (ifr.ifr_name, self->ethtool_iface, IFNAMSIZ - 1); + ifr.ifr_data = (char *) &drvinfo; +#ifdef HAVE_IOCTL_H + rc = ioctl (self->ethtool_fd, SIOCETHTOOL, &ifr); +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif + if (rc < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cannot get driver information [%i]", rc); + return FALSE; + } + g_debug ("FW version %s", drvinfo.fw_version); + + /* sanity check */ + if (drvinfo.eedump_len != fu_device_get_firmware_size_max (FU_DEVICE (self))) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "EEPROM size invalid, got 0x%x, expected 0x%x", + drvinfo.eedump_len, + (guint) fu_device_get_firmware_size_max (FU_DEVICE (self))); + return FALSE; + } + + /* success */ + return TRUE; +#else + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif +} + +static gboolean +fu_bcm57xx_device_activate (FuDevice *device, GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device); + g_autoptr(FuDeviceLocker) locker1 = NULL; + g_autoptr(FuDeviceLocker) locker2 = NULL; + + /* the only way to do this is using the mmap method */ + locker2 = fu_device_locker_new_full (FU_DEVICE (self->recovery), + (FuDeviceLockerFunc) fu_device_detach, + (FuDeviceLockerFunc) fu_device_attach, + error); + if (locker2 == NULL) + return FALSE; + + /* open */ + locker1 = fu_device_locker_new (FU_DEVICE (self->recovery), error); + if (locker1 == NULL) + return FALSE; + + /* activate, causing APE reset, then close, then attach */ + if (!fu_device_activate (FU_DEVICE (self->recovery), error)) + return FALSE; + + /* ensure we attach before we close */ + if (!fu_device_locker_close (locker2, error)) + return FALSE; + + /* wait for the device to restart before calling reload() */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_BUSY); + fu_device_sleep_with_progress (device, 5); /* seconds */ + return TRUE; +} + +static GBytes * +fu_bcm57xx_device_dump_firmware (FuDevice *device, GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device); + const gsize bufsz = fu_device_get_firmware_size_max (FU_DEVICE (self)); + g_autofree guint8 *buf = g_malloc0 (bufsz); + g_autoptr(GPtrArray) chunks = NULL; + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ); + chunks = fu_chunk_array_new (buf, bufsz, 0x0, 0x0, FU_BCM57XX_BLOCK_SZ); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + if (!fu_bcm57xx_device_nvram_read (self, chk->address, + (guint8 *) chk->data, chk->data_sz, + error)) + return NULL; + fu_device_set_progress_full (device, i, chunks->len - 1); + } + + /* read from hardware */ + return g_bytes_new_take (g_steal_pointer (&buf), bufsz); +} + +static FuFirmware * +fu_bcm57xx_device_read_firmware (FuDevice *device, GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_bcm57xx_firmware_new (); + g_autoptr(GBytes) fw = NULL; + + /* read from hardware */ + fw = fu_bcm57xx_device_dump_firmware (device, error); + if (fw == NULL) + return NULL; + if (!fu_firmware_parse (firmware, fw, FWUPD_INSTALL_FLAG_NONE, error)) + return NULL; + + /* remove images that will contain user-data */ + if (!fu_firmware_remove_image_by_id (firmware, "info", error)) + return NULL; + if (!fu_firmware_remove_image_by_id (firmware, "info2", error)) + return NULL; + if (!fu_firmware_remove_image_by_id (firmware, "vpd", error)) + return NULL; + return g_steal_pointer (&firmware); +} + +static FuFirmware * +fu_bcm57xx_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + guint dict_cnt = 0; + g_autoptr(GBytes) fw_old = NULL; + g_autoptr(FuFirmware) firmware = fu_bcm57xx_firmware_new (); + g_autoptr(FuFirmware) firmware_tmp = fu_bcm57xx_firmware_new (); + g_autoptr(FuFirmwareImage) img_ape = NULL; + g_autoptr(FuFirmwareImage) img_stage1 = NULL; + g_autoptr(FuFirmwareImage) img_stage2 = NULL; + g_autoptr(GPtrArray) images = NULL; + + /* try to parse NVRAM, stage1 or APE */ + if (!fu_firmware_parse (firmware_tmp, fw, flags, error)) { + g_prefix_error (error, "failed to parse new firmware: "); + return NULL; + } + + /* for full NVRAM image, verify if correct device */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + guint16 vid = fu_bcm57xx_firmware_get_vendor (FU_BCM57XX_FIRMWARE (firmware_tmp)); + guint16 did = fu_bcm57xx_firmware_get_model (FU_BCM57XX_FIRMWARE (firmware_tmp)); + if (vid != 0x0 && did != 0x0 && + (fu_udev_device_get_vendor (FU_UDEV_DEVICE (device)) != vid || + fu_udev_device_get_model (FU_UDEV_DEVICE (device)) != did)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "PCI vendor or model incorrect, " + "got: %04X:%04X expected %04X:%04X", + vid, did, + fu_udev_device_get_vendor (FU_UDEV_DEVICE (device)), + fu_udev_device_get_model (FU_UDEV_DEVICE (device))); + return NULL; + } + } + + /* get the existing firmware from the device */ + fw_old = fu_bcm57xx_device_dump_firmware (device, error); + if (fw_old == NULL) + return NULL; + if (!fu_firmware_parse (firmware, fw_old, flags, error)) { + g_prefix_error (error, "failed to parse existing firmware: "); + return NULL; + } + if (g_getenv ("FWUPD_BCM57XX_VERBOSE") != NULL) { + g_autofree gchar *str = fu_firmware_to_string (firmware); + g_debug ("existing device firmware: %s", str); + } + + /* merge in all the provided images into the existing firmware */ + img_stage1 = fu_firmware_get_image_by_id (firmware_tmp, "stage1", NULL); + if (img_stage1 != NULL) + fu_firmware_add_image (firmware, img_stage1); + img_stage2 = fu_firmware_get_image_by_id (firmware_tmp, "stage2", NULL); + if (img_stage2 != NULL) + fu_firmware_add_image (firmware, img_stage2); + img_ape = fu_firmware_get_image_by_id (firmware_tmp, "ape", NULL); + if (img_ape != NULL) + fu_firmware_add_image (firmware, img_ape); + + /* the src and dst dictionaries may be in different order */ + images = fu_firmware_get_images (firmware); + for (guint i = 0; i < images->len; i++) { + FuFirmwareImage *img = g_ptr_array_index (images, i); + if (FU_IS_BCM57XX_DICT_IMAGE (img)) { + fu_firmware_image_set_idx (img, 0x80 + dict_cnt); + dict_cnt++; + } + } + if (g_getenv ("FWUPD_BCM57XX_VERBOSE") != NULL) { + g_autofree gchar *str = fu_firmware_to_string (firmware); + g_debug ("proposed device firmware: %s", str); + } + + /* success */ + return g_steal_pointer (&firmware); +} + +static gboolean +fu_bcm57xx_device_write_firmware (FuDevice *device, + FuFirmware *firmware, + FwupdInstallFlags flags, + GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) blob_verify = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* build the images into one linear blob of the correct size */ + fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); + blob = fu_firmware_write (firmware, error); + if (blob == NULL) + return FALSE; + + /* hit hardware */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + chunks = fu_chunk_array_new_from_bytes (blob, 0x0, 0x0, FU_BCM57XX_BLOCK_SZ); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + if (!fu_bcm57xx_device_nvram_write (self, chk->address, + chk->data, chk->data_sz, + error)) + return FALSE; + fu_device_set_progress_full (device, i, chunks->len - 1); + } + + /* verify */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_VERIFY); + blob_verify = fu_bcm57xx_device_dump_firmware (device, error); + if (blob_verify == NULL) + return FALSE; + if (!fu_common_bytes_compare (blob, blob_verify, error)) + return FALSE; + + /* reset APE */ + return fu_device_activate (device, error); +} + +static gboolean +fu_bcm57xx_device_setup (FuDevice *device, GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device); + guint32 fwversion = 0; + + /* device is in recovery mode */ + if (self->ethtool_iface == NULL) { + g_autoptr(FuDeviceLocker) locker = NULL; + g_debug ("device in recovery mode, use alternate device"); + locker = fu_device_locker_new (FU_DEVICE (self->recovery), error); + if (locker == NULL) + return FALSE; + return fu_device_setup (FU_DEVICE (self->recovery), error); + } + + /* check the EEPROM size */ + if (!fu_bcm57xx_device_nvram_check (self, error)) + return FALSE; + + /* get NVRAM version */ + if (!fu_bcm57xx_device_nvram_read (self, BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERSION, + (guint8 *) &fwversion, sizeof(guint32), error)) + return FALSE; + if (fwversion != 0x0) { + g_autofree gchar *fwversion_str = NULL; + + /* this is only set on the OSS firmware */ + fwversion_str = fu_common_version_from_uint32 (GUINT32_FROM_BE(fwversion), + FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version_format (device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version (device, fwversion_str); + fu_device_set_version_raw (device, fwversion); + fu_device_set_branch (device, BCM_FW_BRANCH_OSS_FIRMWARE); + } else { + guint8 bufver[16] = { 0x0 }; + guint32 veraddr = 0; + g_autoptr(Bcm57xxVeritem) veritem = NULL; + + /* fall back to the string, e.g. '5719-v1.43' */ + if (!fu_bcm57xx_device_nvram_read (self, + BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERADDR, + (guint8 *) &veraddr, sizeof(guint32), error)) + return FALSE; + veraddr = GUINT32_FROM_BE(veraddr); + if (veraddr > BCM_PHYS_ADDR_DEFAULT) + veraddr -= BCM_PHYS_ADDR_DEFAULT; + if (!fu_bcm57xx_device_nvram_read (self, + BCM_NVRAM_STAGE1_BASE + veraddr, + bufver, sizeof(bufver), error)) + return FALSE; + veritem = fu_bcm57xx_veritem_new (bufver, sizeof(bufver)); + if (veritem != NULL) { + fu_device_set_version_format (device, veritem->verfmt); + fu_device_set_version (device, veritem->version); + fu_device_set_branch (device, veritem->branch); + } + } + + /* success */ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL); + return TRUE; +} + +static gboolean +fu_bcm57xx_device_open (FuDevice *device, GError **error) +{ +#ifdef HAVE_SOCKET_H + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device); + self->ethtool_fd = socket (AF_INET, SOCK_DGRAM, 0); + if (self->ethtool_fd < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to open socket: %s", +#ifdef HAVE_ERRNO_H + strerror (errno)); +#else + "unspecified error"); +#endif + return FALSE; + } + return TRUE; +#else + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "socket() not supported as sys/socket.h not available"); + return FALSE; +#endif +} + +static gboolean +fu_bcm57xx_device_close (FuDevice *device, GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device); + return g_close (self->ethtool_fd, error); +} + +static void +fu_bcm57xx_device_init (FuBcm57xxDevice *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NO_GUID_MATCHING); + fu_device_set_protocol (FU_DEVICE (self), "com.broadcom.bcm57xx"); + fu_device_add_icon (FU_DEVICE (self), "network-wired"); + + /* other values are set from a quirk */ + fu_device_set_firmware_size (FU_DEVICE (self), BCM_FIRMWARE_SIZE); + + /* used for recovery in case of ethtool failure and for APE reset */ + self->recovery = fu_bcm57xx_recovery_device_new (); +} + +static void +fu_bcm57xx_device_finalize (GObject *object) +{ + FuBcm57xxDevice *self= FU_BCM57XX_DEVICE (object); + g_free (self->ethtool_iface); + G_OBJECT_CLASS (fu_bcm57xx_device_parent_class)->finalize (object); +} + +static void +fu_bcm57xx_device_class_init (FuBcm57xxDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUdevDeviceClass *klass_udev_device = FU_UDEV_DEVICE_CLASS (klass); + object_class->finalize = fu_bcm57xx_device_finalize; + klass_device->prepare_firmware = fu_bcm57xx_device_prepare_firmware; + klass_device->setup = fu_bcm57xx_device_setup; + klass_device->reload = fu_bcm57xx_device_setup; + klass_device->open = fu_bcm57xx_device_open; + klass_device->close = fu_bcm57xx_device_close; + klass_device->activate = fu_bcm57xx_device_activate; + klass_device->write_firmware = fu_bcm57xx_device_write_firmware; + klass_device->read_firmware = fu_bcm57xx_device_read_firmware; + klass_device->dump_firmware = fu_bcm57xx_device_dump_firmware; + klass_udev_device->probe = fu_bcm57xx_device_probe; + klass_udev_device->to_string = fu_bcm57xx_device_to_string; +} diff --git a/plugins/bcm57xx/fu-bcm57xx-device.h b/plugins/bcm57xx/fu-bcm57xx-device.h new file mode 100644 index 000000000..cc93537df --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +#define FU_TYPE_BCM57XX_DEVICE (fu_bcm57xx_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuBcm57xxDevice, fu_bcm57xx_device, FU, BCM57XX_DEVICE, FuUdevDevice) diff --git a/plugins/bcm57xx/fu-bcm57xx-dict-image.c b/plugins/bcm57xx/fu-bcm57xx-dict-image.c new file mode 100644 index 000000000..c499bbb94 --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-dict-image.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-common.h" + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-dict-image.h" + +struct _FuBcm57xxDictImage { + FuFirmwareImage parent_instance; + guint8 target; + guint8 kind; +}; + +G_DEFINE_TYPE (FuBcm57xxDictImage, fu_bcm57xx_dict_image, FU_TYPE_FIRMWARE_IMAGE) + +static void +fu_bcm57xx_dict_image_to_string (FuFirmwareImage *image, guint idt, GString *str) +{ + FuBcm57xxDictImage *self = FU_BCM57XX_DICT_IMAGE (image); + if (self->target != 0xff) + fu_common_string_append_kx (str, idt, "Target", self->target); + if (self->kind != 0xff) + fu_common_string_append_kx (str, idt, "Kind", self->kind); +} + +static gboolean +fu_bcm57xx_dict_image_parse (FuFirmwareImage *image, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(GBytes) fw_nocrc = NULL; + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if (!fu_bcm57xx_verify_crc (fw, error)) + return FALSE; + } + fw_nocrc = g_bytes_new_from_bytes (fw, 0x0, g_bytes_get_size (fw) - sizeof(guint32)); + fu_firmware_image_set_bytes (image, fw_nocrc); + return TRUE; +} + +static GBytes * +fu_bcm57xx_dict_image_write (FuFirmwareImage *image, GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + guint32 crc; + g_autoptr(GByteArray) blob = NULL; + g_autoptr(GBytes) fw_nocrc = NULL; + + /* get the CRC-less data */ + fw_nocrc = fu_firmware_image_get_bytes (image); + if (fw_nocrc == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported"); + return NULL; + } + + /* add to a mutable buffer */ + buf = g_bytes_get_data (fw_nocrc, &bufsz); + blob = g_byte_array_sized_new (bufsz + sizeof(guint32)); + g_byte_array_append (blob, buf, bufsz); + + /* add CRC */ + crc = fu_bcm57xx_nvram_crc (buf, bufsz); + fu_byte_array_append_uint32 (blob, crc, G_BIG_ENDIAN); + return g_byte_array_free_to_bytes (g_steal_pointer (&blob)); +} + +static gboolean +fu_bcm57xx_dict_image_build (FuFirmwareImage *image, XbNode *n, GError **error) +{ + FuBcm57xxDictImage *self = FU_BCM57XX_DICT_IMAGE (image); + guint64 tmp; + + /* two simple properties */ + tmp = xb_node_query_text_as_uint (n, "kind", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + fu_bcm57xx_dict_image_set_kind (self, tmp); + tmp = xb_node_query_text_as_uint (n, "target", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + fu_bcm57xx_dict_image_set_target (self, tmp); + + /* success */ + return TRUE; +} + +static void +fu_bcm57xx_dict_image_ensure_id (FuBcm57xxDictImage *self) +{ + g_autofree gchar *id = NULL; + struct { + guint8 target; + guint8 kind; + const gchar *id; + } ids[] = { + { 0x00, 0x00, "pxe" }, + { 0x0D, 0x00, "ape" }, + { 0x09, 0x00, "iscsi1" }, + { 0x05, 0x00, "iscsi2" }, + { 0x0b, 0x00, "iscsi3" }, + { 0x00, 0x01, "cfg1000" }, + { 0x04, 0x01, "vpd2" }, + { 0xff, 0xff, NULL } + }; + if (self->target == 0xff || self->kind == 0xff) + return; + for (guint i = 0; ids[i].id != NULL; i++) { + if (self->target == ids[i].target && self->kind == ids[i].kind) { + g_debug ("using %s for %02x:%02x", + ids[i].id, self->target, self->kind); + fu_firmware_image_set_id (FU_FIRMWARE_IMAGE (self), ids[i].id); + return; + } + } + id = g_strdup_printf ("dict-%02x-%02x", self->target, self->kind); + g_warning ("falling back to %s, please report", id); + fu_firmware_image_set_id (FU_FIRMWARE_IMAGE (self), id); +} + +void +fu_bcm57xx_dict_image_set_target (FuBcm57xxDictImage *self, guint8 target) +{ + self->target = target; + fu_bcm57xx_dict_image_ensure_id (self); +} + +guint8 +fu_bcm57xx_dict_image_get_target (FuBcm57xxDictImage *self) +{ + return self->target; +} + +void +fu_bcm57xx_dict_image_set_kind (FuBcm57xxDictImage *self, guint8 kind) +{ + self->kind = kind; + fu_bcm57xx_dict_image_ensure_id (self); +} + +guint8 +fu_bcm57xx_dict_image_get_kind (FuBcm57xxDictImage *self) +{ + return self->kind; +} + +static void +fu_bcm57xx_dict_image_init (FuBcm57xxDictImage *self) +{ + self->target = 0xff; + self->kind = 0xff; +} + +static void +fu_bcm57xx_dict_image_class_init (FuBcm57xxDictImageClass *klass) +{ + FuFirmwareImageClass *klass_image = FU_FIRMWARE_IMAGE_CLASS (klass); + klass_image->parse = fu_bcm57xx_dict_image_parse; + klass_image->write = fu_bcm57xx_dict_image_write; + klass_image->build = fu_bcm57xx_dict_image_build; + klass_image->to_string = fu_bcm57xx_dict_image_to_string; +} + +FuFirmwareImage * +fu_bcm57xx_dict_image_new (void) +{ + return FU_FIRMWARE_IMAGE (g_object_new (FU_TYPE_BCM57XX_DICT_IMAGE, NULL)); +} diff --git a/plugins/bcm57xx/fu-bcm57xx-dict-image.h b/plugins/bcm57xx/fu-bcm57xx-dict-image.h new file mode 100644 index 000000000..5f376078a --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-dict-image.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware-image.h" + +#define FU_TYPE_BCM57XX_DICT_IMAGE (fu_bcm57xx_dict_image_get_type ()) +G_DECLARE_FINAL_TYPE (FuBcm57xxDictImage, fu_bcm57xx_dict_image, FU, BCM57XX_DICT_IMAGE, FuFirmwareImage) + +FuFirmwareImage *fu_bcm57xx_dict_image_new (void); +void fu_bcm57xx_dict_image_set_kind (FuBcm57xxDictImage *self, + guint8 kind); +guint8 fu_bcm57xx_dict_image_get_kind (FuBcm57xxDictImage *self); +void fu_bcm57xx_dict_image_set_target (FuBcm57xxDictImage *self, + guint8 target); +guint8 fu_bcm57xx_dict_image_get_target (FuBcm57xxDictImage *self); diff --git a/plugins/bcm57xx/fu-bcm57xx-firmware.c b/plugins/bcm57xx/fu-bcm57xx-firmware.c new file mode 100644 index 000000000..8e49baec8 --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-firmware.c @@ -0,0 +1,580 @@ +/* + * Copyright (C) 2018-2020 Evan Lojewski + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-common.h" + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-firmware.h" +#include "fu-bcm57xx-dict-image.h" +#include "fu-bcm57xx-stage1-image.h" +#include "fu-bcm57xx-stage2-image.h" + +struct _FuBcm57xxFirmware { + FuFirmware parent_instance; + guint16 vendor; + guint16 model; + gboolean is_backup; + guint32 phys_addr; + gsize source_size; + guint8 source_padchar; +}; + +G_DEFINE_TYPE (FuBcm57xxFirmware, fu_bcm57xx_firmware, FU_TYPE_FIRMWARE) + +#define BCM_STAGE1_HEADER_MAGIC_BROADCOM 0x0E000E03 +#define BCM_STAGE1_HEADER_MAGIC_MEKLORT 0x3C1D0800 + +#define BCM_APE_HEADER_MAGIC 0x1A4D4342 + +#define BCM_CODE_DIRECTORY_ADDR_APE 0x07 + +static void +fu_bcm57xx_firmware_to_string (FuFirmware *firmware, guint idt, GString *str) +{ + FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE (firmware); + fu_common_string_append_kx (str, idt, "Vendor", self->vendor); + fu_common_string_append_kx (str, idt, "Model", self->model); + fu_common_string_append_kb (str, idt, "IsBackup", self->is_backup); + fu_common_string_append_kx (str, idt, "PhysAddr", self->phys_addr); +} + +static gboolean +fu_bcm57xx_firmware_parse_header (FuBcm57xxFirmware *self, GBytes *fw, GError **error) +{ + gsize bufsz = 0x0; + const guint8 *buf = g_bytes_get_data (fw, &bufsz); + + /* verify magic and CRC */ + if (!fu_bcm57xx_verify_magic (fw, 0x0, error)) + return FALSE; + if (!fu_bcm57xx_verify_crc (fw, error)) + return FALSE; + + /* get address */ + return fu_common_read_uint32_safe (buf, bufsz, BCM_NVRAM_HEADER_PHYS_ADDR, + &self->phys_addr, G_BIG_ENDIAN, error); +} + +static FuFirmwareImage * +fu_bcm57xx_firmware_parse_info (FuBcm57xxFirmware *self, GBytes *fw, GError **error) +{ + gsize bufsz = 0x0; + guint32 mac_addr0 = 0; + const guint8 *buf = g_bytes_get_data (fw, &bufsz); + g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (fw); + + /* if the MAC is set non-zero this is an actual backup rather than a container */ + if (!fu_common_read_uint32_safe (buf, bufsz, BCM_NVRAM_INFO_MAC_ADDR0, + &mac_addr0, G_BIG_ENDIAN, error)) + return NULL; + self->is_backup = mac_addr0 != 0x0 && mac_addr0 != 0xffffffff; + + /* read vendor + model */ + if (!fu_common_read_uint16_safe (buf, bufsz, BCM_NVRAM_INFO_VENDOR, + &self->vendor, G_BIG_ENDIAN, error)) + return NULL; + if (!fu_common_read_uint16_safe (buf, bufsz, BCM_NVRAM_INFO_DEVICE, + &self->model, G_BIG_ENDIAN, error)) + return NULL; + + /* success */ + fu_firmware_image_set_id (img, "info"); + return g_steal_pointer (&img); +} + +static FuFirmwareImage * +fu_bcm57xx_firmware_parse_stage1 (FuBcm57xxFirmware *self, + GBytes *fw, + guint32 *out_stage1_sz, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0x0; + guint32 stage1_wrds = 0; + guint32 stage1_sz; + guint32 stage1_off = 0; + const guint8 *buf = g_bytes_get_data (fw, &bufsz); + g_autoptr(FuFirmwareImage) img = fu_bcm57xx_stage1_image_new (); + g_autoptr(GBytes) blob = NULL; + + if (!fu_common_read_uint32_safe (buf, bufsz, + BCM_NVRAM_HEADER_BASE + BCM_NVRAM_HEADER_SIZE_WRDS, + &stage1_wrds, G_BIG_ENDIAN, error)) + return NULL; + if (!fu_common_read_uint32_safe (buf, bufsz, + BCM_NVRAM_HEADER_BASE + BCM_NVRAM_HEADER_OFFSET, + &stage1_off, G_BIG_ENDIAN, error)) + return NULL; + stage1_sz = (stage1_wrds * sizeof(guint32)); + if (stage1_off != BCM_NVRAM_STAGE1_BASE) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "stage1 offset invalid, got: 0x%x, expected 0x%x", + (guint) stage1_sz, (guint) BCM_NVRAM_STAGE1_BASE); + return NULL; + } + if (stage1_off + stage1_sz > bufsz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "bigger than firmware, got: 0x%x @ 0x%x", + (guint) stage1_sz, (guint) stage1_off); + return NULL; + } + + /* verify CRC */ + blob = g_bytes_new_from_bytes (fw, stage1_off, stage1_sz); + if (!fu_firmware_image_parse (img, blob, flags, error)) + return NULL; + + /* needed for stage2 */ + if (out_stage1_sz != NULL) + *out_stage1_sz = stage1_sz; + + /* success */ + fu_firmware_image_set_id (img, "stage1"); + fu_firmware_image_set_offset (img, stage1_off); + return g_steal_pointer (&img); +} + +static FuFirmwareImage * +fu_bcm57xx_firmware_parse_stage2 (FuBcm57xxFirmware *self, + GBytes *fw, + guint32 stage1_sz, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0x0; + const guint8 *buf = g_bytes_get_data (fw, &bufsz); + guint32 stage2_off = 0; + guint32 stage2_sz = 0; + g_autoptr(FuFirmwareImage) img = fu_bcm57xx_stage2_image_new (); + g_autoptr(GBytes) blob = NULL; + + stage2_off = BCM_NVRAM_STAGE1_BASE + stage1_sz; + if (!fu_bcm57xx_verify_magic (fw, stage2_off, error)) + return NULL; + if (!fu_common_read_uint32_safe (buf, bufsz, stage2_off + sizeof(guint32), + &stage2_sz, G_BIG_ENDIAN, error)) + return NULL; + if (stage2_off + stage2_sz > bufsz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "bigger than firmware, got: 0x%x @ 0x%x", + (guint) stage2_sz, (guint) stage2_off); + return NULL; + } + + /* verify CRC */ + blob = g_bytes_new_from_bytes (fw, stage2_off + 0x8, stage2_sz); + if (!fu_firmware_image_parse (img, blob, flags, error)) + return NULL; + + /* success */ + fu_firmware_image_set_id (img, "stage2"); + fu_firmware_image_set_offset (img, stage2_off); + return g_steal_pointer (&img); +} + +static gboolean +fu_bcm57xx_firmware_parse_dict (FuBcm57xxFirmware *self, GBytes *fw, guint idx, + FwupdInstallFlags flags, GError **error) +{ + gsize bufsz = 0x0; + guint32 dict_addr = 0x0; + guint32 dict_info = 0x0; + guint32 dict_off = 0x0; + guint32 dict_sz; + guint32 base = BCM_NVRAM_DIRECTORY_BASE + (idx * BCM_NVRAM_DIRECTORY_SZ); + const guint8 *buf = g_bytes_get_data (fw, &bufsz); + g_autoptr(FuFirmwareImage) img = fu_bcm57xx_dict_image_new (); + g_autoptr(GBytes) blob = NULL; + + /* header */ + if (!fu_common_read_uint32_safe (buf, bufsz, + base + BCM_NVRAM_DIRECTORY_ADDR, + &dict_addr, G_BIG_ENDIAN, error)) + return FALSE; + if (!fu_common_read_uint32_safe (buf, bufsz, + base + BCM_NVRAM_DIRECTORY_SIZE_WRDS, + &dict_info, G_BIG_ENDIAN, error)) + return FALSE; + if (!fu_common_read_uint32_safe (buf, bufsz, + base + BCM_NVRAM_DIRECTORY_OFFSET, + &dict_off, G_BIG_ENDIAN, error)) + return FALSE; + + /* no dict stored */ + if (dict_addr == 0 && dict_info == 0 && dict_off == 0) + return TRUE; + + dict_sz = (dict_info & 0x00FFFFFF) * sizeof(guint32); /* implies that maximum size is 16 MB */ + fu_bcm57xx_dict_image_set_target (FU_BCM57XX_DICT_IMAGE (img), (dict_info & 0x0F000000) >> 24); + fu_bcm57xx_dict_image_set_kind (FU_BCM57XX_DICT_IMAGE (img), (dict_info & 0xF0000000) >> 28); + fu_firmware_image_set_addr (img, dict_addr); + fu_firmware_image_set_offset (img, dict_off); + fu_firmware_image_set_idx (img, 0x80 + idx); + + /* empty */ + if (dict_sz == 0) { + blob = g_bytes_new (NULL, 0); + fu_firmware_image_set_bytes (img, blob); + fu_firmware_add_image (FU_FIRMWARE (self), img); + return TRUE; + } + + /* check against image size */ + if (dict_off + dict_sz > bufsz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "bigger than firmware, got: 0x%x @ 0x%x", + (guint) dict_sz, (guint) dict_off); + return FALSE; + } + blob = g_bytes_new_from_bytes (fw, dict_off, dict_sz); + if (!fu_firmware_image_parse (img, blob, flags, error)) + return FALSE; + + /* success */ + fu_firmware_add_image (FU_FIRMWARE (self), img); + return TRUE; +} + +static gboolean +fu_bcm57xx_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE (firmware); + gsize bufsz = 0x0; + guint32 magic = 0; + guint32 stage1_sz = 0; + const guint8 *buf = g_bytes_get_data (fw, &bufsz); + g_autoptr(FuFirmwareImage) img_info2 = NULL; + g_autoptr(FuFirmwareImage) img_info = NULL; + g_autoptr(FuFirmwareImage) img_stage1 = NULL; + g_autoptr(FuFirmwareImage) img_stage2 = NULL; + g_autoptr(FuFirmwareImage) img_vpd = NULL; + g_autoptr(GBytes) blob_header = NULL; + g_autoptr(GBytes) blob_info2 = NULL; + g_autoptr(GBytes) blob_info = NULL; + g_autoptr(GBytes) blob_vpd = NULL; + + /* try to autodetect the file type */ + if (!fu_common_read_uint32_safe (buf, bufsz, 0x0, &magic, G_BIG_ENDIAN, error)) + return FALSE; + + /* standalone APE */ + if (magic == BCM_APE_HEADER_MAGIC) { + g_autoptr(FuFirmwareImage) img = fu_bcm57xx_dict_image_new (); + fu_bcm57xx_dict_image_set_target (FU_BCM57XX_DICT_IMAGE (img), 0xD); + fu_bcm57xx_dict_image_set_kind (FU_BCM57XX_DICT_IMAGE (img), 0x0); + fu_firmware_image_set_bytes (img, fw); + fu_firmware_image_set_addr (img, BCM_CODE_DIRECTORY_ADDR_APE); + fu_firmware_image_set_id (img, "ape"); + fu_firmware_add_image (firmware, img); + return TRUE; + } + + /* standalone stage1 */ + if (magic == BCM_STAGE1_HEADER_MAGIC_BROADCOM || + magic == BCM_STAGE1_HEADER_MAGIC_MEKLORT) { + img_stage1 = fu_firmware_image_new (fw); + fu_firmware_image_set_id (img_stage1, "stage1"); + fu_firmware_add_image (firmware, img_stage1); + return TRUE; + } + + /* not full NVRAM image */ + if (magic != BCM_NVRAM_MAGIC) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "file not supported, got: 0x%08X", + magic); + return FALSE; + } + + /* save the size so we can export the padding for a perfect roundtrip */ + self->source_size = bufsz; + self->source_padchar = buf[bufsz - 1]; + + /* NVRAM header */ + blob_header = g_bytes_new_from_bytes (fw, BCM_NVRAM_HEADER_BASE, BCM_NVRAM_HEADER_SZ); + if (!fu_bcm57xx_firmware_parse_header (self, blob_header, error)) { + g_prefix_error (error, "failed to parse header: "); + return FALSE; + } + + /* info */ + blob_info = g_bytes_new_from_bytes (fw, BCM_NVRAM_INFO_BASE, BCM_NVRAM_INFO_SZ); + img_info = fu_bcm57xx_firmware_parse_info (self, blob_info, error); + if (img_info == NULL) { + g_prefix_error (error, "failed to parse info: "); + return FALSE; + } + fu_firmware_image_set_offset (img_info, BCM_NVRAM_INFO_BASE); + fu_firmware_add_image (firmware, img_info); + + /* VPD */ + blob_vpd = g_bytes_new_from_bytes (fw, BCM_NVRAM_VPD_BASE, BCM_NVRAM_VPD_SZ); + img_vpd = fu_firmware_image_new (blob_vpd); + fu_firmware_image_set_id (img_vpd, "vpd"); + fu_firmware_image_set_offset (img_vpd, BCM_NVRAM_VPD_BASE); + fu_firmware_add_image (firmware, img_vpd); + + /* info2 */ + blob_info2 = g_bytes_new_from_bytes (fw, BCM_NVRAM_INFO2_BASE, BCM_NVRAM_INFO2_SZ); + img_info2 = fu_firmware_image_new (blob_info2); + fu_firmware_image_set_id (img_info2, "info2"); + fu_firmware_image_set_offset (img_info2, BCM_NVRAM_INFO2_BASE); + fu_firmware_add_image (firmware, img_info2); + + /* stage1 */ + img_stage1 = fu_bcm57xx_firmware_parse_stage1 (self, fw, &stage1_sz, flags, error); + if (img_stage1 == NULL) { + g_prefix_error (error, "failed to parse stage1: "); + return FALSE; + } + fu_firmware_add_image (firmware, img_stage1); + + /* stage2 */ + img_stage2 = fu_bcm57xx_firmware_parse_stage2 (self, fw, stage1_sz, flags, error); + if (img_stage2 == NULL) { + g_prefix_error (error, "failed to parse stage2: "); + return FALSE; + } + fu_firmware_add_image (firmware, img_stage2); + + /* dictionaries, e.g. APE */ + for (guint i = 0; i < 8; i++) { + g_autoptr(FuFirmwareImage) img = NULL; + if (!fu_bcm57xx_firmware_parse_dict (self, fw, i, flags, error)) { + g_prefix_error (error, "failed to parse dict 0x%x: ", i); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static void +_g_byte_array_append_bytes (GByteArray *buf, GBytes *bytes) +{ + g_byte_array_append (buf, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)); +} + +static GBytes * +_g_bytes_new_sized (gsize sz) +{ + GByteArray *tmp = g_byte_array_sized_new (sz); + for (gsize i = 0; i < sz; i++) + fu_byte_array_append_uint8 (tmp, 0x0); + return g_byte_array_free_to_bytes (tmp); +} + +static gboolean +fu_bcm57xx_firmware_build (FuFirmware *firmware, XbNode *n, GError **error) +{ + FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE (firmware); + guint64 tmp; + + /* two simple properties */ + tmp = xb_node_query_text_as_uint (n, "vendor", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + self->vendor = tmp; + tmp = xb_node_query_text_as_uint (n, "model", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + self->model = tmp; + + /* success */ + return TRUE; +} + +static GBytes * +fu_bcm57xx_firmware_write (FuFirmware *firmware, GError **error) +{ + gsize off = BCM_NVRAM_STAGE1_BASE; + FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE (firmware); + g_autoptr(GByteArray) buf = g_byte_array_sized_new (self->source_size); + g_autoptr(FuFirmwareImage) img_info2 = NULL; + g_autoptr(FuFirmwareImage) img_info = NULL; + g_autoptr(FuFirmwareImage) img_stage1 = NULL; + g_autoptr(FuFirmwareImage) img_stage2 = NULL; + g_autoptr(FuFirmwareImage) img_vpd = NULL; + g_autoptr(GBytes) blob_info2 = NULL; + g_autoptr(GBytes) blob_info = NULL; + g_autoptr(GBytes) blob_stage1 = NULL; + g_autoptr(GBytes) blob_stage2 = NULL; + g_autoptr(GBytes) blob_vpd = NULL; + g_autoptr(GPtrArray) blob_dicts = NULL; + + /* write out the things we need to pre-compute */ + img_stage1 = fu_firmware_get_image_by_id (firmware, "stage1", error); + if (img_stage1 == NULL) + return NULL; + blob_stage1 = fu_firmware_image_write (img_stage1, error); + if (blob_stage1 == NULL) + return NULL; + off += g_bytes_get_size (blob_stage1); + img_stage2 = fu_firmware_get_image_by_id (firmware, "stage2", error); + if (img_stage2 == NULL) + return NULL; + blob_stage2 = fu_firmware_image_write (img_stage2, error); + if (blob_stage2 == NULL) + return NULL; + off += g_bytes_get_size (blob_stage2); + + /* add header */ + fu_byte_array_append_uint32 (buf, BCM_NVRAM_MAGIC, G_BIG_ENDIAN); + fu_byte_array_append_uint32 (buf, self->phys_addr, G_BIG_ENDIAN); + fu_byte_array_append_uint32 (buf, g_bytes_get_size (blob_stage1) / sizeof(guint32), G_BIG_ENDIAN); + fu_byte_array_append_uint32 (buf, BCM_NVRAM_STAGE1_BASE, G_BIG_ENDIAN); + fu_byte_array_append_uint32 (buf, fu_bcm57xx_nvram_crc (buf->data, buf->len), G_BIG_ENDIAN); + + /* add directory entries */ + blob_dicts = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref); + for (guint i = 0; i < 8; i++) { + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(GBytes) blob = NULL; + + img = fu_firmware_get_image_by_idx (firmware, 0x80 + i, NULL); + if (img != NULL) { + blob = fu_firmware_image_write (img, error); + if (blob == NULL) + return NULL; + } + if (blob != NULL) { + fu_byte_array_append_uint32 (buf, fu_firmware_image_get_addr (img), G_BIG_ENDIAN); + fu_byte_array_append_uint32 (buf, + (g_bytes_get_size (blob) / sizeof(guint32)) | + (guint32) fu_bcm57xx_dict_image_get_target (FU_BCM57XX_DICT_IMAGE (img)) << 24 | + (guint32) fu_bcm57xx_dict_image_get_kind (FU_BCM57XX_DICT_IMAGE (img)) << 28, + G_BIG_ENDIAN); + if (g_bytes_get_size (blob) > 0) { + fu_byte_array_append_uint32 (buf, off, G_BIG_ENDIAN); + off += g_bytes_get_size (blob); + } else { + fu_byte_array_append_uint32 (buf, 0x0, G_BIG_ENDIAN); + } + } else { + blob = g_bytes_new (NULL, 0); + for (guint32 j = 0; j < sizeof(guint32) * 3; j++) + fu_byte_array_append_uint8 (buf, 0x0); + } + g_ptr_array_add (blob_dicts, g_steal_pointer (&blob)); + } + + /* add info */ + img_info = fu_firmware_get_image_by_id (firmware, "info", NULL); + if (img_info != NULL) { + blob_info = fu_firmware_image_write (img_info, error); + if (blob_info == NULL) + return NULL; + } else { + GByteArray *tmp = g_byte_array_sized_new (BCM_NVRAM_INFO_SZ); + for (gsize i = 0; i < BCM_NVRAM_INFO_SZ; i++) + fu_byte_array_append_uint8 (tmp, 0x0); + fu_common_write_uint16 (tmp->data + BCM_NVRAM_INFO_VENDOR, + self->vendor, G_BIG_ENDIAN); + fu_common_write_uint16 (tmp->data + BCM_NVRAM_INFO_DEVICE, + self->model, G_BIG_ENDIAN); + blob_info = g_byte_array_free_to_bytes (tmp); + } + _g_byte_array_append_bytes (buf, blob_info); + + /* add vpd */ + img_vpd = fu_firmware_get_image_by_id (firmware, "vpd", NULL); + if (img_vpd != NULL) { + blob_vpd = fu_firmware_image_write (img_vpd, error); + if (blob_vpd == NULL) + return NULL; + } else { + blob_vpd = _g_bytes_new_sized (BCM_NVRAM_VPD_SZ); + } + _g_byte_array_append_bytes (buf, blob_vpd); + + /* add info2 */ + img_info2 = fu_firmware_get_image_by_id (firmware, "info2", NULL); + if (img_info2 != NULL) { + blob_info2 = fu_firmware_image_write (img_info2, error); + if (blob_info2 == NULL) + return NULL; + } else { + blob_info2 = _g_bytes_new_sized (BCM_NVRAM_INFO2_SZ); + } + _g_byte_array_append_bytes (buf, blob_info2); + + /* add stage1+2 */ + _g_byte_array_append_bytes (buf, blob_stage1); + _g_byte_array_append_bytes (buf, blob_stage2); + + /* add dictionaries, e.g. APE */ + for (guint i = 0; i < blob_dicts->len; i++) { + GBytes *blob = g_ptr_array_index (blob_dicts, i); + _g_byte_array_append_bytes (buf, blob); + } + + /* pad until full */ + for (guint32 i = buf->len; i < self->source_size; i++) + fu_byte_array_append_uint8 (buf, self->source_padchar); + + /* add EOF */ + return g_byte_array_free_to_bytes (g_steal_pointer (&buf)); +} + +guint16 +fu_bcm57xx_firmware_get_vendor (FuBcm57xxFirmware *self) +{ + return self->vendor; +} + +guint16 +fu_bcm57xx_firmware_get_model (FuBcm57xxFirmware *self) +{ + return self->model; +} + +gboolean +fu_bcm57xx_firmware_is_backup (FuBcm57xxFirmware *self) +{ + return self->is_backup; +} + +static void +fu_bcm57xx_firmware_init (FuBcm57xxFirmware *self) +{ + self->phys_addr = BCM_PHYS_ADDR_DEFAULT; + self->source_size = BCM_FIRMWARE_SIZE; + self->source_padchar = 0xff; + fu_firmware_add_flag (FU_FIRMWARE (self), FU_FIRMWARE_FLAG_DEDUPE_ID); +} + +static void +fu_bcm57xx_firmware_class_init (FuBcm57xxFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_bcm57xx_firmware_parse; + klass_firmware->to_string = fu_bcm57xx_firmware_to_string; + klass_firmware->write = fu_bcm57xx_firmware_write; + klass_firmware->build = fu_bcm57xx_firmware_build; +} + +FuFirmware * +fu_bcm57xx_firmware_new (void) +{ + return FU_FIRMWARE (g_object_new (FU_TYPE_BCM57XX_FIRMWARE, NULL)); +} diff --git a/plugins/bcm57xx/fu-bcm57xx-firmware.h b/plugins/bcm57xx/fu-bcm57xx-firmware.h new file mode 100644 index 000000000..59aafc424 --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-firmware.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_BCM57XX_FIRMWARE (fu_bcm57xx_firmware_get_type ()) +G_DECLARE_FINAL_TYPE (FuBcm57xxFirmware, fu_bcm57xx_firmware, FU, BCM57XX_FIRMWARE, FuFirmware) + +FuFirmware *fu_bcm57xx_firmware_new (void); +guint16 fu_bcm57xx_firmware_get_vendor (FuBcm57xxFirmware *self); +guint16 fu_bcm57xx_firmware_get_model (FuBcm57xxFirmware *self); +gboolean fu_bcm57xx_firmware_is_backup (FuBcm57xxFirmware *self); diff --git a/plugins/bcm57xx/fu-bcm57xx-recovery-device.c b/plugins/bcm57xx/fu-bcm57xx-recovery-device.c new file mode 100644 index 000000000..a2e4d12f2 --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-recovery-device.c @@ -0,0 +1,769 @@ +/* + * Copyright (C) 2018-2020 Evan Lojewski + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: GPL-2+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_MMAN_H +#include +#endif + +#ifdef HAVE_VALGRIND +#include +#endif /* HAVE_VALGRIND */ + +#include "fu-common.h" + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-recovery-device.h" +#include "fu-bcm57xx-firmware.h" + +/* offsets into BAR[0] */ +#define REG_DEVICE_PCI_VENDOR_DEVICE_ID 0x6434 +#define REG_NVM_SOFTWARE_ARBITRATION 0x7020 +#define REG_NVM_ACCESS 0x7024 +#define REG_NVM_COMMAND 0x7000 +#define REG_NVM_ADDR 0x700c +#define REG_NVM_READ 0x7010 +#define REG_NVM_WRITE 0x7008 + +/* offsets into BAR[2] */ +#define REG_APE_MODE 0x0 + +typedef struct { + guint8 *buf; + gsize bufsz; +} FuBcm57xxMmap; + +#define FU_BCM57XX_BAR_DEVICE 0 +#define FU_BCM57XX_BAR_APE 1 +#define FU_BCM57XX_BAR_MAX 3 + +struct _FuBcm57xxRecoveryDevice { + FuUdevDevice parent_instance; + FuBcm57xxMmap bar[FU_BCM57XX_BAR_MAX]; +}; + +typedef union { + guint32 r32; + struct { + guint32 reserved_0_0 : 1; + guint32 Reset : 1; + guint32 reserved_2_2 : 1; + guint32 Done : 1; + guint32 Doit : 1; + guint32 Wr : 1; + guint32 Erase : 1; + guint32 First : 1; + guint32 Last : 1; + guint32 reserved_15_9 : 7; + guint32 WriteEnableCommand : 1; + guint32 WriteDisableCommand : 1; + guint32 reserved_31_18 : 14; + } __attribute__((packed)) bits; +} BcmRegNVMCommand; + +typedef union { + guint32 r32; + struct { + guint32 ReqSet0 : 1; + guint32 ReqSet1 : 1; + guint32 ReqSet2 : 1; + guint32 ReqSet3 : 1; + guint32 ReqClr0 : 1; + guint32 ReqClr1 : 1; + guint32 ReqClr2 : 1; + guint32 ReqClr3 : 1; + guint32 ArbWon0 : 1; + guint32 ArbWon1 : 1; + guint32 ArbWon2 : 1; + guint32 ArbWon3 : 1; + guint32 Req0 : 1; + guint32 Req1 : 1; + guint32 Req2 : 1; + guint32 Req3 : 1; + guint32 reserved_31_16 : 16; + } __attribute__((packed)) bits; +} BcmRegNVMSoftwareArbitration; + +typedef union { + guint32 r32; + struct { + guint32 Enable : 1; + guint32 WriteEnable : 1; + guint32 reserved_31_2 : 30; + } __attribute__((packed)) bits; +} BcmRegNVMAccess; + +typedef union { + guint32 r32; + struct { + guint32 Reset : 1; + guint32 Halt : 1; + guint32 FastBoot : 1; + guint32 HostDiag : 1; + guint32 reserved_4_4 : 1; + guint32 Event1 : 1; + guint32 Event2 : 1; + guint32 GRCint : 1; + guint32 reserved_8_8 : 1; + guint32 SwapATBdword : 1; + guint32 reserved_10_10 : 1; + guint32 SwapARBdword : 1; + guint32 reserved_13_12 : 2; + guint32 Channel0Enable : 1; + guint32 Channel2Enable : 1; + guint32 reserved_17_16 : 2; + guint32 MemoryECC : 1; + guint32 ICodePIPRdDisable : 1; + guint32 reserved_29_20 : 10; + guint32 Channel1Enable : 1; + guint32 Channel3Enable : 1; + } __attribute__((packed)) bits; +} BcmRegAPEMode; + +G_DEFINE_TYPE (FuBcm57xxRecoveryDevice, fu_bcm57xx_recovery_device, FU_TYPE_UDEV_DEVICE) + +#ifdef __ppc64__ +#define BARRIER() __asm__ volatile ("sync 0\neieio\n" : : : "memory") +#else +#define BARRIER() __asm__ volatile ("" : : : "memory"); +#endif + +static gboolean +fu_bcm57xx_recovery_device_bar_read (FuBcm57xxRecoveryDevice *self, + guint bar, gsize offset, guint32 *val, + GError **error) +{ + guint8 *base = self->bar[bar].buf + offset; + + /* this should never happen */ + if (self->bar[bar].buf == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "BAR[%u] is not mapped!", bar); + return FALSE; + } + + BARRIER(); + *val = *(guint32 *)base; + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_bar_write (FuBcm57xxRecoveryDevice *self, + guint bar, gsize offset, guint32 val, + GError **error) +{ + guint8 *base = self->bar[bar].buf + offset; + + /* this should never happen */ + if (self->bar[bar].buf == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "BAR[%u] is not mapped!", bar); + return FALSE; + } + + BARRIER(); + *(guint32 *)base = val; + BARRIER(); + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_disable (FuBcm57xxRecoveryDevice *self, + GError **error) +{ + BcmRegNVMAccess tmp; + if (!fu_bcm57xx_recovery_device_bar_read (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, &tmp.r32, error)) + return FALSE; + tmp.bits.Enable = FALSE; + tmp.bits.WriteEnable = FALSE; + return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, tmp.r32, error); +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_enable (FuBcm57xxRecoveryDevice *self, + GError **error) +{ + BcmRegNVMAccess tmp; + if (!fu_bcm57xx_recovery_device_bar_read (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, &tmp.r32, error)) + return FALSE; + tmp.bits.Enable = TRUE; + tmp.bits.WriteEnable = FALSE; + return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, tmp.r32, error); +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_enable_write (FuBcm57xxRecoveryDevice *self, + GError **error) +{ + BcmRegNVMAccess tmp; + if (!fu_bcm57xx_recovery_device_bar_read (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, &tmp.r32, error)) + return FALSE; + tmp.bits.Enable = TRUE; + tmp.bits.WriteEnable = TRUE; + return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, tmp.r32, error); +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_acquire_lock (FuBcm57xxRecoveryDevice *self, + GError **error) +{ + BcmRegNVMSoftwareArbitration tmp = { 0 }; + g_autoptr(GTimer) timer = g_timer_new (); + tmp.bits.ReqSet1 = 1; + if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_SOFTWARE_ARBITRATION, + tmp.r32, error)) + return FALSE; + do { + if (!fu_bcm57xx_recovery_device_bar_read (self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_SOFTWARE_ARBITRATION, + &tmp.r32, error)) + return FALSE; + if (tmp.bits.ArbWon1) + return TRUE; + if (g_timer_elapsed (timer, NULL) > 0.2) + break; + } while (TRUE); + + /* timed out */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "timed out trying to acquire lock #1"); + return FALSE; +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_release_lock (FuBcm57xxRecoveryDevice *self, + GError **error) +{ + BcmRegNVMSoftwareArbitration tmp = { 0 }; + tmp.r32 = 0; + tmp.bits.ReqClr1 = 1; + return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_SOFTWARE_ARBITRATION, tmp.r32, + error); +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_wait_done (FuBcm57xxRecoveryDevice *self, GError **error) +{ + BcmRegNVMCommand tmp = { 0 }; + g_autoptr(GTimer) timer = g_timer_new (); + do { + if (!fu_bcm57xx_recovery_device_bar_read (self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_COMMAND, &tmp.r32, + error)) + return FALSE; + if (tmp.bits.Done) + return TRUE; + if (g_timer_elapsed (timer, NULL) > 0.2) + break; + } while (TRUE); + + /* timed out */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "timed out"); + return FALSE; +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_clear_done (FuBcm57xxRecoveryDevice *self, GError **error) +{ + BcmRegNVMCommand tmp = { 0 }; + tmp.bits.Done = 1; + return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_COMMAND, tmp.r32, error); +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_read (FuBcm57xxRecoveryDevice *self, + guint32 address, guint32 *buf, gsize bufsz, + GError **error) +{ + for (guint i = 0; i < bufsz; i++) { + BcmRegNVMCommand tmp = { 0 }; + guint32 val32 = 0; + if (!fu_bcm57xx_recovery_device_nvram_clear_done (self, error)) + return FALSE; + if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_ADDR, address, error)) + return FALSE; + tmp.bits.Doit = 1; + tmp.bits.First = i == 0; + tmp.bits.Last = i == bufsz - 1; + + if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_COMMAND, tmp.r32, error)) + return FALSE; + if (!fu_bcm57xx_recovery_device_nvram_wait_done (self, error)) { + g_prefix_error (error, "failed to read @0x%x: ", address); + return FALSE; + } + if (!fu_bcm57xx_recovery_device_bar_read (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_READ, &val32, error)) + return FALSE; + buf[i] = GUINT32_FROM_BE(val32); + address += sizeof(guint32); + fu_device_set_progress_full (FU_DEVICE (self), i, bufsz); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_write (FuBcm57xxRecoveryDevice *self, + guint32 address, const guint32 *buf, gsize bufsz_dwrds, + GError **error) +{ + const guint32 page_size_dwrds = 64; + + /* can only write in pages of 64 dwords */ + if (bufsz_dwrds % page_size_dwrds != 0 || + (address * sizeof(guint32)) % page_size_dwrds != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "can only write aligned with page size 0x%x", + page_size_dwrds); + return FALSE; + } + + for (guint i = 0; i < bufsz_dwrds; i++) { + BcmRegNVMCommand tmp = { 0 }; + if (!fu_bcm57xx_recovery_device_nvram_clear_done (self, error)) + return FALSE; + if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_WRITE, GUINT32_TO_BE(buf[i]), error)) + return FALSE; + if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_ADDR, address, error)) + return FALSE; + tmp.bits.Wr = TRUE; + tmp.bits.Doit = TRUE; + tmp.bits.First = i % page_size_dwrds == 0; + tmp.bits.Last = (i + 1) % page_size_dwrds == 0; + if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE, + REG_NVM_COMMAND, tmp.r32, error)) + return FALSE; + if (!fu_bcm57xx_recovery_device_nvram_wait_done (self, error)) { + g_prefix_error (error, "failed to write @0x%x: ", address); + return FALSE; + } + address += sizeof(guint32); + fu_device_set_progress_full (FU_DEVICE (self), i, bufsz_dwrds); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_detach (FuDevice *device, GError **error) +{ + /* unbind tg3 */ + return fu_device_unbind_driver (device, error); +} + +static gboolean +fu_bcm57xx_recovery_device_attach (FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* bind tg3, which might fail if the module is not compiled */ + if (!fu_device_bind_driver (device, "pci", "tg3", &error_local)) { + if (g_error_matches (error_local, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED)) { + g_warning ("failed to bind tg3: %s", error_local->message); + } else { + g_propagate_prefixed_error (error, + g_steal_pointer (&error_local), + "failed to bind tg3: "); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_activate (FuDevice *device, GError **error) +{ + BcmRegAPEMode mode = { 0 }; + FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE (device); + + /* halt */ + mode.bits.Halt = 1; + mode.bits.FastBoot = 0; + if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_APE, + REG_APE_MODE, mode.r32, error)) + return FALSE; + + /* boot */ + mode.bits.Halt = 0; + mode.bits.FastBoot = 0; + mode.bits.Reset = 1; + return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_APE, + REG_APE_MODE, mode.r32, error); +} + +static GBytes * +fu_bcm57xx_recovery_device_dump_firmware (FuDevice *device, GError **error) +{ + FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE (device); + gsize bufsz_dwrds = fu_device_get_firmware_size_max (FU_DEVICE (self)) / sizeof(guint32); + g_autofree guint32 *buf_dwrds = g_new0 (guint32, bufsz_dwrds); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDeviceLocker) locker2 = NULL; + + /* read from hardware */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ); + locker = fu_device_locker_new_full (self, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_acquire_lock, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_release_lock, + error); + if (locker == NULL) + return NULL; + locker2 = fu_device_locker_new_full (self, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_enable, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_disable, + error); + if (locker2 == NULL) + return NULL; + if (!fu_bcm57xx_recovery_device_nvram_read (self, 0x0, buf_dwrds, bufsz_dwrds, error)) + return NULL; + if (!fu_device_locker_close (locker2, error)) + return NULL; + return g_bytes_new (buf_dwrds, bufsz_dwrds * sizeof(guint32)); +} + +static FuFirmware * +fu_bcm57xx_recovery_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) firmware_bin = fu_firmware_new (); + g_autoptr(FuFirmware) firmware_tmp = fu_bcm57xx_firmware_new (); + + /* check is a NVRAM backup */ + if (!fu_firmware_parse (firmware_tmp, fw, flags, error)) { + g_prefix_error (error, "failed to parse new firmware: "); + return NULL; + } + if (!fu_bcm57xx_firmware_is_backup (FU_BCM57XX_FIRMWARE (firmware_tmp))) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "can only recover with backup firmware"); + return NULL; + } + if (!fu_firmware_parse (firmware_bin, fw, flags, error)) + return NULL; + return g_steal_pointer (&firmware_bin); +} + +static gboolean +fu_bcm57xx_recovery_device_write_firmware (FuDevice *device, + FuFirmware *firmware, + FwupdInstallFlags flags, + GError **error) +{ + FuBcm57xxRecoveryDevice *self= FU_BCM57XX_RECOVERY_DEVICE (device); + const guint8 *buf; + gsize bufsz = 0; + gsize bufsz_dwrds; + g_autofree guint32 *buf_dwrds = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDeviceLocker) locker2 = NULL; + g_autoptr(GBytes) blob = NULL; + + /* build the images into one linear blob of the correct size */ + fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); + blob = fu_firmware_write (firmware, error); + if (blob == NULL) + return FALSE; + + /* align into uint32_t buffer */ + buf = g_bytes_get_data (blob, &bufsz); + bufsz_dwrds = bufsz / sizeof(guint32); + buf_dwrds = g_new0 (guint32, bufsz_dwrds); + if (!fu_memcpy_safe ((guint8 *) buf_dwrds, bufsz_dwrds * sizeof(guint32), 0x0, /* dst */ + buf, bufsz, 0x0, /* src */ + bufsz, error)) + return FALSE; + + /* hit hardware */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + locker = fu_device_locker_new_full (self, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_acquire_lock, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_release_lock, + error); + if (locker == NULL) + return FALSE; + locker2 = fu_device_locker_new_full (self, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_enable_write, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_disable, + error); + if (locker2 == NULL) + return FALSE; + if (!fu_bcm57xx_recovery_device_nvram_write (self, 0x0, buf_dwrds, bufsz_dwrds, error)) + return FALSE; + if (!fu_device_locker_close (locker2, error)) + return FALSE; + if (!fu_device_locker_close (locker, error)) + return FALSE; + + /* reset APE */ + return fu_device_activate (device, error); +} + +static gboolean +fu_bcm57xx_recovery_device_setup (FuDevice *device, GError **error) +{ + FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE (device); + guint32 fwversion = 0; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDeviceLocker) locker2 = NULL; + + locker = fu_device_locker_new_full (self, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_acquire_lock, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_release_lock, + error); + if (locker == NULL) + return FALSE; + locker2 = fu_device_locker_new_full (self, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_enable, + (FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_disable, + error); + if (locker2 == NULL) + return FALSE; + + /* get NVRAM version */ + if (!fu_bcm57xx_recovery_device_nvram_read (self, BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERSION, + &fwversion, 1, error)) + return FALSE; + if (fwversion != 0x0) { + g_autofree gchar *fwversion_str = NULL; + + /* this is only set on the OSS firmware */ + fwversion_str = fu_common_version_from_uint32 (GUINT32_FROM_BE(fwversion), + FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version_format (device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version (device, fwversion_str); + fu_device_set_version_raw (device, fwversion); + fu_device_set_branch (device, BCM_FW_BRANCH_OSS_FIRMWARE); + } else { + guint32 bufver[4] = { 0x0 }; + guint32 veraddr = 0; + g_autoptr(Bcm57xxVeritem) veritem = NULL; + + /* fall back to the string, e.g. '5719-v1.43' */ + if (!fu_bcm57xx_recovery_device_nvram_read (self, + BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERADDR, + &veraddr, 1, error)) + return FALSE; + veraddr = GUINT32_FROM_BE(veraddr); + if (veraddr > BCM_PHYS_ADDR_DEFAULT) + veraddr -= BCM_PHYS_ADDR_DEFAULT; + if (!fu_bcm57xx_recovery_device_nvram_read (self, + BCM_NVRAM_STAGE1_BASE + veraddr, + bufver, 4, error)) + return FALSE; + veritem = fu_bcm57xx_veritem_new ((guint8 *) bufver, sizeof(bufver)); + if (veritem != NULL) { + fu_device_set_version (device, veritem->version); + fu_device_set_branch (device, veritem->branch); + fu_device_set_version_format (device, veritem->verfmt); + } + } + + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_open (FuDevice *device, GError **error) +{ +#ifdef HAVE_MMAN_H + FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE (device); + FuUdevDevice *udev_device = FU_UDEV_DEVICE (device); + const gchar *sysfs_path = fu_udev_device_get_sysfs_path (udev_device); +#endif + +#ifdef RUNNING_ON_VALGRIND + /* this can't work */ + if (RUNNING_ON_VALGRIND) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "cannot mmap'ing BARs when using valgrind"); + return FALSE; + } +#endif + +#ifdef HAVE_MMAN_H + /* map BARs */ + for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) { + int memfd; + struct stat st; + g_autofree gchar *fn = NULL; + g_autofree gchar *resfn = NULL; + + /* open 64 bit resource */ + resfn = g_strdup_printf ("resource%u", i * 2); + fn = g_build_filename (sysfs_path, resfn, NULL); + memfd = open (fn, O_RDWR | O_SYNC); + if (memfd < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "error opening %s", fn); + return FALSE; + } + if (fstat (memfd, &st) < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "could not stat %s", fn); + close (memfd); + return FALSE; + } + + /* mmap */ + if (g_getenv ("FWUPD_BCM57XX_VERBOSE") != NULL) + g_debug ("mapping BAR[%u] %s for 0x%x bytes", i, fn, (guint) st.st_size); + self->bar[i].buf = (guint8 *) mmap (0, st.st_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, memfd, 0); + self->bar[i].bufsz = st.st_size; + close (memfd); + if (self->bar[i].buf == MAP_FAILED) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "could not mmap %s: %s", + fn, strerror(errno)); + return FALSE; + } + } + + /* success */ + return TRUE; +#else + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "mmap() not supported as sys/mman.h not available"); + return FALSE; +#endif +} + +static gboolean +fu_bcm57xx_recovery_device_close (FuDevice *device, GError **error) +{ +#ifdef HAVE_MMAN_H + FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE (device); + + /* unmap BARs */ + for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) { + if (self->bar[i].buf == NULL) + continue; + if (g_getenv ("FWUPD_BCM57XX_VERBOSE") != NULL) + g_debug ("unmapping BAR[%u]", i); + munmap (self->bar[i].buf, self->bar[i].bufsz); + self->bar[i].buf = NULL; + self->bar[i].bufsz = 0; + } + + /* success */ + return TRUE; +#else + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "munmap() not supported as sys/mman.h not available"); + return FALSE; +#endif +} + +static void +fu_bcm57xx_recovery_device_init (FuBcm57xxRecoveryDevice *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NO_GUID_MATCHING); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IGNORE_VALIDATION); + fu_device_set_protocol (FU_DEVICE (self), "com.broadcom.bcm57xx"); + fu_device_add_icon (FU_DEVICE (self), "network-wired"); + fu_device_set_logical_id (FU_DEVICE (self), "recovery"); + + /* other values are set from a quirk */ + fu_device_set_firmware_size (FU_DEVICE (self), BCM_FIRMWARE_SIZE); + + /* no BARs mapped */ + for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) { + self->bar[i].buf = NULL; + self->bar[i].bufsz = 0; + } +} + +static gboolean +fu_bcm57xx_recovery_device_probe (FuUdevDevice *device, GError **error) +{ + /* success */ + return fu_udev_device_set_physical_id (device, "pci", error); +} + +static void +fu_bcm57xx_recovery_device_class_init (FuBcm57xxRecoveryDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUdevDeviceClass *klass_udev_device = FU_UDEV_DEVICE_CLASS (klass); + klass_device->activate = fu_bcm57xx_recovery_device_activate; + klass_device->prepare_firmware = fu_bcm57xx_recovery_device_prepare_firmware; + klass_device->setup = fu_bcm57xx_recovery_device_setup; + klass_device->reload = fu_bcm57xx_recovery_device_setup; + klass_device->open = fu_bcm57xx_recovery_device_open; + klass_device->close = fu_bcm57xx_recovery_device_close; + klass_device->write_firmware = fu_bcm57xx_recovery_device_write_firmware; + klass_device->dump_firmware = fu_bcm57xx_recovery_device_dump_firmware; + klass_device->attach = fu_bcm57xx_recovery_device_attach; + klass_device->detach = fu_bcm57xx_recovery_device_detach; + klass_udev_device->probe = fu_bcm57xx_recovery_device_probe; +} + +FuBcm57xxRecoveryDevice * +fu_bcm57xx_recovery_device_new (void) +{ + FuUdevDevice *self = g_object_new (FU_TYPE_BCM57XX_RECOVERY_DEVICE, NULL); + return FU_BCM57XX_RECOVERY_DEVICE (self); +} diff --git a/plugins/bcm57xx/fu-bcm57xx-recovery-device.h b/plugins/bcm57xx/fu-bcm57xx-recovery-device.h new file mode 100644 index 000000000..a78c5b511 --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-recovery-device.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +#define FU_TYPE_BCM57XX_RECOVERY_DEVICE (fu_bcm57xx_recovery_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuBcm57xxRecoveryDevice, fu_bcm57xx_recovery_device, FU, BCM57XX_RECOVERY_DEVICE, FuUdevDevice) + +FuBcm57xxRecoveryDevice *fu_bcm57xx_recovery_device_new (void); diff --git a/plugins/bcm57xx/fu-bcm57xx-stage1-image.c b/plugins/bcm57xx/fu-bcm57xx-stage1-image.c new file mode 100644 index 000000000..93d5a1268 --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-stage1-image.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-common.h" +#include "fu-common-version.h" + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-stage1-image.h" + +struct _FuBcm57xxStage1Image { + FuFirmwareImage parent_instance; +}; + +G_DEFINE_TYPE (FuBcm57xxStage1Image, fu_bcm57xx_stage1_image, FU_TYPE_FIRMWARE_IMAGE) + +static gboolean +fu_bcm57xx_stage1_image_parse (FuFirmwareImage *image, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0x0; + guint32 fwversion = 0; + const guint8 *buf = g_bytes_get_data (fw, &bufsz); + g_autoptr(GBytes) fw_nocrc = NULL; + + /* verify CRC */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if (!fu_bcm57xx_verify_crc (fw, error)) + return FALSE; + } + + /* get version number */ + if (!fu_common_read_uint32_safe (buf, bufsz, BCM_NVRAM_STAGE1_VERSION, + &fwversion, G_BIG_ENDIAN, error)) + return FALSE; + if (fwversion != 0x0) { + g_autofree gchar *tmp = NULL; + tmp = fu_common_version_from_uint32 (fwversion, FWUPD_VERSION_FORMAT_TRIPLET); + fu_firmware_image_set_version (image, tmp); + } else { + guint32 bufver[4] = { '\0' }; + guint32 veraddr = 0x0; + g_autoptr(Bcm57xxVeritem) veritem = NULL; + + /* fall back to the string, e.g. '5719-v1.43' */ + if (!fu_common_read_uint32_safe (buf, bufsz, BCM_NVRAM_STAGE1_VERADDR, + &veraddr, G_BIG_ENDIAN, error)) + return FALSE; + if (veraddr < BCM_PHYS_ADDR_DEFAULT + sizeof(bufver)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "version address 0x%x less than physical 0x%x", + veraddr, (guint) BCM_PHYS_ADDR_DEFAULT); + return FALSE; + } + if (!fu_memcpy_safe ((guint8 *) bufver, sizeof(bufver), 0x0, /* dst */ + buf, bufsz, veraddr - BCM_PHYS_ADDR_DEFAULT, /* src */ + sizeof(bufver), error)) + return FALSE; + veritem = fu_bcm57xx_veritem_new ((guint8 *) bufver, sizeof(bufver)); + if (veritem != NULL) + fu_firmware_image_set_version (image, veritem->version); + } + + fw_nocrc = g_bytes_new_from_bytes (fw, 0x0, g_bytes_get_size (fw) - sizeof(guint32)); + fu_firmware_image_set_bytes (image, fw_nocrc); + return TRUE; +} + +static GBytes * +fu_bcm57xx_stage1_image_write (FuFirmwareImage *image, GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + guint32 crc; + g_autoptr(GByteArray) blob = NULL; + g_autoptr(GBytes) fw_nocrc = NULL; + g_autoptr(GBytes) fw_align = NULL; + + /* get the CRC-less data */ + fw_nocrc = fu_firmware_image_get_bytes (image); + if (fw_nocrc == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported"); + return NULL; + } + + /* this has to be aligned by DWORDs */ + fw_align = fu_common_bytes_align (fw_nocrc, sizeof(guint32), 0xff); + + /* add to a mutable buffer */ + buf = g_bytes_get_data (fw_align, &bufsz); + blob = g_byte_array_sized_new (bufsz + sizeof(guint32)); + g_byte_array_append (blob, buf, bufsz); + + /* add CRC */ + crc = fu_bcm57xx_nvram_crc (buf, bufsz); + fu_byte_array_append_uint32 (blob, crc, G_BIG_ENDIAN); + return g_byte_array_free_to_bytes (g_steal_pointer (&blob)); +} + +static void +fu_bcm57xx_stage1_image_init (FuBcm57xxStage1Image *self) +{ +} + +static void +fu_bcm57xx_stage1_image_class_init (FuBcm57xxStage1ImageClass *klass) +{ + FuFirmwareImageClass *klass_image = FU_FIRMWARE_IMAGE_CLASS (klass); + klass_image->parse = fu_bcm57xx_stage1_image_parse; + klass_image->write = fu_bcm57xx_stage1_image_write; +} + +FuFirmwareImage * +fu_bcm57xx_stage1_image_new (void) +{ + return FU_FIRMWARE_IMAGE (g_object_new (FU_TYPE_BCM57XX_STAGE1_IMAGE, NULL)); +} diff --git a/plugins/bcm57xx/fu-bcm57xx-stage1-image.h b/plugins/bcm57xx/fu-bcm57xx-stage1-image.h new file mode 100644 index 000000000..c895a193a --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-stage1-image.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware-image.h" + +#define FU_TYPE_BCM57XX_STAGE1_IMAGE (fu_bcm57xx_stage1_image_get_type ()) +G_DECLARE_FINAL_TYPE (FuBcm57xxStage1Image, fu_bcm57xx_stage1_image, FU, BCM57XX_STAGE1_IMAGE, FuFirmwareImage) + +FuFirmwareImage *fu_bcm57xx_stage1_image_new (void); diff --git a/plugins/bcm57xx/fu-bcm57xx-stage2-image.c b/plugins/bcm57xx/fu-bcm57xx-stage2-image.c new file mode 100644 index 000000000..f9c2f6fc4 --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-stage2-image.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-common.h" + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-stage2-image.h" + +struct _FuBcm57xxStage2Image { + FuFirmwareImage parent_instance; +}; + +G_DEFINE_TYPE (FuBcm57xxStage2Image, fu_bcm57xx_stage2_image, FU_TYPE_FIRMWARE_IMAGE) + +static gboolean +fu_bcm57xx_stage2_image_parse (FuFirmwareImage *image, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(GBytes) fw_nocrc = NULL; + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if (!fu_bcm57xx_verify_crc (fw, error)) + return FALSE; + } + fw_nocrc = g_bytes_new_from_bytes (fw, 0x0, g_bytes_get_size (fw) - sizeof(guint32)); + fu_firmware_image_set_bytes (image, fw_nocrc); + return TRUE; +} + +static GBytes * +fu_bcm57xx_stage2_image_write (FuFirmwareImage *image, GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + g_autoptr(GByteArray) blob = NULL; + g_autoptr(GBytes) fw_nocrc = NULL; + + /* get the CRC-less data */ + fw_nocrc = fu_firmware_image_get_bytes (image); + if (fw_nocrc == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported"); + return NULL; + } + + /* add to a mutable buffer */ + buf = g_bytes_get_data (fw_nocrc, &bufsz); + blob = g_byte_array_sized_new (bufsz + (sizeof(guint32) * 3)); + fu_byte_array_append_uint32 (blob, BCM_NVRAM_MAGIC, G_BIG_ENDIAN); + fu_byte_array_append_uint32 (blob, + g_bytes_get_size (fw_nocrc) + sizeof(guint32), + G_BIG_ENDIAN); + g_byte_array_append (blob, buf, bufsz); + + /* add CRC */ + fu_byte_array_append_uint32 (blob, fu_bcm57xx_nvram_crc (buf, bufsz), G_BIG_ENDIAN); + return g_byte_array_free_to_bytes (g_steal_pointer (&blob)); +} + +static void +fu_bcm57xx_stage2_image_init (FuBcm57xxStage2Image *self) +{ +} + +static void +fu_bcm57xx_stage2_image_class_init (FuBcm57xxStage2ImageClass *klass) +{ + FuFirmwareImageClass *klass_image = FU_FIRMWARE_IMAGE_CLASS (klass); + klass_image->parse = fu_bcm57xx_stage2_image_parse; + klass_image->write = fu_bcm57xx_stage2_image_write; +} + +FuFirmwareImage * +fu_bcm57xx_stage2_image_new (void) +{ + return FU_FIRMWARE_IMAGE (g_object_new (FU_TYPE_BCM57XX_STAGE2_IMAGE, NULL)); +} diff --git a/plugins/bcm57xx/fu-bcm57xx-stage2-image.h b/plugins/bcm57xx/fu-bcm57xx-stage2-image.h new file mode 100644 index 000000000..b971e1ace --- /dev/null +++ b/plugins/bcm57xx/fu-bcm57xx-stage2-image.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware-image.h" + +#define FU_TYPE_BCM57XX_STAGE2_IMAGE (fu_bcm57xx_stage2_image_get_type ()) +G_DECLARE_FINAL_TYPE (FuBcm57xxStage2Image, fu_bcm57xx_stage2_image, FU, BCM57XX_STAGE2_IMAGE, FuFirmwareImage) + +FuFirmwareImage *fu_bcm57xx_stage2_image_new (void); diff --git a/plugins/bcm57xx/fu-plugin-bcm57xx.c b/plugins/bcm57xx/fu-plugin-bcm57xx.c new file mode 100644 index 000000000..a36f5f345 --- /dev/null +++ b/plugins/bcm57xx/fu-plugin-bcm57xx.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" + +#include "fu-bcm57xx-device.h" +#include "fu-bcm57xx-dict-image.h" +#include "fu-bcm57xx-firmware.h" +#include "fu-bcm57xx-stage1-image.h" +#include "fu-bcm57xx-stage2-image.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_udev_subsystem (plugin, "pci"); + fu_plugin_set_device_gtype (plugin, FU_TYPE_BCM57XX_DEVICE); + fu_plugin_add_firmware_gtype (plugin, "bcm57xx", FU_TYPE_BCM57XX_FIRMWARE); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_BETTER_THAN, "optionrom"); + g_type_ensure (FU_TYPE_BCM57XX_DICT_IMAGE); + g_type_ensure (FU_TYPE_BCM57XX_STAGE1_IMAGE); + g_type_ensure (FU_TYPE_BCM57XX_STAGE2_IMAGE); +} diff --git a/plugins/bcm57xx/fu-self-test.c b/plugins/bcm57xx/fu-self-test.c new file mode 100644 index 000000000..ceaf77168 --- /dev/null +++ b/plugins/bcm57xx/fu-self-test.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-common.h" +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-firmware.h" + +static void +fu_bcm57xx_create_verbuf (guint8 *bufver, const gchar *version) +{ + memcpy (bufver, version, strlen (version) + 1); +} + +static void +fu_bcm57xx_common_veritem_func (void) +{ + g_autoptr(Bcm57xxVeritem) veritem1 = NULL; + g_autoptr(Bcm57xxVeritem) veritem2 = NULL; + g_autoptr(Bcm57xxVeritem) veritem3 = NULL; + guint8 bufver[16] = { 0x0 }; + + fu_bcm57xx_create_verbuf (bufver, "5719-v1.43"); + veritem1 = fu_bcm57xx_veritem_new (bufver, sizeof(bufver)); + g_assert_nonnull (veritem1); + g_assert_cmpstr (veritem1->version, ==, "1.43"); + g_assert_cmpstr (veritem1->branch, ==, BCM_FW_BRANCH_UNKNOWN); + g_assert_cmpint (veritem1->verfmt, ==, FWUPD_VERSION_FORMAT_PAIR); + + fu_bcm57xx_create_verbuf (bufver, "stage1-0.4.391"); + veritem2 = fu_bcm57xx_veritem_new (bufver, sizeof(bufver)); + g_assert_nonnull (veritem2); + g_assert_cmpstr (veritem2->version, ==, "0.4.391"); + g_assert_cmpstr (veritem2->branch, ==, BCM_FW_BRANCH_OSS_FIRMWARE); + g_assert_cmpint (veritem2->verfmt, ==, FWUPD_VERSION_FORMAT_TRIPLET); + + fu_bcm57xx_create_verbuf (bufver, "RANDOM-7"); + veritem3 = fu_bcm57xx_veritem_new (bufver, sizeof(bufver)); + g_assert_nonnull (veritem3); + g_assert_cmpstr (veritem3->version, ==, "RANDOM-7"); + g_assert_cmpstr (veritem3->branch, ==, BCM_FW_BRANCH_UNKNOWN); + g_assert_cmpint (veritem3->verfmt, ==, FWUPD_VERSION_FORMAT_UNKNOWN); +} + +static void +fu_bcm57xx_firmware_talos_func (void) +{ + gboolean ret; + g_autofree gchar *fn = NULL; + g_autofree gchar *fn_out = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) blob_out = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) images = NULL; + g_autoptr(FuFirmware) firmware = fu_bcm57xx_firmware_new (); + + /* load file */ + fn = g_test_build_filename (G_TEST_DIST, "tests", "Bcm5719_talos.bin", NULL); + if (!g_file_test (fn, G_FILE_TEST_EXISTS)) { + g_test_skip ("missing file"); + return; + } + blob = fu_common_get_contents_bytes (fn, &error); + g_assert_no_error (error); + g_assert_nonnull (blob); + ret = fu_firmware_parse (firmware, blob, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert_true (ret); + images = fu_firmware_get_images (firmware); + g_assert_cmpint (images->len, ==, 6); + + blob_out = fu_firmware_write (firmware, &error); + g_assert_no_error (error); + g_assert_nonnull (blob_out); + fn_out = g_test_build_filename (G_TEST_BUILT, "tests", "Bcm5719_talos.bin", NULL); + ret = fu_common_set_contents_bytes (fn_out, blob_out, &error); + g_assert_no_error (error); + g_assert_true (ret); + ret = fu_common_bytes_compare (blob, blob_out, &error); + g_assert_no_error (error); + g_assert_true (ret); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + + /* tests go here */ + g_test_add_func ("/fwupd/bcm57xx/firmware{talos}", fu_bcm57xx_firmware_talos_func); + g_test_add_func ("/fwupd/bcm57xx/common{veritem}", fu_bcm57xx_common_veritem_func); + return g_test_run (); +} diff --git a/plugins/bcm57xx/meson.build b/plugins/bcm57xx/meson.build new file mode 100644 index 000000000..38cb196b8 --- /dev/null +++ b/plugins/bcm57xx/meson.build @@ -0,0 +1,64 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginBcm57xx"'] + +install_data(['bcm57xx.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) +shared_module('fu_plugin_bcm57xx', + fu_hash, + sources : [ + 'fu-plugin-bcm57xx.c', + 'fu-bcm57xx-common.c', + 'fu-bcm57xx-device.c', + 'fu-bcm57xx-dict-image.c', + 'fu-bcm57xx-firmware.c', + 'fu-bcm57xx-recovery-device.c', + 'fu-bcm57xx-stage1-image.c', + 'fu-bcm57xx-stage2-image.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + valgrind, + ], +) + +if get_option('tests') + e = executable( + 'bcm57xx-self-test', + fu_hash, + sources : [ + 'fu-self-test.c', + 'fu-bcm57xx-common.c', + 'fu-bcm57xx-dict-image.c', + 'fu-bcm57xx-firmware.c', + 'fu-bcm57xx-stage1-image.c', + 'fu-bcm57xx-stage2-image.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + dependencies : [ + plugin_deps, + ], + link_with : [ + fwupd, + fwupdplugin, + ], + install : true, + install_dir : installed_test_bindir, + ) + test('ata-self-test', e) +endif diff --git a/plugins/bios/fu-plugin-bios.c b/plugins/bios/fu-plugin-bios.c new file mode 100644 index 000000000..ef8bbddb1 --- /dev/null +++ b/plugins/bios/fu-plugin-bios.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-efivar.h" +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ + const gchar *vendor; + + vendor = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_BIOS_VENDOR); + if (g_strcmp0 (vendor, "coreboot") == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "system uses coreboot"); + return FALSE; + } + + return TRUE; +} + +gboolean +fu_plugin_coldplug (FuPlugin *plugin, GError **error) +{ + g_autofree gchar *sysfsfwdir = NULL; + g_autofree gchar *esrt_path = NULL; + + /* are the EFI dirs set up so we can update each device */ +#if defined(__x86_64__) || defined(__i386__) + g_autoptr(GError) error_local = NULL; + if (!fu_efivar_supported (&error_local)) { + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_LEGACY_BIOS); + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); + return TRUE; + } +#endif + + /* get the directory of ESRT entries */ + sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + esrt_path = g_build_filename (sysfsfwdir, "efi", "esrt", NULL); + if (!g_file_test (esrt_path, G_FILE_TEST_IS_DIR)) { + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED); + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); + return TRUE; + } + + /* we appear to have UEFI capsule updates */ + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED); + return TRUE; +} diff --git a/plugins/bios/meson.build b/plugins/bios/meson.build new file mode 100644 index 000000000..9e7189089 --- /dev/null +++ b/plugins/bios/meson.build @@ -0,0 +1,23 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginBios"'] + +shared_module('fu_plugin_bios', + fu_hash, + sources : [ + 'fu-plugin-bios.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/ccgx/README.md b/plugins/ccgx/README.md index 8be26bdf8..67444e68f 100644 --- a/plugins/ccgx/README.md +++ b/plugins/ccgx/README.md @@ -97,3 +97,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, for example set to `USB:0x04B4` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/ccgx/fu-ccgx-hpi-common.h b/plugins/ccgx/fu-ccgx-hpi-common.h index 4812f4d36..63d158d01 100644 --- a/plugins/ccgx/fu-ccgx-hpi-common.h +++ b/plugins/ccgx/fu-ccgx-hpi-common.h @@ -25,7 +25,7 @@ #define CY_I2C_ENABLE_PRECISE_TIMING 1 #define CY_I2C_EVENT_NOTIFICATION_LEN 3 -#define PD_I2C_SLAVE_ADDRESS 0x08 +#define PD_I2C_TARGET_ADDRESS 0x08 /* timeout (ms) for USB I2C communication */ #define FU_CCGX_HPI_WAIT_TIMEOUT 5000 @@ -60,11 +60,11 @@ typedef enum { * length = 16, data_out = 16 byte configuration information */ CY_I2C_WRITE_CMD, /* perform I2C write operation * value = bit0 - start, bit1 - stop, bit3 - start on idle, - * bits[14:8] - slave address, bit15 - scbIndex. length = 0 the + * bits[14:8] - target address, bit15 - scbIndex. length = 0 the * data is provided over the bulk endpoints */ CY_I2C_READ_CMD, /* rerform I2C read operation. * value = bit0 - start, bit1 - stop, bit2 - Nak last byte, - * bit3 - start on idle, bits[14:8] - slave address, bit15 - scbIndex, + * bit3 - start on idle, bits[14:8] - target address, bit15 - scbIndex, * length = 0. The data is provided over the bulk endpoints */ CY_I2C_GET_STATUS_CMD, /* retrieve the I2C bus status. * value = bit0 - 0: TX 1: RX, bit15 - scbIndex, length = 3, @@ -107,10 +107,10 @@ typedef enum { typedef struct __attribute__((packed)) { guint32 frequency; /* frequency of operation. Only valid values are 100KHz and 400KHz */ - guint8 slave_address; /* slave address to be used when in slave mode */ + guint8 target_address; /* target address to be used when in target mode */ guint8 is_msb_first; /* whether to transmit most significant bit first */ - guint8 is_master; /* whether to block is to be configured as a master*/ - guint8 s_ignore; /* ignore general call in slave mode */ + guint8 is_initiator; /* whether to block is to be configured as a initiator */ + guint8 s_ignore; /* ignore general call in target mode */ guint8 is_clock_stretch; /* whether to stretch clock in case of no FIFO availability */ guint8 is_loop_back; /* whether to loop back TX data to RX. Valid only for debug purposes */ guint8 reserved[6]; diff --git a/plugins/ccgx/fu-ccgx-hpi-device.c b/plugins/ccgx/fu-ccgx-hpi-device.c index 6934decf5..e61a095ba 100644 --- a/plugins/ccgx/fu-ccgx-hpi-device.c +++ b/plugins/ccgx/fu-ccgx-hpi-device.c @@ -26,7 +26,7 @@ struct _FuCcgxHpiDevice guint8 num_ports; /* max number of ports */ FWMode fw_mode; FWImageType fw_image_type; - guint8 slave_address; + guint8 target_address; guint8 ep_bulk_in; guint8 ep_bulk_out; guint8 ep_intr_in; @@ -273,19 +273,19 @@ fu_ccgx_hpi_device_i2c_read (FuCcgxHpiDevice *self, CyI2CDataConfigBits cfg_bits, GError **error) { - guint8 slave_address = 0; + guint8 target_address = 0; if (!fu_ccgx_hpi_device_check_i2c_status (self, CY_I2C_MODE_READ, error)) { g_prefix_error (error, "i2c read error: "); return FALSE; } - slave_address = (self->slave_address & 0x7F) | (self->scb_index << 7); + target_address = (self->target_address & 0x7F) | (self->scb_index << 7); if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)), G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, G_USB_DEVICE_REQUEST_TYPE_VENDOR, G_USB_DEVICE_RECIPIENT_DEVICE, CY_I2C_READ_CMD, - (((guint16) slave_address) << 8) | cfg_bits, + (((guint16) target_address) << 8) | cfg_bits, bufsz, NULL, 0x0, NULL, FU_CCGX_HPI_WAIT_TIMEOUT, NULL, error)) { @@ -316,19 +316,19 @@ fu_ccgx_hpi_device_i2c_write (FuCcgxHpiDevice *self, CyI2CDataConfigBits cfg_bits, GError **error) { - guint8 slave_address; + guint8 target_address; if (!fu_ccgx_hpi_device_check_i2c_status (self, CY_I2C_MODE_WRITE, error)) { g_prefix_error (error, "i2c get status error: "); return FALSE; } - slave_address = (self->slave_address & 0x7F) | (self->scb_index << 7); + target_address = (self->target_address & 0x7F) | (self->scb_index << 7); if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)), G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, G_USB_DEVICE_REQUEST_TYPE_VENDOR, G_USB_DEVICE_RECIPIENT_DEVICE, CY_I2C_WRITE_CMD, - ((guint16) slave_address << 8) | (cfg_bits & CY_I2C_DATA_CONFIG_STOP), + ((guint16) target_address << 8) | (cfg_bits & CY_I2C_DATA_CONFIG_STOP), bufsz, /* idx */ NULL, 0x0, NULL, FU_CCGX_HPI_WAIT_TIMEOUT, @@ -359,20 +359,20 @@ fu_ccgx_hpi_device_i2c_write_no_resp (FuCcgxHpiDevice *self, CyI2CDataConfigBits cfg_bits, GError **error) { - guint8 slave_address = 0; + guint8 target_address = 0; g_autoptr(GError) error_local = NULL; if (!fu_ccgx_hpi_device_check_i2c_status (self, CY_I2C_MODE_WRITE, error)) { g_prefix_error (error, "i2c write error: "); return FALSE; } - slave_address = (self->slave_address & 0x7F) | (self->scb_index << 7); + target_address = (self->target_address & 0x7F) | (self->scb_index << 7); if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)), G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, G_USB_DEVICE_REQUEST_TYPE_VENDOR, G_USB_DEVICE_RECIPIENT_DEVICE, CY_I2C_WRITE_CMD, - ((guint16) slave_address << 8) | (cfg_bits & CY_I2C_DATA_CONFIG_STOP), + ((guint16) target_address << 8) | (cfg_bits & CY_I2C_DATA_CONFIG_STOP), bufsz, NULL, 0x0, NULL, FU_CCGX_HPI_WAIT_TIMEOUT, NULL, error)) { @@ -1082,7 +1082,7 @@ fu_ccgx_hpi_device_prepare_firmware (FuDevice *device, self->silicon_id, fw_silicon_id); return NULL; } - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { fw_app_type = fu_ccgx_firmware_get_app_type (FU_CCGX_FIRMWARE (firmware)); if (fw_app_type != self->fw_app_type) { g_set_error (error, @@ -1375,7 +1375,7 @@ fu_ccgx_hpi_device_setup (FuDevice *device, GError **error) return FALSE; } i2c_config.frequency = FU_CCGX_HPI_FREQ; - i2c_config.is_master = TRUE; + i2c_config.is_initiator = TRUE; i2c_config.is_msb_first = TRUE; if (!fu_ccgx_hpi_device_set_i2c_config (self, &i2c_config, error)) { g_prefix_error (error, "set config error: "); @@ -1573,7 +1573,7 @@ fu_ccgx_hpi_device_init (FuCcgxHpiDevice *self) self->inf_num = 0x0; self->hpi_addrsz = 1; self->num_ports = 1; - self->slave_address = PD_I2C_SLAVE_ADDRESS; + self->target_address = PD_I2C_TARGET_ADDRESS; self->ep_bulk_out = PD_I2C_USB_EP_BULK_OUT; self->ep_bulk_in = PD_I2C_USB_EP_BULK_IN; self->ep_intr_in = PD_I2C_USB_EP_INTR_IN; diff --git a/plugins/colorhug/README.md b/plugins/colorhug/README.md index c86db7516..9e19ec748 100644 --- a/plugins/colorhug/README.md +++ b/plugins/colorhug/README.md @@ -34,3 +34,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, in this instance set to `USB:0x273F` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/colorhug/fu-colorhug-common.c b/plugins/colorhug/fu-colorhug-common.c index b0773c758..3baa5f3d2 100644 --- a/plugins/colorhug/fu-colorhug-common.c +++ b/plugins/colorhug/fu-colorhug-common.c @@ -77,10 +77,10 @@ ch_strerror (ChError error_enum) return "Self test failed: ADC Vss"; if (error_enum == CH_ERROR_SELF_TEST_ADC_VREF) return "Self test failed: ADC Vref"; - if (error_enum == CH_ERROR_I2C_SLAVE_ADDRESS) - return "I2C set slave address failed"; - if (error_enum == CH_ERROR_I2C_SLAVE_CONFIG) - return "I2C set slave config failed"; + if (error_enum == CH_ERROR_I2C_TARGET_ADDRESS) + return "I2C set target address failed"; + if (error_enum == CH_ERROR_I2C_TARGET_CONFIG) + return "I2C set target config failed"; if (error_enum == CH_ERROR_SELF_TEST_EEPROM) return "Self test failed: EEPROM"; return NULL; diff --git a/plugins/colorhug/fu-colorhug-common.h b/plugins/colorhug/fu-colorhug-common.h index 61723a283..cef79e540 100644 --- a/plugins/colorhug/fu-colorhug-common.h +++ b/plugins/colorhug/fu-colorhug-common.h @@ -42,8 +42,8 @@ typedef enum { CH_ERROR_SELF_TEST_ADC_VDD, CH_ERROR_SELF_TEST_ADC_VSS, CH_ERROR_SELF_TEST_ADC_VREF, - CH_ERROR_I2C_SLAVE_ADDRESS, - CH_ERROR_I2C_SLAVE_CONFIG, + CH_ERROR_I2C_TARGET_ADDRESS, + CH_ERROR_I2C_TARGET_CONFIG, CH_ERROR_SELF_TEST_EEPROM, CH_ERROR_LAST } ChError; diff --git a/plugins/coreboot/README.md b/plugins/coreboot/README.md index beec31f75..c55ca55a3 100644 --- a/plugins/coreboot/README.md +++ b/plugins/coreboot/README.md @@ -57,3 +57,7 @@ Vendor ID Security ------------------ The vendor ID is set from the BIOS vendor, in this instance `DMI:coreboot` + +External interface access +------------------------- +This plugin does not currently use any external access. diff --git a/plugins/cpu/README.md b/plugins/cpu/README.md index 832f43a62..b2f54c230 100644 --- a/plugins/cpu/README.md +++ b/plugins/cpu/README.md @@ -7,3 +7,16 @@ Introduction This plugin reads the sysfs attributes associated with CPU microcode. It displays a read-only value of the CPU microcode version loaded onto the physical CPU at fwupd startup. + +GUID Generation +--------------- + +These devices add extra instance IDs from the CPUID values, e.g. + + * `CPUID\PRO_0&FAM_06` + * `CPUID\PRO_0&FAM_06&MOD_0E` + * `CPUID\PRO_0&FAM_06&MOD_0E&STP_3` + +External interface access +------------------------- +This plugin requires no extra access. diff --git a/plugins/cpu/cpu.quirk b/plugins/cpu/cpu.quirk new file mode 100644 index 000000000..88ee7c5c1 --- /dev/null +++ b/plugins/cpu/cpu.quirk @@ -0,0 +1,3 @@ +# Intel Atom Bay Trail [Silvermont] +[DeviceInstanceId=CPUID\PRO_0&FAM_06&MOD_37] +BcrAddr = 0x0 diff --git a/plugins/cpu/fu-cpu-device.c b/plugins/cpu/fu-cpu-device.c index aa1fae22e..a97191b55 100644 --- a/plugins/cpu/fu-cpu-device.c +++ b/plugins/cpu/fu-cpu-device.c @@ -10,36 +10,87 @@ struct _FuCpuDevice { FuDevice parent_instance; + FuCpuDeviceFlag flags; }; G_DEFINE_TYPE (FuCpuDevice, fu_cpu_device, FU_TYPE_DEVICE) -static void fu_cpu_device_parse_section (FuDevice *dev, const gchar *data) +gboolean +fu_cpu_device_has_flag (FuCpuDevice *self, FuCpuDeviceFlag flag) { - g_auto(GStrv) lines = NULL; + return (self->flags & flag) > 0; +} - lines = g_strsplit (data, "\n", 0); - for (guint i = 0; lines[i] != NULL; i++) { - if (g_str_has_prefix (lines[i], "vendor_id")) { - g_auto(GStrv) fields = g_strsplit (lines[i], ":", -1); - if (fields[1] != NULL) - fu_device_set_vendor (dev, g_strchug (fields[1])); - } else if (g_str_has_prefix (lines[i], "model name")) { - g_auto(GStrv) fields = g_strsplit (lines[i], ":", -1); - if (fields[1] != NULL) - fu_device_set_name (dev, g_strchug (fields[1])); - } else if (g_str_has_prefix (lines[i], "microcode")) { - g_auto(GStrv) fields = g_strsplit (lines[i], ":", -1); - if (fields[1] != NULL) - fu_device_set_version (dev, g_strchug (fields[1])); - } else if (g_str_has_prefix (lines[i], "physical id")) { - g_auto(GStrv) fields = g_strsplit (lines[i], ":", -1); - if (fields[1] != NULL) { - g_autofree gchar *tmp = g_strdup_printf ("cpu:%s", g_strchug (fields[1])); - fu_device_set_physical_id (dev, tmp); - } - } - } +static void +fu_cpu_device_to_string (FuDevice *device, guint idt, GString *str) +{ + FuCpuDevice *self = FU_CPU_DEVICE (device); + fu_common_string_append_kb (str, idt, "HasSHSTK", + fu_cpu_device_has_flag (self, FU_CPU_DEVICE_FLAG_SHSTK)); + fu_common_string_append_kb (str, idt, "HasIBT", + fu_cpu_device_has_flag (self, FU_CPU_DEVICE_FLAG_IBT)); + fu_common_string_append_kb (str, idt, "HasTME", + fu_cpu_device_has_flag (self, FU_CPU_DEVICE_FLAG_TME)); + fu_common_string_append_kb (str, idt, "HasSMAP", + fu_cpu_device_has_flag (self, FU_CPU_DEVICE_FLAG_SMAP)); +} + +static const gchar * +fu_cpu_device_convert_vendor (const gchar *vendor) +{ + if (g_strcmp0 (vendor, "GenuineIntel") == 0) + return "Intel"; + if (g_strcmp0 (vendor, "AuthenticAMD") == 0 || + g_strcmp0 (vendor, "AMDisbetter!") == 0) + return "AMD"; + if (g_strcmp0 (vendor, "CentaurHauls") == 0) + return "IDT"; + if (g_strcmp0 (vendor, "CyrixInstead") == 0) + return "Cyrix"; + if (g_strcmp0 (vendor, "TransmetaCPU") == 0 || + g_strcmp0 (vendor, "GenuineTMx86") == 0) + return "Transmeta"; + if (g_strcmp0 (vendor, "Geode by NSC") == 0) + return "National Semiconductor"; + if (g_strcmp0 (vendor, "NexGenDriven") == 0) + return "NexGen"; + if (g_strcmp0 (vendor, "RiseRiseRise") == 0) + return "Rise"; + if (g_strcmp0 (vendor, "SiS SiS SiS ") == 0) + return "SiS"; + if (g_strcmp0 (vendor, "UMC UMC UMC ") == 0) + return "UMC"; + if (g_strcmp0 (vendor, "VIA VIA VIA ") == 0) + return "VIA"; + if (g_strcmp0 (vendor, "Vortex86 SoC") == 0) + return "Vortex"; + if (g_strcmp0 (vendor, " Shanghai ") == 0) + return "Zhaoxin"; + if (g_strcmp0 (vendor, "HygonGenuine") == 0) + return "Hygon"; + if (g_strcmp0 (vendor, "E2K MACHINE") == 0) + return "MCST"; + if (g_strcmp0 (vendor, "bhyve bhyve ") == 0) + return "bhyve"; + if (g_strcmp0 (vendor, " KVMKVMKVM ") == 0) + return "KVM"; + if (g_strcmp0 (vendor, "TCGTCGTCGTCG") == 0) + return "QEMU"; + if (g_strcmp0 (vendor, "Microsoft Hv") == 0) + return "Microsoft"; + if (g_strcmp0 (vendor, " lrpepyh vr") == 0) + return "Parallels"; + if (g_strcmp0 (vendor, "VMwareVMware") == 0) + return "VMware"; + if (g_strcmp0 (vendor, "XenVMMXenVMM") == 0) + return "Xen"; + if (g_strcmp0 (vendor, "ACRNACRNACRN") == 0) + return "ACRN"; + if (g_strcmp0 (vendor, " QNXQVMBSQG ") == 0) + return "QNX"; + if (g_strcmp0 (vendor, "VirtualApple") == 0) + return "Apple"; + return vendor; } static void @@ -49,18 +100,180 @@ fu_cpu_device_init (FuCpuDevice *self) fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_icon (FU_DEVICE (self), "computer"); fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_HEX); + fu_device_set_physical_id (FU_DEVICE (self), "cpu:0"); +} + +static gboolean +fu_cpu_device_add_instance_ids (FuDevice *device, GError **error) +{ + guint32 eax = 0; + guint32 family_id; + guint32 family_id_ext; + guint32 model_id; + guint32 model_id_ext; + guint32 processor_id; + guint32 stepping_id; + g_autofree gchar *devid1 = NULL; + g_autofree gchar *devid2 = NULL; + g_autofree gchar *devid3 = NULL; + + /* decode according to https://en.wikipedia.org/wiki/CPUID */ + if (!fu_common_cpuid (0x1, &eax, NULL, NULL, NULL, error)) + return FALSE; + processor_id = (eax >> 12) & 0x3; + model_id = (eax >> 4) & 0xf; + family_id = (eax >> 8) & 0xf; + model_id_ext = (eax >> 16) & 0xf; + family_id_ext = (eax >> 20) & 0xff; + stepping_id = eax & 0xf; + + /* use extended IDs where required */ + if (family_id == 6 || family_id == 15) + model_id |= model_id_ext << 4; + if (family_id == 15) + family_id += family_id_ext; + + devid1 = g_strdup_printf ("CPUID\\PRO_%01X&FAM_%02X", + processor_id, + family_id); + fu_device_add_instance_id (device, devid1); + devid2 = g_strdup_printf ("CPUID\\PRO_%01X&FAM_%02X&MOD_%02X", + processor_id, + family_id, + model_id); + fu_device_add_instance_id (device, devid2); + devid3 = g_strdup_printf ("CPUID\\PRO_%01X&FAM_%02X&MOD_%02X&STP_%01X", + processor_id, + family_id, + model_id, + stepping_id); + fu_device_add_instance_id (device, devid3); + return TRUE; +} + +static gboolean +fu_cpu_device_probe_manufacturer_id (FuDevice *device, GError **error) +{ + guint32 ebx = 0; + guint32 ecx = 0; + guint32 edx = 0; + gchar str[13] = { '\0' }; + if (!fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), 0x0, /* dst */ + (const guint8 *) &ebx, sizeof(ebx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), 0x4, /* dst */ + (const guint8 *) &edx, sizeof(edx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), 0x8, /* dst */ + (const guint8 *) &ecx, sizeof(ecx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + fu_device_set_vendor (device, fu_cpu_device_convert_vendor (str)); + return TRUE; +} + +static gboolean +fu_cpu_device_probe_model (FuDevice *device, GError **error) +{ + guint32 eax = 0; + guint32 ebx = 0; + guint32 ecx = 0; + guint32 edx = 0; + gchar str[49] = { '\0' }; + + for (guint32 i = 0; i < 3; i++) { + if (!fu_common_cpuid (0x80000002 + i, &eax, &ebx, &ecx, &edx, error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), (16 * i) + 0x0, /* dst */ + (const guint8 *) &eax, sizeof(eax), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), (16 * i) + 0x4, /* dst */ + (const guint8 *) &ebx, sizeof(ebx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), (16 * i) + 0x8, /* dst */ + (const guint8 *) &ecx, sizeof(ecx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), (16 * i) + 0xc, /* dst */ + (const guint8 *) &edx, sizeof(edx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + } + fu_device_set_name (device, str); + return TRUE; +} + +static gboolean +fu_cpu_device_probe_extended_features (FuDevice *device, GError **error) +{ + FuCpuDevice *self = FU_CPU_DEVICE (device); + guint32 ebx = 0; + guint32 ecx = 0; + + if (!fu_common_cpuid (0x7, NULL, &ebx, &ecx, NULL, error)) + return FALSE; + if ((ebx >> 20) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_SMAP; + if ((ecx >> 7) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_SHSTK; + if ((ecx >> 13) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_TME; + if ((ecx >> 20) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_IBT; + return TRUE; +} + +static gboolean +fu_cpu_device_probe (FuDevice *device, GError **error) +{ + if (!fu_cpu_device_probe_manufacturer_id (device, error)) + return FALSE; + if (!fu_cpu_device_probe_model (device, error)) + return FALSE; + if (!fu_cpu_device_probe_extended_features (device, error)) + return FALSE; + if (!fu_cpu_device_add_instance_ids (device, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_cpu_device_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + if (g_strcmp0 (key, "BcrAddr") == 0) { + guint64 tmp = fu_common_strtoull (value); + fu_device_set_metadata_integer (device, "BcrAddr", tmp); + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "no supported"); + return FALSE; } static void fu_cpu_device_class_init (FuCpuDeviceClass *klass) { + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + klass_device->to_string = fu_cpu_device_to_string; + klass_device->probe = fu_cpu_device_probe; + klass_device->set_quirk_kv = fu_cpu_device_set_quirk_kv; } FuCpuDevice * -fu_cpu_device_new (const gchar *section) +fu_cpu_device_new (void) { FuCpuDevice *device = NULL; device = g_object_new (FU_TYPE_CPU_DEVICE, NULL); - fu_cpu_device_parse_section (FU_DEVICE (device), section); return device; } diff --git a/plugins/cpu/fu-cpu-device.h b/plugins/cpu/fu-cpu-device.h index 1796c6b91..0115916a2 100644 --- a/plugins/cpu/fu-cpu-device.h +++ b/plugins/cpu/fu-cpu-device.h @@ -11,4 +11,14 @@ #define FU_TYPE_CPU_DEVICE (fu_cpu_device_get_type ()) G_DECLARE_FINAL_TYPE (FuCpuDevice, fu_cpu_device, FU, CPU_DEVICE, FuDevice) -FuCpuDevice *fu_cpu_device_new (const gchar *section); +typedef enum { + FU_CPU_DEVICE_FLAG_NONE = 0, + FU_CPU_DEVICE_FLAG_SHSTK = 1 << 0, + FU_CPU_DEVICE_FLAG_IBT = 1 << 1, + FU_CPU_DEVICE_FLAG_TME = 1 << 2, + FU_CPU_DEVICE_FLAG_SMAP = 1 << 3, +} FuCpuDeviceFlag; + +FuCpuDevice *fu_cpu_device_new (void); +gboolean fu_cpu_device_has_flag (FuCpuDevice *self, + FuCpuDeviceFlag flag); diff --git a/plugins/cpu/fu-cpu-helper-cet-common.c b/plugins/cpu/fu-cpu-helper-cet-common.c new file mode 100644 index 000000000..271ece7e8 --- /dev/null +++ b/plugins/cpu/fu-cpu-helper-cet-common.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2020 H.J. Lu + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-cpu-helper-cet-common.h" + +static void +fu_cpu_helper_cet_testfn_fptr (void) +{ +} + +static void +__attribute__ ((noinline, noclone)) +fu_cpu_helper_cet_testfn_call_fptr (void (*func) (void)) +{ + func (); +} + +void +__attribute__ ((noinline, noclone)) +fu_cpu_helper_cet_testfn1 (void) +{ + fu_cpu_helper_cet_testfn_call_fptr (fu_cpu_helper_cet_testfn_fptr); +} diff --git a/plugins/cpu/fu-cpu-helper-cet-common.h b/plugins/cpu/fu-cpu-helper-cet-common.h new file mode 100644 index 000000000..31130d6e3 --- /dev/null +++ b/plugins/cpu/fu-cpu-helper-cet-common.h @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2020 H.J. Lu + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +void fu_cpu_helper_cet_testfn1 (void); diff --git a/plugins/cpu/fu-cpu-helper-cet.c b/plugins/cpu/fu-cpu-helper-cet.c new file mode 100644 index 000000000..56ca9a573 --- /dev/null +++ b/plugins/cpu/fu-cpu-helper-cet.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2020 H.J. Lu + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-cpu-helper-cet-common.h" + +#ifdef HAVE_SIGACTION +static __attribute__((noreturn))void +segfault_sigaction (int signal, siginfo_t *si, void *arg) +{ + /* CET did exactly as it should to protect the system */ + exit (0); +} +#endif + +int +main (int argc, char *argv[]) +{ +#ifdef HAVE_SIGACTION + struct sigaction sa = { 0 }; + + sigemptyset (&sa.sa_mask); + sa.sa_sigaction = segfault_sigaction; + sa.sa_flags = SA_SIGINFO; + sigaction (SIGSEGV, &sa, NULL); +#endif + + fu_cpu_helper_cet_testfn1 (); + + /* this means CET did not work */ + return 1; +} diff --git a/plugins/cpu/fu-plugin-cpu.c b/plugins/cpu/fu-plugin-cpu.c index 43e58e12d..52015c4bd 100644 --- a/plugins/cpu/fu-plugin-cpu.c +++ b/plugins/cpu/fu-plugin-cpu.c @@ -14,28 +14,139 @@ void fu_plugin_init (FuPlugin *plugin) { fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_BEFORE, "msr"); } gboolean fu_plugin_coldplug (FuPlugin *plugin, GError **error) { - gsize length; - g_autofree gchar *data = NULL; - g_auto(GStrv) lines = NULL; - - if (!g_file_get_contents ("/proc/cpuinfo", &data, &length, error)) + g_autoptr(FuCpuDevice) dev = fu_cpu_device_new (); + fu_device_set_quirks (FU_DEVICE (dev), fu_plugin_get_quirks (plugin)); + if (!fu_device_probe (FU_DEVICE (dev), error)) return FALSE; - - lines = g_strsplit (data, "\n\n", 0); - for (guint i = 0; lines[i] != NULL; i++) { - g_autoptr(FuCpuDevice) dev = NULL; - if (strlen (lines[i]) == 0) - continue; - dev = fu_cpu_device_new (lines[i]); - if (!fu_device_setup (FU_DEVICE (dev), error)) - return FALSE; - fu_plugin_device_add (plugin, FU_DEVICE (dev)); - } - + if (!fu_device_setup (FU_DEVICE (dev), error)) + return FALSE; + fu_plugin_cache_add (plugin, "cpu", dev); + fu_plugin_device_add (plugin, FU_DEVICE (dev)); return TRUE; } + +static void +fu_plugin_add_security_attrs_intel_cet_enabled (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuCpuDevice *device = fu_plugin_cache_lookup (plugin, "cpu"); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_INTEL_CET_ENABLED); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL); + fu_security_attrs_append (attrs, attr); + + /* check for CET */ + if (!fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_SHSTK) || + !fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_IBT)) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_plugin_add_security_attrs_intel_cet_active (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuCpuDevice *device = fu_plugin_cache_lookup (plugin, "cpu"); + gint exit_status = 0xff; + g_autofree gchar *toolfn = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + + /* check for CET */ + if (!fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_SHSTK) || + !fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_IBT)) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_INTEL_CET_ACTIVE); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fu_security_attrs_append (attrs, attr); + + /* check that userspace has been compiled for CET support */ + toolfn = g_build_filename (FWUPD_LIBEXECDIR, "fwupd", "fwupd-detect-cet", NULL); + if (!g_spawn_command_line_sync (toolfn, NULL, NULL, &exit_status, &error_local)) { + g_warning ("failed to test CET: %s", error_local->message); + return; + } + if (!g_spawn_check_exit_status (exit_status, &error_local)) { + g_debug ("CET does not function, not supported: %s", error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_SUPPORTED); +} + +static void +fu_plugin_add_security_attrs_intel_tme (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuCpuDevice *device = fu_plugin_cache_lookup (plugin, "cpu"); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_PROTECTION); + fu_security_attrs_append (attrs, attr); + + /* check for TME */ + if (!fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_TME)) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_plugin_add_security_attrs_intel_smap (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuCpuDevice *device = fu_plugin_cache_lookup (plugin, "cpu"); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_INTEL_SMAP); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_PROTECTION); + fu_security_attrs_append (attrs, attr); + + /* check for SMEP and SMAP */ + if (!fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_SMAP)) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + /* only Intel */ + if (!fu_common_is_cpu_intel ()) + return; + + fu_plugin_add_security_attrs_intel_cet_enabled (plugin, attrs); + fu_plugin_add_security_attrs_intel_cet_active (plugin, attrs); + fu_plugin_add_security_attrs_intel_tme (plugin, attrs); + fu_plugin_add_security_attrs_intel_smap (plugin, attrs); +} diff --git a/plugins/cpu/meson.build b/plugins/cpu/meson.build index aaaed6e92..09f762953 100644 --- a/plugins/cpu/meson.build +++ b/plugins/cpu/meson.build @@ -1,5 +1,9 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginCpu"'] +install_data(['cpu.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + shared_module('fu_plugin_cpu', fu_hash, sources : [ @@ -22,3 +26,32 @@ shared_module('fu_plugin_cpu', plugin_deps, ], ) + +if cc.has_argument('-fcf-protection') + libfwupdcethelper = static_library('fwupdcethelper', + sources : [ + 'fu-cpu-helper-cet-common.c', + ], + include_directories : [ + root_incdir, + ], + c_args : ['-fcf-protection=none'], + install : false, + ) + + executable( + 'fwupd-detect-cet', + sources : [ + 'fu-cpu-helper-cet.c', + ], + include_directories : [ + root_incdir, + ], + link_with : [ + libfwupdcethelper, + ], + c_args : ['-fcf-protection=full'], + install : true, + install_dir : join_paths(libexecdir, 'fwupd') + ) +endif diff --git a/plugins/cros-ec/README.md b/plugins/cros-ec/README.md new file mode 100644 index 000000000..5b348b004 --- /dev/null +++ b/plugins/cros-ec/README.md @@ -0,0 +1,47 @@ +Chrome OS EC Support +=================== + +Introduction +------------ + +This plugin provides support for the firmware updates for Chrome OS EC +project based devices. + +Initially, it supports the USB endpoint updater, but lays the groundwork for +future updaters which use other update methods other than the USB endpoint. + +This is based on the chromeos ec project's usb_updater2 application [1]. + +Information about the USB update protocol is available at [2]. + +Firmware Format +--------------- +The daemon will decompress the cabinet archive and extract a firmware blob in +the Google fmap [3] file format. + +This plugin supports the following protocol ID: + + * com.google.usb.crosec + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_18D1&PID_501A` + +Vendor ID Security +------------------ + +The vendor ID is set from the USB vendor, which is set to various different +values depending on the model and device mode. The list of USB VIDs used is: + + * `USB:0x18D1` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. + +[1] https://chromium.googlesource.com/chromiumos/platform/ec/+/master/extra/usb_updater/usb_updater2.c +[2] https://chromium.googlesource.com/chromiumos/platform/ec/+/master/docs/usb_updater.md +[3] https://www.chromium.org/chromium-os/firmware-porting-guide/fmap diff --git a/plugins/cros-ec/cros-ec.quirk b/plugins/cros-ec/cros-ec.quirk new file mode 100644 index 000000000..91a46201d --- /dev/null +++ b/plugins/cros-ec/cros-ec.quirk @@ -0,0 +1,14 @@ +# Servo Micro +[DeviceInstanceId=USB\VID_18D1&PID_501A] +Plugin = cros_ec +Summary = Servo Micro (aka "uServo") Debug Board + +# Quiche +[DeviceInstanceId=USB\VID_18D1&PID_5048] +Plugin = cros_ec +Summary = Quiche Reference Board + +# Gingerbread +[DeviceInstanceId=USB\VID_18D1&PID_5049] +Plugin = cros_ec +Summary = Gingerbread Reference Board diff --git a/plugins/cros-ec/data/lsusb-servo-micro.txt b/plugins/cros-ec/data/lsusb-servo-micro.txt new file mode 100644 index 000000000..358ad1724 --- /dev/null +++ b/plugins/cros-ec/data/lsusb-servo-micro.txt @@ -0,0 +1,239 @@ + +Bus 003 Device 006: ID 18d1:501a Google Inc. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x18d1 Google Inc. + idProduct 0x501a + bcdDevice 1.00 + iManufacturer 1 Google Inc. + iProduct 2 Servo Micro + iSerial 3 CMO653-00166-040491U00771 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 170 + bNumInterfaces 7 + bConfigurationValue 1 + iConfiguration 4 servo_micro_v2.4.17-df61092c3 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 80 + bInterfaceProtocol 1 + iInterface 6 UART3 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 83 + bInterfaceProtocol 255 + iInterface 10 Firmware update + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 81 + bInterfaceProtocol 1 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 3 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 80 + bInterfaceProtocol 1 + iInterface 7 Servo Shell + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x84 EP 4 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x04 EP 4 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 4 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 82 + bInterfaceProtocol 1 + iInterface 5 I2C + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x85 EP 5 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x05 EP 5 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 5 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 80 + bInterfaceProtocol 1 + iInterface 8 CPU + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x86 EP 6 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x06 EP 6 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 6 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 80 + bInterfaceProtocol 1 + iInterface 9 EC + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x87 EP 7 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x07 EP 7 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 0 +Device Status: 0x0000 + (Bus Powered) diff --git a/plugins/cros-ec/fu-cros-ec-common.c b/plugins/cros-ec/fu-cros-ec-common.c new file mode 100644 index 000000000..07111b40a --- /dev/null +++ b/plugins/cros-ec/fu-cros-ec-common.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-cros-ec-common.h" + +gboolean +fu_cros_ec_parse_version (const gchar *version_raw, + struct cros_ec_version *version, GError **error) +{ + g_auto(GStrv) v_split = NULL; + g_auto(GStrv) marker_split = NULL; + g_auto(GStrv) triplet_split = NULL; + + if (NULL == version_raw || 0 == strlen (version_raw)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no version string to parse"); + return FALSE; + } + + /* sample version string: cheese_v1.1.1755-4da9520 */ + v_split = g_strsplit (version_raw, "_v", 2); + if (g_strv_length (v_split) < 2) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "version marker not found"); + return FALSE; + } + marker_split = g_strsplit_set (v_split[1], "-+", 2); + if (g_strv_length (marker_split) < 2) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "hash marker not found: %s", v_split[1]); + return FALSE; + } + triplet_split = g_strsplit_set (marker_split[0], ".", 3); + if (g_strv_length (triplet_split) < 3) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "improper version triplet: %s", marker_split[0]); + return FALSE; + } + g_strlcpy (version->triplet, marker_split[0], 32); + if (g_strlcpy (version->boardname, v_split[0], 32) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "empty board name"); + return FALSE; + } + if (g_strlcpy (version->sha1, marker_split[1], 32) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "empty SHA"); + return FALSE; + } + version->dirty = (g_strrstr(v_split[1], "+") != NULL); + + return TRUE; +} diff --git a/plugins/cros-ec/fu-cros-ec-common.h b/plugins/cros-ec/fu-cros-ec-common.h new file mode 100644 index 000000000..e626e9cda --- /dev/null +++ b/plugins/cros-ec/fu-cros-ec-common.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-plugin.h" + +#define UPDATE_PROTOCOL_VERSION 6 +#define FU_CROS_EC_STRLEN 32 + +/* + * This is the format of the update PDU header. + * + * block digest: the first four bytes of the sha1 digest of the rest of the + * structure (can be 0 on boards where digest is ignored). + * block_base: offset of this PDU into the flash SPI. + */ +typedef struct __attribute__((packed)) { + guint32 block_digest; + guint32 block_base; + /* The actual payload goes here. */ +} update_command; + +/* + * This is the frame format the host uses when sending update PDUs over USB. + * + * The PDUs are up to 1K bytes in size, they are fragmented into USB chunks of + * 64 bytes each and reassembled on the receive side before being passed to + * the flash update function. + * + * The flash update function receives the unframed PDU body (starting at the + * cmd field below), and puts its reply into the same buffer the PDU was in. + */ +struct update_frame_header { + guint32 block_size; /* Total frame size, including this field. */ + update_command cmd; +}; + +/* + * A convenience structure which allows to group together various revision + * fields of the header created by the signer (cr50-specific). + * + * These fields are compared when deciding if versions of two images are the + * same or when deciding which one of the available images to run. + */ +struct signed_header_version { + guint32 minor; + guint32 major; + guint32 epoch; +}; + +/* + * Response to the connection establishment request. + * + * When responding to the very first packet of the update sequence, the + * original USB update implementation was responding with a four byte value, + * just as to any other block of the transfer sequence. + * + * It became clear that there is a need to be able to enhance the update + * protocol, while staying backwards compatible. + * + * All newer protocol versions (starting with version 2) respond to the very + * first packet with an 8 byte or larger response, where the first 4 bytes are + * a version specific data, and the second 4 bytes - the protocol version + * number. + * + * This way the host receiving of a four byte value in response to the first + * packet is considered an indication of the target running the 'legacy' + * protocol, version 1. Receiving of an 8 byte or longer response would + * communicates the protocol version in the second 4 bytes. + */ +struct first_response_pdu { + guint32 return_value; + + /* The below fields are present in versions 2 and up. */ + + /* Type of header following (one of first_response_pdu_header_type) */ + guint16 header_type; + + /* Must be UPDATE_PROTOCOL_VERSION */ + guint16 protocol_version; + + /* In version 6 and up, a board-specific header follows. */ + union { + /* cr50 (header_type = UPDATE_HEADER_TYPE_CR50) */ + struct { + /* The below fields are present in versions 3 and up. */ + guint32 backup_ro_offset; + guint32 backup_rw_offset; + + /* The below fields are present in versions 4 and up. */ + /* + * Versions of the currently active RO and RW sections. + */ + struct signed_header_version shv[2]; + + /* The below fields are present in versions 5 and up */ + /* keyids of the currently active RO and RW sections. */ + guint32 keyid[2]; + } cr50; + /* Common code (header_type = UPDATE_HEADER_TYPE_COMMON) */ + struct { + /* Maximum PDU size */ + guint32 maximum_pdu_size; + + /* Flash protection status */ + guint32 flash_protection; + + /* Offset of the other region */ + guint32 offset; + + /* Version string of the other region */ + gchar version[FU_CROS_EC_STRLEN]; + + /* Minimum rollback version that RO will accept */ + gint32 min_rollback; + + /* RO public key version */ + guint32 key_version; + } common; + }; +}; + +enum first_response_pdu_header_type { + UPDATE_HEADER_TYPE_CR50 = 0, /* Must be 0 for backwards compatibility */ + UPDATE_HEADER_TYPE_COMMON = 1, +}; + +struct cros_ec_version { + gchar boardname[FU_CROS_EC_STRLEN]; + gchar triplet[FU_CROS_EC_STRLEN]; + gchar sha1[FU_CROS_EC_STRLEN]; + gboolean dirty; +}; + +gboolean fu_cros_ec_parse_version (const gchar *version_raw, + struct cros_ec_version *version, + GError **error); diff --git a/plugins/cros-ec/fu-cros-ec-firmware.c b/plugins/cros-ec/fu-cros-ec-firmware.c new file mode 100644 index 000000000..12001174c --- /dev/null +++ b/plugins/cros-ec/fu-cros-ec-firmware.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-fmap-firmware.h" +#include "fu-cros-ec-common.h" +#include "fu-cros-ec-firmware.h" + +#define MAXSECTIONS 2 + +struct _FuCrosEcFirmware { + FuFmapFirmware parent_instance; + struct cros_ec_version version; + GPtrArray *sections; +}; + +G_DEFINE_TYPE (FuCrosEcFirmware, fu_cros_ec_firmware, FU_TYPE_FMAP_FIRMWARE) + +gboolean +fu_cros_ec_firmware_pick_sections (FuCrosEcFirmware *self, + guint32 writeable_offset, + GError **error) +{ + gboolean found = FALSE; + + for (gsize i = 0; i < self->sections->len; i++) { + FuCrosEcFirmwareSection *section = g_ptr_array_index (self->sections, i); + guint32 offset = section->offset; + + if (offset != writeable_offset) + continue; + + section->ustatus = FU_CROS_EC_FW_NEEDED; + found = TRUE; + } + + if (!found) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no writeable section found with offset: 0x%x", + writeable_offset); + return FALSE; + } + + /* success */ + return TRUE; +} + +GPtrArray * +fu_cros_ec_firmware_get_sections (FuCrosEcFirmware *self) +{ + return self->sections; +} + +static gboolean +fu_cros_ec_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + FuCrosEcFirmware *self = FU_CROS_EC_FIRMWARE (firmware); + FuFirmware *fmap_firmware = FU_FIRMWARE (firmware); + + for (gsize i = 0; i < self->sections->len; i++) { + gboolean rw = FALSE; + FuCrosEcFirmwareSection *section = g_ptr_array_index (self->sections, i); + const gchar *fmap_name; + const gchar *fmap_fwid_name; + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(FuFirmwareImage) fwid_img = NULL; + g_autoptr(GBytes) payload_bytes = NULL; + g_autoptr(GBytes) fwid_bytes = NULL; + + if (g_strcmp0 (section->name, "RO") == 0) { + fmap_name = "EC_RO"; + fmap_fwid_name = "RO_FRID"; + } else if (g_strcmp0 (section->name, "RW") == 0) { + rw = TRUE; + fmap_name = "EC_RW"; + fmap_fwid_name = "RW_FWID"; + } else { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "incorrect section name"); + return FALSE; + } + + img = fu_firmware_get_image_by_id (fmap_firmware, + fmap_name, error); + if (img == NULL) { + g_prefix_error (error, "%s image not found: ", + fmap_name); + return FALSE; + } + + fwid_img = fu_firmware_get_image_by_id (fmap_firmware, + fmap_fwid_name, error); + if (fwid_img == NULL) { + g_prefix_error (error, "%s image not found: ", + fmap_fwid_name); + return FALSE; + } + fwid_bytes = fu_firmware_image_write (fwid_img, error); + if (fwid_bytes == NULL) { + g_prefix_error (error, + "unable to get bytes from %s: ", + fmap_fwid_name); + return FALSE; + } + if (!fu_memcpy_safe ((guint8 *) section->raw_version, + FU_FMAP_FIRMWARE_STRLEN, 0x0, + g_bytes_get_data (fwid_bytes, NULL), + g_bytes_get_size (fwid_bytes), 0x0, + g_bytes_get_size (fwid_bytes), error)) + return FALSE; + + payload_bytes = fu_firmware_image_write (img, error); + if (payload_bytes == NULL) { + g_prefix_error (error, + "unable to get bytes from %s: ", + fmap_name); + return FALSE; + } + section->offset = fu_firmware_image_get_addr (img); + section->size = g_bytes_get_size (payload_bytes); + fu_firmware_image_set_version (img, section->raw_version); + section->image_idx = fu_firmware_image_get_idx (img); + + if (!fu_cros_ec_parse_version (section->raw_version, + §ion->version, + error)) { + g_prefix_error (error, + "failed parsing firmware's version: %32s: ", + section->raw_version); + return FALSE; + } + + if (rw) { + if (!fu_cros_ec_parse_version (section->raw_version, + &self->version, + error)) { + g_prefix_error (error, + "failed parsing firmware's version: %32s: ", + section->raw_version); + return FALSE; + } + fu_firmware_set_version (firmware, + self->version.triplet); + } + } + + /* success */ + return TRUE; +} + +static void +fu_cros_ec_firmware_init (FuCrosEcFirmware *self) +{ + FuCrosEcFirmwareSection *section; + + self->sections = g_ptr_array_new_with_free_func (g_free); + section = g_new0 (FuCrosEcFirmwareSection, 1); + section->name = "RO"; + g_ptr_array_add (self->sections, section); + section = g_new0 (FuCrosEcFirmwareSection, 1); + section->name = "RW"; + g_ptr_array_add (self->sections, section); +} + +static void +fu_cros_ec_firmware_finalize (GObject *object) +{ + FuCrosEcFirmware *self = FU_CROS_EC_FIRMWARE (object); + g_ptr_array_free (self->sections, TRUE); + G_OBJECT_CLASS (fu_cros_ec_firmware_parent_class)->finalize (object); +} + +static void +fu_cros_ec_firmware_class_init (FuCrosEcFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuFmapFirmwareClass *klass_firmware = FU_FMAP_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_cros_ec_firmware_parse; + object_class->finalize = fu_cros_ec_firmware_finalize; +} + +FuFirmware * +fu_cros_ec_firmware_new (void) +{ + return g_object_new (FU_TYPE_CROS_EC_FIRMWARE, NULL); +} diff --git a/plugins/cros-ec/fu-cros-ec-firmware.h b/plugins/cros-ec/fu-cros-ec-firmware.h new file mode 100644 index 000000000..e6aaebef2 --- /dev/null +++ b/plugins/cros-ec/fu-cros-ec-firmware.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" +#include "fu-fmap-firmware.h" +#include "fu-cros-ec-common.h" + +#define FU_TYPE_CROS_EC_FIRMWARE (fu_cros_ec_firmware_get_type ()) +G_DECLARE_FINAL_TYPE (FuCrosEcFirmware, fu_cros_ec_firmware, FU, CROS_EC_FIRMWARE, FuFmapFirmware) + +/* + * Each RO or RW section of the new image can be in one of the following + * states. + */ +typedef enum { + FU_CROS_EC_FW_NOT_NEEDED= 0, /* Version below or equal that on the target. */ + FU_CROS_EC_FW_NOT_POSSIBLE, /* + * RO is newer, but can't be transferred due to + * target RW shortcomings. + */ + FU_CROS_EC_FW_NEEDED /* + * This section needs to be transferred to the + * target. + */ +} FuCrosEcFirmwareUpgradeStatus; + +typedef struct { + const gchar *name; + guint32 offset; + gsize size; + FuCrosEcFirmwareUpgradeStatus ustatus; + gchar raw_version[FU_FMAP_FIRMWARE_STRLEN]; + struct cros_ec_version version; + gint32 rollback; + guint32 key_version; + guint64 image_idx; +} FuCrosEcFirmwareSection; + +gboolean fu_cros_ec_firmware_pick_sections (FuCrosEcFirmware *self, + guint32 writeable_offset, + GError **error); +GPtrArray *fu_cros_ec_firmware_get_sections (FuCrosEcFirmware *self); +FuFirmware *fu_cros_ec_firmware_new (void); diff --git a/plugins/cros-ec/fu-cros-ec-usb-device.c b/plugins/cros-ec/fu-cros-ec-usb-device.c new file mode 100644 index 000000000..b9a2de7dd --- /dev/null +++ b/plugins/cros-ec/fu-cros-ec-usb-device.c @@ -0,0 +1,984 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-chunk.h" +#include "fu-cros-ec-usb-device.h" +#include "fu-cros-ec-common.h" +#include "fu-cros-ec-firmware.h" + +#define USB_SUBCLASS_GOOGLE_UPDATE 0x53 +#define USB_PROTOCOL_GOOGLE_UPDATE 0xff + +#define SETUP_RETRY_CNT 5 +#define MAX_BLOCK_XFER_RETRIES 10 +#define FLUSH_TIMEOUT_MS 10 +#define BULK_SEND_TIMEOUT_MS 2000 +#define BULK_RECV_TIMEOUT_MS 5000 +#define CROS_EC_REMOVE_DELAY_RE_ENUMERATE 20000 + +#define UPDATE_DONE 0xB007AB1E +#define UPDATE_EXTRA_CMD 0xB007AB1F + +enum update_extra_command { + UPDATE_EXTRA_CMD_IMMEDIATE_RESET = 0, + UPDATE_EXTRA_CMD_JUMP_TO_RW = 1, + UPDATE_EXTRA_CMD_STAY_IN_RO = 2, + UPDATE_EXTRA_CMD_UNLOCK_RW = 3, + UPDATE_EXTRA_CMD_UNLOCK_ROLLBACK = 4, + UPDATE_EXTRA_CMD_INJECT_ENTROPY = 5, + UPDATE_EXTRA_CMD_PAIR_CHALLENGE = 6, + UPDATE_EXTRA_CMD_TOUCHPAD_INFO = 7, + UPDATE_EXTRA_CMD_TOUCHPAD_DEBUG = 8, + UPDATE_EXTRA_CMD_CONSOLE_READ_INIT = 9, + UPDATE_EXTRA_CMD_CONSOLE_READ_NEXT = 10, +}; + +struct _FuCrosEcUsbDevice { + FuUsbDevice parent_instance; + guint8 iface_idx; /* bInterfaceNumber */ + guint8 ep_num; /* bEndpointAddress */ + guint16 chunk_len; /* wMaxPacketSize */ + + struct first_response_pdu targ; + guint32 writeable_offset; + guint16 protocol_version; + guint16 header_type; + struct cros_ec_version version; /* version of other region */ + struct cros_ec_version active_version; /* version of active region */ + gchar configuration[FU_CROS_EC_STRLEN]; + gboolean in_bootloader; +}; + +G_DEFINE_TYPE (FuCrosEcUsbDevice, fu_cros_ec_usb_device, FU_TYPE_USB_DEVICE) + +typedef union _START_RESP { + struct first_response_pdu rpdu; + guint32 legacy_resp; +} START_RESP; + +typedef struct { + struct update_frame_header ufh; + GBytes *image_bytes; + gsize offset; + gsize payload_size; +} FuCrosEcUsbBlockInfo; + +static gboolean +fu_cros_ec_usb_device_get_configuration (FuCrosEcUsbDevice *self, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + guint8 index; + g_autofree gchar *configuration = NULL; + +#if G_USB_CHECK_VERSION(0,3,5) + index = g_usb_device_get_configuration_index (usb_device); +#else + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "this version of GUsb is not supported"); + return FALSE; +#endif + configuration = g_usb_device_get_string_descriptor (usb_device, + index, + error); + if (configuration == NULL) + return FALSE; + + if (g_strlcpy (self->configuration, configuration, FU_CROS_EC_STRLEN) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "empty iConfiguration"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_find_interface (FuUsbDevice *device, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + g_autoptr(GPtrArray) intfs = NULL; + + /* based on usb_updater2's find_interfacei() and find_endpoint() */ + + intfs = g_usb_device_get_interfaces (usb_device, error); + if (intfs == NULL) + return FALSE; + for (guint i = 0; i < intfs->len; i++) { + GUsbInterface *intf = g_ptr_array_index (intfs, i); + if (g_usb_interface_get_class (intf) == 255 && + g_usb_interface_get_subclass (intf) == USB_SUBCLASS_GOOGLE_UPDATE && + g_usb_interface_get_protocol (intf) == USB_PROTOCOL_GOOGLE_UPDATE) { + GUsbEndpoint *ep; + g_autoptr(GPtrArray) endpoints = NULL; + + endpoints = g_usb_interface_get_endpoints (intf); + if (NULL == endpoints || 0 == endpoints->len) + continue; + ep = g_ptr_array_index (endpoints, 0); + self->iface_idx = g_usb_interface_get_number (intf); + self->ep_num = g_usb_endpoint_get_address (ep) & 0x7f; + self->chunk_len = g_usb_endpoint_get_maximum_packet_size (ep); + + return TRUE; + } + } + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no update interface found"); + return FALSE; +} + +static gboolean +fu_cros_ec_usb_device_open (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + + if (!g_usb_device_claim_interface (usb_device, self->iface_idx, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to claim interface: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_probe (FuUsbDevice *device, GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + + /* very much like usb_updater2's usb_findit() */ + + if (!fu_cros_ec_usb_device_find_interface (device, error)) { + g_prefix_error (error, "failed to find update interface: "); + return FALSE; + } + + if (self->chunk_len == 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "wMaxPacketSize isn't valid: %" G_GUINT16_FORMAT, + self->chunk_len); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_do_xfer (FuCrosEcUsbDevice * self, guint8 *outbuf, + gsize outlen, guint8 *inbuf, gsize inlen, + gboolean allow_less, gsize *rxed_count, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize actual = 0; + + /* send data out */ + if (outbuf != NULL && outlen > 0) { + if (!g_usb_device_bulk_transfer (usb_device, self->ep_num, + outbuf, outlen, + &actual, BULK_SEND_TIMEOUT_MS, + NULL, error)) { + return FALSE; + } + if (actual != outlen) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + "only sent %" G_GSIZE_FORMAT "/%" + G_GSIZE_FORMAT " bytes", + actual, outlen); + return FALSE; + } + } + + /* read reply back */ + if (inbuf != NULL && inlen > 0) { + actual = 0; + if (!g_usb_device_bulk_transfer (usb_device, + self->ep_num | 0x80, + inbuf, inlen, + &actual, BULK_RECV_TIMEOUT_MS, + NULL, error)) { + return FALSE; + } + if (actual != inlen && !allow_less) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + "only received %" G_GSIZE_FORMAT "/%" + G_GSIZE_FORMAT " bytes", + actual, outlen); + return FALSE; + } + } + + if (rxed_count != NULL) + *rxed_count = actual; + + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_flush (FuDevice *device, gpointer user_data, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + gsize actual = 0; + g_autofree guint8 *inbuf = g_malloc0 (self->chunk_len); + + if (g_usb_device_bulk_transfer (usb_device, self->ep_num | 0x80, inbuf, + self->chunk_len, &actual, + FLUSH_TIMEOUT_MS, NULL, NULL)) { + g_debug ("flushing %" G_GSIZE_FORMAT " bytes", actual); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "flushing %" G_GSIZE_FORMAT " bytes", actual); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_recovery (FuDevice *device, GError **error) +{ + /* flush all data from endpoint to recover in case of error */ + if (!fu_device_retry (device, fu_cros_ec_usb_device_flush, + SETUP_RETRY_CNT, NULL, error)) { + g_prefix_error (error, "failed to flush device to idle state: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +/* + * Channel TPM extension/vendor command over USB. The payload of the USB frame + * in this case consists of the 2 byte subcommand code concatenated with the + * command body. The caller needs to indicate if a response is expected, and + * if it is - of what maximum size. + */ +static gboolean +fu_cros_ec_usb_ext_cmd (FuDevice *device, guint16 subcommand, + gpointer cmd_body, gsize body_size, + gpointer resp, gsize *resp_size, + gboolean allow_less, GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + guint16 *frame_ptr; + gsize usb_msg_size = sizeof (struct update_frame_header) + + sizeof (subcommand) + body_size; + g_autofree struct update_frame_header *ufh = g_malloc0 (usb_msg_size); + + ufh->block_size = GUINT32_TO_BE (usb_msg_size); + ufh->cmd.block_digest = 0; + ufh->cmd.block_base = GUINT32_TO_BE (UPDATE_EXTRA_CMD); + frame_ptr = (guint16 *)(ufh + 1); + *frame_ptr = GUINT16_TO_BE (subcommand); + + if (body_size != 0) { + gsize offset = sizeof (struct update_frame_header) + sizeof (subcommand); + if (!fu_memcpy_safe ((guint8 *) ufh, usb_msg_size, offset, + (const guint8 *) cmd_body, body_size, + 0x0, body_size, error)) + return FALSE; + } + + return fu_cros_ec_usb_device_do_xfer (self, (guint8 *)ufh, usb_msg_size, + (guint8 *)resp, + resp_size != NULL ? *resp_size : 0, + TRUE, NULL, error); +} + +static gboolean +fu_cros_ec_usb_device_start_request (FuDevice *device, gpointer user_data, + GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + guint8 *start_resp = (guint8 *) user_data; + struct update_frame_header ufh; + gsize rxed_size = 0; + + memset(&ufh, 0, sizeof (ufh)); + ufh.block_size = GUINT32_TO_BE (sizeof(ufh)); + if (!fu_cros_ec_usb_device_do_xfer (self, (guint8 *)&ufh, sizeof(ufh), + start_resp, + sizeof(START_RESP), TRUE, + &rxed_size, error)) + return FALSE; + + /* we got something, so check for errors in response */ + if (rxed_size < 8) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + "unexpected response size %" G_GSIZE_FORMAT, + rxed_size); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_setup (FuDevice *device, GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + guint32 error_code; + START_RESP start_resp; + g_auto(GStrv) config_split = NULL; + + if (!fu_cros_ec_usb_device_recovery (device, error)) + return FALSE; + + /* send start request */ + if (!fu_device_retry (device, fu_cros_ec_usb_device_start_request, + SETUP_RETRY_CNT, &start_resp, error)) { + g_prefix_error (error, "failed to send start request: "); + return FALSE; + } + + self->protocol_version = GUINT16_FROM_BE (start_resp.rpdu.protocol_version); + + if (self->protocol_version < 5 || self->protocol_version > 6) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "unsupported protocol version %d", + self->protocol_version); + return FALSE; + } + self->header_type = GUINT16_FROM_BE (start_resp.rpdu.header_type); + + error_code = GUINT32_FROM_BE (start_resp.rpdu.return_value); + if (error_code != 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "target reporting error %u", error_code); + return FALSE; + } + + self->writeable_offset = GUINT32_FROM_BE (start_resp.rpdu.common.offset); + if (!fu_memcpy_safe ((guint8 *) self->targ.common.version, + FU_CROS_EC_STRLEN, 0x0, + (const guint8 *) start_resp.rpdu.common.version, + sizeof(start_resp.rpdu.common.version), 0x0, + sizeof(start_resp.rpdu.common.version), error)) + return FALSE; + self->targ.common.maximum_pdu_size = + GUINT32_FROM_BE (start_resp.rpdu.common.maximum_pdu_size); + self->targ.common.flash_protection = + GUINT32_FROM_BE (start_resp.rpdu.common.flash_protection); + self->targ.common.min_rollback = GINT32_FROM_BE (start_resp.rpdu.common.min_rollback); + self->targ.common.key_version = GUINT32_FROM_BE (start_resp.rpdu.common.key_version); + + /* get active version string and running region from iConfiguration */ + if (!fu_cros_ec_usb_device_get_configuration (self, error)) + return FALSE; + config_split = g_strsplit (self->configuration, ":", 2); + if (g_strv_length (config_split) < 2) { + /* no prefix found so fall back to offset */ + self->in_bootloader = self->writeable_offset != 0x0; + if (!fu_cros_ec_parse_version (self->configuration, + &self->active_version, error)) { + g_prefix_error (error, + "failed parsing device's version: %32s: ", + self->configuration); + return FALSE; + } + } else { + self->in_bootloader = g_strcmp0 ("RO", config_split[0]) == 0; + if (!fu_cros_ec_parse_version (config_split[1], + &self->active_version, error)) { + g_prefix_error (error, + "failed parsing device's version: %32s: ", + config_split[1]); + return FALSE; + } + } + + /* get the other region's version string from targ */ + if (!fu_cros_ec_parse_version (self->targ.common.version, + &self->version, error)) { + g_prefix_error (error, + "failed parsing device's version: %32s: ", + self->targ.common.version); + return FALSE; + } + + if (self->in_bootloader) { + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_set_version (FU_DEVICE (device), self->version.triplet); + fu_device_set_version_bootloader (FU_DEVICE (device), + self->active_version.triplet); + } else { + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_set_version (FU_DEVICE (device), self->active_version.triplet); + fu_device_set_version_bootloader (FU_DEVICE (device), + self->version.triplet); + } + fu_device_add_instance_id (FU_DEVICE (device), self->version.boardname); + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_transfer_block (FuDevice *device, gpointer user_data, + GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + FuCrosEcUsbBlockInfo *block_info = (FuCrosEcUsbBlockInfo *) user_data; + gsize image_size = 0; + gsize transfer_size = 0; + guint32 reply = 0; + g_autoptr(GBytes) block_bytes = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + g_return_val_if_fail (block_info != NULL, FALSE); + + image_size = g_bytes_get_size (block_info->image_bytes); + if (block_info->offset + block_info->payload_size > image_size) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "offset %" G_GSIZE_FORMAT "plus payload_size %" + G_GSIZE_FORMAT " exceeds image size %" + G_GSIZE_FORMAT, + block_info->offset, block_info->payload_size, + image_size); + return FALSE; + } + + block_bytes = g_bytes_new_from_bytes (block_info->image_bytes, + block_info->offset, + block_info->payload_size); + chunks = fu_chunk_array_new_from_bytes (block_bytes, + 0x00, + 0x00, + self->chunk_len); + + /* first send the header */ + if (!fu_cros_ec_usb_device_do_xfer (self, (guint8 *)&block_info->ufh, + sizeof(struct update_frame_header), + NULL, + 0, FALSE, + NULL, error)) { + /* flush all data from endpoint to recover in case of error */ + if (!fu_cros_ec_usb_device_recovery (device, NULL)) { + g_debug ("failed to flush to idle"); + } + g_prefix_error (error, "failed at sending header: "); + return FALSE; + } + + /* send the block, chunk by chunk */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + + if (!fu_cros_ec_usb_device_do_xfer (self, + (guint8 *)chk->data, + chk->data_sz, + NULL, + 0, FALSE, + NULL, error)) { + g_prefix_error (error, "failed at sending chunk: "); + + /* flush all data from endpoint to recover in case of error */ + if (!fu_cros_ec_usb_device_recovery (device, NULL)) { + g_debug ("failed to flush to idle"); + } + return FALSE; + } + } + + /* get the reply */ + if (!fu_cros_ec_usb_device_do_xfer (self, NULL, 0, + (guint8 *)&reply, sizeof (reply), + TRUE, &transfer_size, error)) { + g_prefix_error (error, "failed at reply: "); + /* flush all data from endpoint to recover in case of error */ + if (!fu_cros_ec_usb_device_recovery (device, NULL)) { + g_debug ("failed to flush to idle"); + } + return FALSE; + } + if (transfer_size == 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "zero bytes received for block reply"); + return FALSE; + } + if (reply != 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "error: status 0x%#x", reply); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_transfer_section (FuDevice *device, + FuFirmware *firmware, + FuCrosEcFirmwareSection *section, + GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + const guint8 * data_ptr = NULL; + guint32 section_addr = 0; + gsize data_len = 0; + gsize offset = 0; + g_autoptr(GBytes) img_bytes = NULL; + + g_return_val_if_fail (section != NULL, FALSE); + + section_addr = section->offset; + img_bytes = fu_firmware_get_image_by_idx_bytes (firmware, + section->image_idx, + error); + if (img_bytes == NULL) { + g_prefix_error (error, "failed to find section image: "); + return FALSE; + } + + data_ptr = (const guint8 *)g_bytes_get_data (img_bytes, &data_len); + if (data_ptr == NULL || data_len != section->size) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "image and section sizes do not match: image = %" + G_GSIZE_FORMAT " bytes vs section size = %" + G_GSIZE_FORMAT " bytes", + data_len, section->size); + return FALSE; + } + + /* smart update: trim trailing bytes */ + while (data_len != 0 && (data_ptr[data_len - 1] == 0xff)) + data_len--; + g_debug ("trimmed %" G_GSIZE_FORMAT " trailing bytes", + section->size - data_len); + + g_debug ("sending 0x%zx bytes to %#x", data_len, section_addr); + while (data_len > 0) { + gsize payload_size; + guint32 block_base; + FuCrosEcUsbBlockInfo block_info; + + /* prepare the header to prepend to the block */ + block_info.image_bytes = img_bytes; + payload_size = MIN (data_len, + self->targ.common.maximum_pdu_size); + block_base = GUINT32_TO_BE (section_addr); + block_info.ufh.block_size = GUINT32_TO_BE (payload_size + + sizeof (struct update_frame_header)); + block_info.ufh.cmd.block_base = block_base; + block_info.ufh.cmd.block_digest = 0; + block_info.offset = offset; + block_info.payload_size = payload_size; + + if (!fu_device_retry (device, + fu_cros_ec_usb_device_transfer_block, + MAX_BLOCK_XFER_RETRIES, &block_info, + error)) { + g_prefix_error (error, + "failed to transfer block, %" + G_GSIZE_FORMAT " to go: ", data_len); + return FALSE; + } + data_len -= payload_size; + offset += payload_size; + section_addr += payload_size; + } + + /* success */ + return TRUE; +} + +static void +fu_cros_ec_usb_device_send_done (FuDevice *device) +{ + guint32 out = GUINT32_TO_BE (UPDATE_DONE); + g_autoptr(GError) error_local = NULL; + + /* send stop request, ignoring reply */ + if (!fu_cros_ec_usb_device_do_xfer (FU_CROS_EC_USB_DEVICE (device), + (guint8 *)&out, sizeof (out), + (guint8 *)&out, 1, + FALSE, NULL, &error_local)) { + g_debug ("error on transfer of done: %s", + error_local->message); + } +} + +static gboolean +fu_cros_ec_usb_device_send_subcommand (FuDevice *device, guint16 subcommand, + gpointer cmd_body, gsize body_size, + gpointer resp, gsize *resp_size, + gboolean allow_less, GError **error) +{ + fu_cros_ec_usb_device_send_done (device); + + if (!fu_cros_ec_usb_ext_cmd (device, subcommand, + cmd_body, body_size, + resp, resp_size, FALSE, error)) { + g_prefix_error (error, + "failed to send subcommand %" G_GUINT16_FORMAT ": ", + subcommand); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_reset_to_ro (FuDevice *device, GError **error) +{ + guint8 response; + guint16 subcommand = UPDATE_EXTRA_CMD_IMMEDIATE_RESET; + guint8 command_body[2]; /* Max command body size. */ + gsize command_body_size = 0; + gsize response_size = 1; + + if (fu_device_has_custom_flag (device, "ro-written")) + fu_device_set_custom_flags (device, "ro-written,rebooting-to-ro"); + else + fu_device_set_custom_flags (device, "rebooting-to-ro"); + if (!fu_cros_ec_usb_device_send_subcommand (device, subcommand, command_body, + command_body_size, &response, + &response_size, FALSE, error)) { + /* failure here is ok */ + g_clear_error (error); + return TRUE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_jump_to_rw (FuDevice *device) +{ + guint8 response; + guint16 subcommand = UPDATE_EXTRA_CMD_JUMP_TO_RW; + guint8 command_body[2]; /* Max command body size. */ + gsize command_body_size = 0; + gsize response_size = 1; + + if (!fu_cros_ec_usb_device_send_subcommand (device, subcommand, command_body, + command_body_size, &response, + &response_size, FALSE, NULL)) { + /* bail out early here if subcommand failed, which is normal */ + return TRUE; + } + + /* Jump to rw may not work, so if we've reached here, initiate a + * full reset using immediate reset */ + subcommand = UPDATE_EXTRA_CMD_IMMEDIATE_RESET; + fu_cros_ec_usb_device_send_subcommand (device, subcommand, command_body, + command_body_size, &response, + &response_size, FALSE, NULL); + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_write_firmware (FuDevice *device, + FuFirmware *firmware, + FwupdInstallFlags flags, + GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + GPtrArray *sections; + FuCrosEcFirmware *cros_ec_firmware = FU_CROS_EC_FIRMWARE (firmware); + gint num_txed_sections = 0; + + if (fu_device_has_custom_flag (device, "rebooting-to-ro")) { + gsize response_size = 1; + guint8 response; + guint16 subcommand = UPDATE_EXTRA_CMD_STAY_IN_RO; + guint8 command_body[2]; /* Max command body size. */ + gsize command_body_size = 0; + START_RESP start_resp; + + if (!fu_cros_ec_usb_device_send_subcommand (device, subcommand, command_body, + command_body_size, &response, + &response_size, FALSE, error)) { + g_prefix_error (error, "failed to send stay-in-ro subcommand: "); + return FALSE; + } + + /* flush all data from endpoint to recover in case of error */ + if (!fu_cros_ec_usb_device_recovery (device, error)) { + g_prefix_error (error, "failed to flush device to idle state: "); + return FALSE; + } + + /* send start request */ + if (!fu_device_retry (device, fu_cros_ec_usb_device_start_request, + SETUP_RETRY_CNT, &start_resp, error)) { + g_prefix_error (error, "failed to send start request: "); + return FALSE; + } + } + + if (fu_device_has_custom_flag (device, "rw-written") && self->in_bootloader) { + /* + * we had previously written to the rw region but somehow + * ended back up here while still in bootloader; this is + * a transitory state due to the fact that we have to boot + * through RO to get to RW. Set another write required to + * allow the RO region to auto-jump to RW + */ + fu_device_set_custom_flags (device, "special,rw-written"); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); + return TRUE; + } + + sections = fu_cros_ec_firmware_get_sections (cros_ec_firmware); + if (sections == NULL) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid sections"); + return FALSE; + } + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < sections->len; i++) { + FuCrosEcFirmwareSection *section = g_ptr_array_index (sections, i); + + if (section->ustatus == FU_CROS_EC_FW_NEEDED) { + if (!fu_cros_ec_usb_device_transfer_section (device, + firmware, + section, + error)) { + return FALSE; + } + num_txed_sections++; + + if (self->in_bootloader) { + fu_device_set_version (FU_DEVICE (device), + section->version.triplet); + } else { + fu_device_set_version_bootloader (FU_DEVICE (device), + section->version.triplet); + } + } + } + /* send done */ + fu_cros_ec_usb_device_send_done (device); + + if (num_txed_sections == 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no sections transferred"); + return FALSE; + } + + if (self->in_bootloader) { + if (fu_device_has_custom_flag (device, "ro-written")) + fu_device_set_custom_flags (device, "ro-written,rw-written"); + else + fu_device_set_custom_flags (device, "rw-written"); + } else if (fu_device_has_custom_flag (device, "rw-written")) { + fu_device_set_custom_flags (device, "ro-written,rw-written"); + } else { + fu_device_set_custom_flags (device, "ro-written"); + } + + /* logical XOR */ + if (fu_device_has_custom_flag (device, "rw-written") != + fu_device_has_custom_flag (device, "ro-written")) + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_close (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + + if (!g_usb_device_release_interface (usb_device, self->iface_idx, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to release interface: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_cros_ec_usb_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + FuCrosEcFirmware *cros_ec_firmware = NULL; + g_autoptr(FuFirmware) firmware = fu_cros_ec_firmware_new (); + + if (!fu_firmware_parse (firmware, fw, flags, error)) + return NULL; + cros_ec_firmware = FU_CROS_EC_FIRMWARE (firmware); + + /* pick sections */ + if (!fu_cros_ec_firmware_pick_sections (cros_ec_firmware, + self->writeable_offset, + error)) { + g_prefix_error (error, "failed to pick sections: "); + return NULL; + } + return g_steal_pointer (&firmware); +} + +static gboolean +fu_cros_ec_usb_device_attach (FuDevice *device, GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + + if (self->in_bootloader && fu_device_has_custom_flag (device, "special")) { + fu_device_set_remove_delay (device, CROS_EC_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; + } + + fu_device_set_remove_delay (device, CROS_EC_REMOVE_DELAY_RE_ENUMERATE); + if (fu_device_has_custom_flag (device, "ro-written") && + !fu_device_has_custom_flag (device, "rw-written")) { + if (!fu_cros_ec_usb_device_reset_to_ro (device, error)) { + return FALSE; + } + } else { + fu_cros_ec_usb_device_jump_to_rw (device); + } + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_detach (FuDevice *device, GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + + if (fu_device_has_custom_flag (device, "rw-written") && + !fu_device_has_custom_flag (device, "ro-written")) + return TRUE; + + if (self->in_bootloader) { + g_debug ("skipping immediate reboot in case of already in bootloader"); + /* in RO so skip reboot */ + return TRUE; + } else if (self->targ.common.flash_protection != 0x0) { + /* in RW, and RO region is write protected, so jump to RO */ + fu_device_set_custom_flags (device, "ro-written"); + fu_device_set_remove_delay (device, CROS_EC_REMOVE_DELAY_RE_ENUMERATE); + if (!fu_cros_ec_usb_device_reset_to_ro (device, error)) + return FALSE; + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } + + /* success */ + return TRUE; +} + +static void +fu_cros_ec_usb_device_init (FuCrosEcUsbDevice *device) +{ + fu_device_set_protocol (FU_DEVICE (device), "com.google.usb.crosec"); + fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_version_format (FU_DEVICE (device), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_DUAL_IMAGE); +} + +static void +fu_cros_ec_usb_device_to_string (FuDevice *device, guint idt, GString *str) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE (device); + g_autofree gchar *min_rollback = NULL; + + fu_common_string_append_kv (str, idt, "GitHash", self->version.sha1); + fu_common_string_append_kb (str, idt, "Dirty", + self->version.dirty); + fu_common_string_append_ku (str, idt, "ProtocolVersion", + self->protocol_version); + fu_common_string_append_ku (str, idt, "HeaderType", + self->header_type); + fu_common_string_append_ku (str, idt, "MaxPDUSize", + self->targ.common.maximum_pdu_size); + fu_common_string_append_kx (str, idt, "FlashProtectionStatus", + self->targ.common.flash_protection); + fu_common_string_append_kv (str, idt, "RawVersion", + self->targ.common.version); + fu_common_string_append_ku (str, idt, "KeyVersion", + self->targ.common.key_version); + min_rollback = g_strdup_printf ("%" G_GINT32_FORMAT, + self->targ.common.min_rollback); + fu_common_string_append_kv (str, idt, "MinRollback", min_rollback); + fu_common_string_append_kx (str, idt, "WriteableOffset", + self->writeable_offset); +} + +static void +fu_cros_ec_usb_device_class_init (FuCrosEcUsbDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + klass_device->attach = fu_cros_ec_usb_device_attach; + klass_device->detach = fu_cros_ec_usb_device_detach; + klass_device->prepare_firmware = fu_cros_ec_usb_device_prepare_firmware; + klass_device->setup = fu_cros_ec_usb_device_setup; + klass_device->to_string = fu_cros_ec_usb_device_to_string; + klass_device->write_firmware = fu_cros_ec_usb_device_write_firmware; + klass_usb_device->open = fu_cros_ec_usb_device_open; + klass_usb_device->probe = fu_cros_ec_usb_device_probe; + klass_usb_device->close = fu_cros_ec_usb_device_close; +} diff --git a/plugins/cros-ec/fu-cros-ec-usb-device.h b/plugins/cros-ec/fu-cros-ec-usb-device.h new file mode 100644 index 000000000..ea3785b43 --- /dev/null +++ b/plugins/cros-ec/fu-cros-ec-usb-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +#define FU_TYPE_CROS_EC_USB_DEVICE (fu_cros_ec_usb_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuCrosEcUsbDevice, fu_cros_ec_usb_device, FU, CROS_EC_USB_DEVICE, FuUsbDevice) + +struct _FuCrosEcUsbDeviceClass +{ + FuUsbDeviceClass parent_class; +}; diff --git a/plugins/cros-ec/fu-plugin-cros-ec.c b/plugins/cros-ec/fu-plugin-cros-ec.c new file mode 100644 index 000000000..5c1f430c1 --- /dev/null +++ b/plugins/cros-ec/fu-plugin-cros-ec.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" + +#include "fu-cros-ec-usb-device.h" +#include "fu-cros-ec-firmware.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_set_device_gtype (plugin, FU_TYPE_CROS_EC_USB_DEVICE); + fu_plugin_add_firmware_gtype (plugin, "cros-ec", FU_TYPE_CROS_EC_FIRMWARE); +} diff --git a/plugins/cros-ec/meson.build b/plugins/cros-ec/meson.build new file mode 100644 index 000000000..643aa9d1e --- /dev/null +++ b/plugins/cros-ec/meson.build @@ -0,0 +1,30 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginCrosEc"'] + +install_data(['cros-ec.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_cros_ec', + fu_hash, + sources : [ + 'fu-plugin-cros-ec.c', + 'fu-cros-ec-usb-device.c', + 'fu-cros-ec-common.c', + 'fu-cros-ec-firmware.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/csr/README.md b/plugins/csr/README.md index 0d013af4b..72286c323 100644 --- a/plugins/csr/README.md +++ b/plugins/csr/README.md @@ -39,3 +39,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, in this instance set to `USB:0x0A12` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/csr/fu-csr-device.c b/plugins/csr/fu-csr-device.c index def8cb83a..e4a540875 100644 --- a/plugins/csr/fu-csr-device.c +++ b/plugins/csr/fu-csr-device.c @@ -188,12 +188,11 @@ fu_csr_device_upload_chunk (FuCsrDevice *self, GError **error) sizeof(buf) - FU_CSR_COMMAND_HEADER_SIZE); } -static FuFirmware * +static GBytes * fu_csr_device_upload (FuDevice *device, GError **error) { FuCsrDevice *self = FU_CSR_DEVICE (device); g_autoptr(GPtrArray) chunks = NULL; - g_autoptr(GBytes) fw = NULL; guint32 total_sz = 0; gsize done_sz = 0; @@ -253,8 +252,7 @@ fu_csr_device_upload (FuDevice *device, GError **error) } /* notify UI */ - fw = dfu_utils_bytes_join_array (chunks); - return fu_firmware_new_from_bytes (fw); + return dfu_utils_bytes_join_array (chunks); } static gboolean @@ -397,7 +395,7 @@ fu_csr_device_probe (FuUsbDevice *device, GError **error) { FuCsrDevice *self = FU_CSR_DEVICE (device); - /* devices have to be whitelisted */ + /* proxy the quirk delay */ if (fu_device_has_custom_flag (FU_DEVICE (device), FU_CSR_DEVICE_FLAG_REQUIRE_DELAY)) self->quirks = FU_CSR_DEVICE_QUIRK_REQUIRE_DELAY; @@ -434,7 +432,7 @@ fu_csr_device_class_init (FuCsrDeviceClass *klass) FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); klass_device->to_string = fu_csr_device_to_string; klass_device->write_firmware = fu_csr_device_download; - klass_device->read_firmware = fu_csr_device_upload; + klass_device->dump_firmware = fu_csr_device_upload; klass_device->prepare_firmware = fu_csr_device_prepare_firmware; klass_device->attach = fu_csr_device_attach; klass_device->setup = fu_csr_device_setup; diff --git a/plugins/dell-dock/README.md b/plugins/dell-dock/README.md index 67e213972..8866867ca 100644 --- a/plugins/dell-dock/README.md +++ b/plugins/dell-dock/README.md @@ -68,3 +68,7 @@ This plugin uses the following plugin-specific quirks: | `DellDockVersionLowest` | The minimum component version required to safely operate the plugin | 1.1.3 | | `DellDockBoard*` | The board description of a board revision | 1.1.3 | | `DellDockInstallDurationI2C` | The duration of time required to install a payload via I2C. | 1.1.3 | + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/dell-dock/fu-dell-dock-hid.c b/plugins/dell-dock/fu-dell-dock-hid.c index 7b0b2301a..fb5be7b12 100644 --- a/plugins/dell-dock/fu-dell-dock-hid.c +++ b/plugins/dell-dock/fu-dell-dock-hid.c @@ -65,7 +65,7 @@ typedef struct __attribute__ ((packed)) { typedef struct __attribute__ ((packed)) { guint8 cmd; guint8 ext; - guint8 i2cslaveaddr; + guint8 i2ctargetaddr; guint8 i2cspeed; union { guint32 startaddress; @@ -129,7 +129,7 @@ fu_dell_dock_hid_get_hub_version (FuDevice *self, .cmd_data2 = 0, .cmd_data3 = 0, .bufferlen = GUINT16_TO_LE (12), - .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .parameters = {.i2ctargetaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, .extended_cmdarea[0 ... 52] = 0, }; @@ -164,7 +164,7 @@ fu_dell_dock_hid_raise_mcu_clock (FuDevice *self, .cmd_data2 = 0, .cmd_data3 = 0, .bufferlen = 0, - .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .parameters = {.i2ctargetaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, .extended_cmdarea[0 ... 52] = 0, }; @@ -193,7 +193,7 @@ fu_dell_dock_hid_get_ec_status (FuDevice *self, .cmd_data2 = 0, .cmd_data3 = 0, .bufferlen = GUINT16_TO_LE (27), - .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .parameters = {.i2ctargetaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, .extended_cmdarea[0 ... 52] = 0, }; @@ -224,7 +224,7 @@ fu_dell_dock_hid_erase_bank (FuDevice *self, guint8 idx, GError **error) .cmd_data2 = 0, .cmd_data3 = 0, .bufferlen = 0, - .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .parameters = {.i2ctargetaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, .extended_cmdarea[0 ... 52] = 0, }; @@ -249,7 +249,7 @@ fu_dell_dock_hid_write_flash (FuDevice *self, .ext = HUB_EXT_WRITEFLASH, .dwregaddr = GUINT32_TO_LE (dwAddr), .bufferlen = GUINT16_TO_LE (write_size), - .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .parameters = {.i2ctargetaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, .extended_cmdarea[0 ... 52] = 0, }; @@ -280,7 +280,7 @@ fu_dell_dock_hid_verify_update (FuDevice *self, .cmd_data2 = 0, .cmd_data3 = 0, .bufferlen = GUINT16_TO_LE (1), - .parameters = {.i2cslaveaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, + .parameters = {.i2ctargetaddr = 0, .regaddrlen = 0, .i2cspeed = 0}, .extended_cmdarea[0 ... 52] = 0, }; @@ -310,7 +310,7 @@ fu_dell_dock_hid_i2c_write (FuDevice *self, .ext = HUB_EXT_I2C_WRITE, .dwregaddr = 0, .bufferlen = GUINT16_TO_LE (write_size), - .parameters = {.i2cslaveaddr = parameters->i2cslaveaddr, + .parameters = {.i2ctargetaddr = parameters->i2ctargetaddr, .regaddrlen = 0, .i2cspeed = parameters->i2cspeed | 0x80}, .extended_cmdarea[0 ... 52] = 0, @@ -336,7 +336,7 @@ fu_dell_dock_hid_i2c_read (FuDevice *self, .ext = HUB_EXT_I2C_READ, .dwregaddr = GUINT32_TO_LE (cmd), .bufferlen = GUINT16_TO_LE (read_size), - .parameters = {.i2cslaveaddr = parameters->i2cslaveaddr, + .parameters = {.i2ctargetaddr = parameters->i2ctargetaddr, .regaddrlen = parameters->regaddrlen, .i2cspeed = parameters->i2cspeed | 0x80}, .extended_cmdarea[0 ... 52] = 0, @@ -365,7 +365,7 @@ fu_dell_dock_hid_tbt_wake (FuDevice *self, FuTbtCmdBuffer cmd_buffer = { .cmd = HUB_CMD_READ_DATA, /* special write command that reads status result */ .ext = HUB_EXT_WRITE_TBT_FLASH, - .i2cslaveaddr = parameters->i2cslaveaddr, + .i2ctargetaddr = parameters->i2ctargetaddr, .i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */ .tbt_command = TBT_COMMAND_WAKEUP, .bufferlen = 0, @@ -408,7 +408,7 @@ fu_dell_dock_hid_tbt_write (FuDevice *self, FuTbtCmdBuffer cmd_buffer = { .cmd = HUB_CMD_READ_DATA, /* It's a special write command that reads status result */ .ext = HUB_EXT_WRITE_TBT_FLASH, - .i2cslaveaddr = parameters->i2cslaveaddr, + .i2ctargetaddr = parameters->i2ctargetaddr, .i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */ .startaddress = GUINT32_TO_LE (start_addr), .bufferlen = write_size, @@ -454,7 +454,7 @@ fu_dell_dock_hid_tbt_authenticate (FuDevice *self, FuTbtCmdBuffer cmd_buffer = { .cmd = HUB_CMD_READ_DATA, /* It's a special write command that reads status result */ .ext = HUB_EXT_WRITE_TBT_FLASH, - .i2cslaveaddr = parameters->i2cslaveaddr, + .i2ctargetaddr = parameters->i2ctargetaddr, .i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */ .tbt_command = GUINT32_TO_LE (TBT_COMMAND_AUTHENTICATE), .bufferlen = 0, diff --git a/plugins/dell-dock/fu-dell-dock-hid.h b/plugins/dell-dock/fu-dell-dock-hid.h index 613151283..460dfda0c 100644 --- a/plugins/dell-dock/fu-dell-dock-hid.h +++ b/plugins/dell-dock/fu-dell-dock-hid.h @@ -23,7 +23,7 @@ #include "fu-device.h" typedef struct __attribute__ ((packed)) { - guint8 i2cslaveaddr; + guint8 i2ctargetaddr; guint8 regaddrlen; guint8 i2cspeed; } FuHIDI2CParameters; diff --git a/plugins/dell-dock/fu-dell-dock-i2c-ec.c b/plugins/dell-dock/fu-dell-dock-i2c-ec.c index fa7ee2d79..e61476cdf 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-ec.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-ec.c @@ -54,7 +54,7 @@ typedef enum { } FuDellDockECFWUpdateStatus; const FuHIDI2CParameters ec_base_settings = { - .i2cslaveaddr = I2C_EC_ADDRESS, + .i2ctargetaddr = I2C_EC_ADDRESS, .regaddrlen = 1, .i2cspeed = I2C_SPEED_250K, }; diff --git a/plugins/dell-dock/fu-dell-dock-i2c-mst.c b/plugins/dell-dock/fu-dell-dock-i2c-mst.c index ae0fd690c..96d5d89de 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-mst.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-mst.c @@ -96,7 +96,7 @@ const MSTBankAttributes esm_attributes = { }; FuHIDI2CParameters mst_base_settings = { - .i2cslaveaddr = I2C_MST_ADDRESS, + .i2ctargetaddr = I2C_MST_ADDRESS, .regaddrlen = 0, .i2cspeed = I2C_SPEED_400K, }; diff --git a/plugins/dell-dock/fu-dell-dock-i2c-tbt.c b/plugins/dell-dock/fu-dell-dock-i2c-tbt.c index 485b1bca9..884a18b9a 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-tbt.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-tbt.c @@ -28,7 +28,7 @@ #define I2C_TBT_ADDRESS 0xa2 const FuHIDI2CParameters tbt_base_settings = { - .i2cslaveaddr = I2C_TBT_ADDRESS, + .i2ctargetaddr = I2C_TBT_ADDRESS, .regaddrlen = 1, .i2cspeed = I2C_SPEED_400K, }; diff --git a/plugins/dell-dock/fu-plugin-dell-dock.c b/plugins/dell-dock/fu-plugin-dell-dock.c index 2d5a2aac5..5c3396500 100644 --- a/plugins/dell-dock/fu-plugin-dell-dock.c +++ b/plugins/dell-dock/fu-plugin-dell-dock.c @@ -121,6 +121,17 @@ fu_plugin_usb_device_added (FuPlugin *plugin, return TRUE; } +void +fu_plugin_device_registered (FuPlugin *plugin, FuDevice *device) +{ + /* thunderbolt plugin */ + if (g_strcmp0 (fu_device_get_plugin (device), "thunderbolt") != 0 || + fu_device_has_flag (device, FWUPD_DEVICE_FLAG_INTERNAL)) + return; + /* clone updatable flag to leave in needs activation state */ + fu_dell_dock_clone_updatable (device); +} + gboolean fu_plugin_device_removed (FuPlugin *plugin, FuDevice *device, GError **error) { diff --git a/plugins/dell-esrt/README.md b/plugins/dell-esrt/README.md index 5c5aefc4a..b96120ac9 100644 --- a/plugins/dell-esrt/README.md +++ b/plugins/dell-esrt/README.md @@ -45,3 +45,9 @@ UEFI dummy device Version: 0 Created: 2018-06-25 ``` + +External interface access +------------------------- +This plugin requires: +* read/write access to `/dev/wmi/dell-smbios` and `/sys/bus/platform/devices/dcdbas`. +* read access to `/sys/firmware/efi/esrt`. diff --git a/plugins/dell-esrt/fu-plugin-dell-esrt.c b/plugins/dell-esrt/fu-plugin-dell-esrt.c index 5f0b0b1d7..a6c92fcce 100644 --- a/plugins/dell-esrt/fu-plugin-dell-esrt.c +++ b/plugins/dell-esrt/fu-plugin-dell-esrt.c @@ -17,11 +17,11 @@ #include "fu-plugin-vfuncs.h" #include "fu-hash.h" -/* Whitelisted smbios class/select commands */ +/* allowed smbios class/select commands */ #define CLASS_ADMIN_PROP 10 #define SELECT_ADMIN_PROP 3 -/* whitelisted tokens */ +/* allowed tokens */ #define CAPSULE_EN_TOKEN 0x0461 #define CAPSULE_DIS_TOKEN 0x0462 @@ -87,7 +87,7 @@ void fu_plugin_init (FuPlugin *plugin) { fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); - fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_BETTER_THAN, "uefi"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_BETTER_THAN, "bios"); } gboolean @@ -172,6 +172,8 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_LOCKED); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); fu_device_set_update_error (dev, "Firmware updates disabled; run 'fwupdmgr unlock' to enable"); + if (!fu_device_setup (dev, error)) + return FALSE; fu_plugin_device_add (plugin, dev); return TRUE; } diff --git a/plugins/dell/README.md b/plugins/dell/README.md index aa25bcfb9..c6e7a1058 100644 --- a/plugins/dell/README.md +++ b/plugins/dell/README.md @@ -181,3 +181,7 @@ These updates can be performed the standard method of using: Some components are updatable via other plugins in fwupd such as multi stream transport hub (MST) and thunderbolt NVM. + +External interface access +------------------------- +This plugin requires read/write access to `/dev/wmi/dell-smbios` and `/sys/bus/platform/devices/dcdbas`. diff --git a/plugins/dell/fu-dell-smi.c b/plugins/dell/fu-dell-smi.c index a7802b236..84382e820 100644 --- a/plugins/dell/fu-dell-smi.c +++ b/plugins/dell/fu-dell-smi.c @@ -23,13 +23,6 @@ struct dock_count_out { guint32 reserved; }; -/* This is used for host flash GUIDs */ -typedef union _ADDR_UNION{ - uint8_t *buf; - efi_guid_t *guid; -} ADDR_UNION; -#pragma pack() - static void _dell_smi_obj_free (FuDellSmiObj *obj) { @@ -230,36 +223,3 @@ fu_dell_toggle_dock_mode (FuDellSmiObj *smi_obj, guint32 new_mode, } return TRUE; } - -gboolean -fu_dell_toggle_host_mode (FuDellSmiObj *smi_obj, const efi_guid_t guid, int mode) -{ - gint ret; - ADDR_UNION buf; - - dell_smi_obj_set_class (smi_obj->smi, DACI_FLASH_INTERFACE_CLASS); - dell_smi_obj_set_select (smi_obj->smi, DACI_FLASH_INTERFACE_SELECT); - dell_smi_obj_set_arg (smi_obj->smi, cbARG1, DACI_FLASH_ARG_FLASH_MODE); - dell_smi_obj_set_arg (smi_obj->smi, cbARG4, mode); - /* needs to be padded with an empty GUID */ - buf.buf = dell_smi_obj_make_buffer_frombios_withoutheader(smi_obj->smi, - cbARG2, - sizeof(efi_guid_t) * 2); - if (!buf.buf) { - g_debug ("Failed to initialize SMI buffer"); - return FALSE; - } - *buf.guid = guid; - ret = dell_smi_obj_execute(smi_obj->smi); - if (ret != SMI_SUCCESS){ - g_debug ("failed to execute SMI: %d", ret); - return FALSE; - } - - ret = dell_smi_obj_get_res(smi_obj->smi, cbRES1); - if (ret != SMI_SUCCESS) { - g_debug ("SMI execution returned error: %d", ret); - return FALSE; - } - return TRUE; -} diff --git a/plugins/dell/fu-dell-smi.h b/plugins/dell/fu-dell-smi.h index a5d0cf892..0530dd639 100644 --- a/plugins/dell/fu-dell-smi.h +++ b/plugins/dell/fu-dell-smi.h @@ -9,7 +9,6 @@ #include "fu-device.h" #include #include -#include typedef struct { struct dell_smi_obj *smi; @@ -95,9 +94,6 @@ gboolean fu_dell_toggle_dock_mode (FuDellSmiObj *smi_obj, guint32 new_mode, guint32 dock_location, GError **error); -gboolean -fu_dell_toggle_host_mode (FuDellSmiObj *smi_obj, const efi_guid_t guid, int mode); - /* SMI return values used */ #define SMI_SUCCESS 0 #define SMI_INVALID_BUFFER -6 diff --git a/plugins/dell/fu-plugin-dell.c b/plugins/dell/fu-plugin-dell.c index 7966fcc41..efe6b1b19 100644 --- a/plugins/dell/fu-plugin-dell.c +++ b/plugins/dell/fu-plugin-dell.c @@ -93,7 +93,7 @@ struct da_structure { /** * Devices that should allow modeswitching */ -static guint16 tpm_switch_whitelist[] = {0x06F2, 0x06F3, 0x06DD, 0x06DE, 0x06DF, +static guint16 tpm_switch_allowlist[] = {0x06F2, 0x06F3, 0x06DD, 0x06DE, 0x06DF, 0x06DB, 0x06DC, 0x06BB, 0x06C6, 0x06BA, 0x06B9, 0x05CA, 0x06C7, 0x06B7, 0x06E0, 0x06E5, 0x06D9, 0x06DA, 0x06E4, 0x0704, @@ -106,7 +106,7 @@ static guint16 tpm_switch_whitelist[] = {0x06F2, 0x06F3, 0x06DD, 0x06DE, 0x06DF, /** * Dell device types to run */ -static guint8 enclosure_whitelist [] = { 0x03, /* desktop */ +static guint8 enclosure_allowlist [] = { 0x03, /* desktop */ 0x04, /* low profile desktop */ 0x06, /* mini tower */ 0x07, /* tower */ @@ -180,8 +180,8 @@ fu_dell_supported (FuPlugin *plugin) value = g_bytes_get_data (enclosure, &len); if (len == 0) return FALSE; - for (guint i = 0; i < G_N_ELEMENTS (enclosure_whitelist); i++) { - if (enclosure_whitelist[i] == value[0]) + for (guint i = 0; i < G_N_ELEMENTS (enclosure_allowlist); i++) { + if (enclosure_allowlist[i] == value[0]) return TRUE; } @@ -286,9 +286,6 @@ fu_plugin_dock_node (FuPlugin *plugin, const gchar *platform, if (fu_plugin_dell_capsule_supported (plugin)) { fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); - } else { - fu_device_set_update_error (dev, - "UEFI capsule updates turned off in BIOS setup"); } } @@ -608,6 +605,7 @@ fu_plugin_dell_add_tpm_model (FuDevice *dev, GError **error) "failed to read TPM vendor string"); return FALSE; } + fu_device_set_metadata (dev, "TpmFamily", family); /* these are not guaranteed by spec and may be NULL */ vendor2 = fu_plugin_dell_get_tpm_capability (ctx, TPM2_PT_VENDOR_STRING_2); @@ -693,8 +691,8 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) else if (system_id == 0) return FALSE; - for (guint i = 0; i < G_N_ELEMENTS (tpm_switch_whitelist); i++) { - if (tpm_switch_whitelist[i] == system_id) { + for (guint i = 0; i < G_N_ELEMENTS (tpm_switch_allowlist); i++) { + if (tpm_switch_allowlist[i] == system_id) { can_switch_modes = TRUE; } } @@ -734,9 +732,6 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) if (fu_plugin_dell_capsule_supported (plugin)) { fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); - } else { - fu_device_set_update_error (dev, - "UEFI capsule updates turned off in BIOS setup"); } fu_device_set_flashes_left (dev, out->flashes_left); } else { @@ -750,6 +745,8 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) if (!fu_device_setup (dev, error)) return FALSE; fu_plugin_device_register (plugin, dev); + fu_plugin_add_report_metadata (plugin, "TpmFamily", + fu_device_get_metadata (dev, "TpmFamily")); /* build alternate device node */ if (can_switch_modes) { @@ -892,10 +889,14 @@ fu_plugin_startup (FuPlugin *plugin, GError **error) */ sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); esrtdir = g_build_filename (sysfsfwdir, "efi", "esrt", NULL); - if (g_file_test (esrtdir, G_FILE_TEST_EXISTS)) { + if (g_file_test (esrtdir, G_FILE_TEST_EXISTS)) data->capsule_supported = TRUE; - } else { - g_debug ("UEFI capsule firmware updating not supported"); + + /* capsules not supported */ + if (!fu_plugin_dell_capsule_supported (plugin)) { + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE); + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED); } return TRUE; diff --git a/plugins/dell/fu-self-test.c b/plugins/dell/fu-self-test.c index ba449b303..007092086 100644 --- a/plugins/dell/fu-self-test.c +++ b/plugins/dell/fu-self-test.c @@ -16,6 +16,11 @@ #include "fu-plugin-vfuncs.h" #include "fu-hash.h" +typedef struct { + FuPlugin *plugin_uefi; + FuPlugin *plugin_dell; +} FuTest; + static FuDevice * _find_device_by_id (GPtrArray *devices, const gchar *device_id) { @@ -62,57 +67,21 @@ fu_engine_plugin_device_register_cb (FuPlugin *plugin_dell, } static void -fu_plugin_dell_tpm_func (void) +fu_plugin_dell_tpm_func (gconstpointer user_data) { + FuTest *self = (FuTest *) user_data; FuDevice *device_v12; FuDevice *device_v20; const guint8 fw[30] = { 'F', 'W', 0x00 }; gboolean ret; + gulong added_id; + gulong register_id; struct tpm_status tpm_out; const gchar *tpm_server_running = g_getenv ("TPM_SERVER_RUNNING"); - g_autofree gchar *pluginfn_uefi = NULL; - g_autofree gchar *pluginfn_dell = NULL; - g_autoptr(FuPlugin) plugin_dell = NULL; - g_autoptr(FuPlugin) plugin_uefi = NULL; g_autoptr(GBytes) blob_fw = g_bytes_new_static (fw, sizeof(fw)); g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; - pluginfn_uefi = g_build_filename (PLUGINBUILDDIR, "..", "uefi", - "libfu_plugin_uefi." G_MODULE_SUFFIX, - NULL); - pluginfn_dell = g_build_filename (PLUGINBUILDDIR, - "libfu_plugin_dell." G_MODULE_SUFFIX, - NULL); - - memset (&tpm_out, 0x0, sizeof(tpm_out)); - - plugin_uefi = fu_plugin_new (); - ret = fu_plugin_open (plugin_uefi, pluginfn_uefi, &error); - g_assert_no_error (error); - g_assert (ret); - ret = fu_plugin_runner_startup (plugin_uefi, &error); - g_assert_no_error (error); - g_assert (ret); - devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - g_signal_connect (plugin_uefi, "device-added", - G_CALLBACK (_plugin_device_added_cb), - devices); - - plugin_dell = fu_plugin_new (); - ret = fu_plugin_open (plugin_dell, pluginfn_dell, &error); - g_assert_no_error (error); - g_assert (ret); - ret = fu_plugin_runner_startup (plugin_dell, &error); - g_assert_no_error (error); - g_assert (ret); - g_signal_connect (plugin_dell, "device-register", - G_CALLBACK (fu_engine_plugin_device_register_cb), - plugin_uefi); - ret = fu_plugin_runner_coldplug (plugin_dell, &error); - g_assert_no_error (error); - g_assert (ret); - #ifdef HAVE_GETUID if (tpm_server_running == NULL && (getuid () != 0 || geteuid () != 0)) { @@ -121,12 +90,28 @@ fu_plugin_dell_tpm_func (void) } #endif + memset (&tpm_out, 0x0, sizeof(tpm_out)); + + devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + added_id = + g_signal_connect (self->plugin_uefi, "device-added", + G_CALLBACK (_plugin_device_added_cb), + devices); + + register_id = + g_signal_connect (self->plugin_dell, "device-register", + G_CALLBACK (fu_engine_plugin_device_register_cb), + self->plugin_uefi); + ret = fu_plugin_runner_coldplug (self->plugin_dell, &error); + g_assert_no_error (error); + g_assert (ret); + /* inject fake data (no TPM) */ tpm_out.ret = -2; - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &tpm_out, 0, 0, NULL, FALSE); - ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); + ret = fu_plugin_dell_detect_tpm (self->plugin_dell, &error); g_assert_no_error (error); g_assert_false (ret); g_assert_cmpint (devices->len, ==, 0); @@ -141,10 +126,10 @@ fu_plugin_dell_tpm_func (void) tpm_out.fw_version = 0; tpm_out.status = TPM_EN_MASK | (TPM_1_2_MODE << 8); tpm_out.flashes_left = 0; - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &tpm_out, 0, 0, NULL, TRUE); - ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); + ret = fu_plugin_dell_detect_tpm (self->plugin_dell, &error); g_assert_true (ret); g_assert_cmpint (devices->len, ==, 2); @@ -159,7 +144,7 @@ fu_plugin_dell_tpm_func (void) g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); /* try to unlock 2.0 */ - ret = fu_plugin_runner_unlock (plugin_uefi, device_v20, &error); + ret = fu_plugin_runner_unlock (self->plugin_uefi, device_v20, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_false (ret); g_clear_error (&error); @@ -175,10 +160,10 @@ fu_plugin_dell_tpm_func (void) */ tpm_out.status = TPM_EN_MASK | TPM_OWN_MASK | (TPM_1_2_MODE << 8); tpm_out.flashes_left = 125; - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &tpm_out, 0, 0, NULL, TRUE); - ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); + ret = fu_plugin_dell_detect_tpm (self->plugin_dell, &error); g_assert_no_error (error); g_assert (ret); @@ -190,7 +175,7 @@ fu_plugin_dell_tpm_func (void) /* try to unlock 2.0 */ device_v20 = _find_device_by_name (devices, "TPM 2.0"); g_assert_nonnull (device_v20); - ret = fu_plugin_runner_unlock (plugin_uefi, device_v20, &error); + ret = fu_plugin_runner_unlock (self->plugin_uefi, device_v20, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_false (ret); g_clear_error (&error); @@ -206,10 +191,10 @@ fu_plugin_dell_tpm_func (void) */ tpm_out.status = TPM_EN_MASK | (TPM_1_2_MODE << 8); tpm_out.flashes_left = 125; - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &tpm_out, 0, 0, NULL, TRUE); - ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); + ret = fu_plugin_dell_detect_tpm (self->plugin_dell, &error); g_assert_no_error (error); g_assert (ret); @@ -222,7 +207,7 @@ fu_plugin_dell_tpm_func (void) g_assert_false (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_UPDATABLE)); /* try to unlock 2.0 */ - ret = fu_plugin_runner_unlock (plugin_uefi, device_v20, &error); + ret = fu_plugin_runner_unlock (self->plugin_uefi, device_v20, &error); g_assert_no_error (error); g_assert (ret); @@ -241,10 +226,10 @@ fu_plugin_dell_tpm_func (void) */ tpm_out.status = TPM_EN_MASK | (TPM_2_0_MODE << 8); tpm_out.flashes_left = 1; - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &tpm_out, 0, 0, NULL, TRUE); - ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); + ret = fu_plugin_dell_detect_tpm (self->plugin_dell, &error); g_assert_no_error (error); g_assert (ret); @@ -257,68 +242,51 @@ fu_plugin_dell_tpm_func (void) g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); /* With one flash left we need an override */ - ret = fu_plugin_runner_update (plugin_uefi, device_v20, blob_fw, + ret = fu_plugin_runner_update (self->plugin_uefi, device_v20, blob_fw, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_false (ret); g_clear_error (&error); /* test override */ - ret = fu_plugin_runner_update (plugin_uefi, device_v20, blob_fw, + ret = fu_plugin_runner_update (self->plugin_uefi, device_v20, blob_fw, FWUPD_INSTALL_FLAG_FORCE, &error); g_assert_no_error (error); g_assert (ret); + + /* all */ + g_signal_handler_disconnect (self->plugin_uefi, added_id); + g_signal_handler_disconnect (self->plugin_dell, register_id); } static void -fu_plugin_dell_dock_func (void) +fu_plugin_dell_dock_func (gconstpointer user_data) { + FuTest *self = (FuTest *) user_data; gboolean ret; guint32 out[4] = { 0x0, 0x0, 0x0, 0x0 }; DOCK_UNION buf; DOCK_INFO *dock_info; - g_autofree gchar *pluginfn_uefi = NULL; - g_autofree gchar *pluginfn_dell = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; - g_autoptr(FuPlugin) plugin_uefi = fu_plugin_new (); - g_autoptr(FuPlugin) plugin_dell = fu_plugin_new (); + gulong added_id; + gulong register_id; - pluginfn_uefi = g_build_filename (PLUGINBUILDDIR, "..", "uefi", - "libfu_plugin_uefi." G_MODULE_SUFFIX, - NULL); - pluginfn_dell = g_build_filename (PLUGINBUILDDIR, - "libfu_plugin_dell." G_MODULE_SUFFIX, - NULL); - - ret = fu_plugin_open (plugin_uefi, pluginfn_uefi, &error); - g_assert_no_error (error); - g_assert (ret); - ret = fu_plugin_runner_startup (plugin_uefi, &error); - g_assert_no_error (error); - g_assert (ret); devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - g_signal_connect (plugin_uefi, "device-added", + added_id = + g_signal_connect (self->plugin_uefi, "device-added", G_CALLBACK (_plugin_device_added_cb), devices); - ret = fu_plugin_open (plugin_dell, pluginfn_dell, &error); - g_assert_no_error (error); - g_assert (ret); - ret = fu_plugin_runner_startup (plugin_dell, &error); - g_assert_no_error (error); - g_assert (ret); - g_signal_connect (plugin_dell, "device-register", + register_id = + g_signal_connect (self->plugin_dell, "device-register", G_CALLBACK (fu_engine_plugin_device_register_cb), - plugin_uefi); - ret = fu_plugin_runner_coldplug (plugin_dell, &error); - g_assert_no_error (error); - g_assert (ret); + self->plugin_uefi); /* make sure bad device doesn't trigger this */ - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &out, 0x1234, 0x4321, NULL, FALSE); - ret = fu_plugin_usb_device_added (plugin_dell, NULL, &error); + ret = fu_plugin_usb_device_added (self->plugin_dell, NULL, &error); g_assert_false (ret); g_clear_error (&error); g_assert_cmpint (devices->len, ==, 0); @@ -326,11 +294,11 @@ fu_plugin_dell_dock_func (void) /* inject a USB dongle matching correct VID/PID */ out[0] = 0; out[1] = 0; - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, NULL, FALSE); - ret = fu_plugin_usb_device_added (plugin_dell, NULL, &error); + ret = fu_plugin_usb_device_added (self->plugin_dell, NULL, &error); g_assert_true (ret); g_clear_error (&error); g_assert_cmpint (devices->len, ==, 0); @@ -360,11 +328,11 @@ fu_plugin_dell_dock_func (void) "Dock1,Cable,Cyp,TBT_Cable,0 :Query 2 2 2 3 0", 44); out[0] = 0; out[1] = 1; - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, buf.buf, FALSE); - ret = fu_plugin_usb_device_added (plugin_dell, NULL, NULL); + ret = fu_plugin_usb_device_added (self->plugin_dell, NULL, NULL); g_assert (ret); g_assert_cmpint (devices->len, ==, 4); g_ptr_array_set_size (devices, 0); @@ -395,11 +363,11 @@ fu_plugin_dell_dock_func (void) "Dock1,Cable,Cyp,TBT_Cable,0 :Query 2 2 2 3 0", 44); out[0] = 0; out[1] = 1; - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, buf.buf, FALSE); - ret = fu_plugin_usb_device_added (plugin_dell, NULL, NULL); + ret = fu_plugin_usb_device_added (self->plugin_dell, NULL, NULL); g_assert (ret); g_assert_cmpint (devices->len, ==, 3); g_ptr_array_set_size (devices, 0); @@ -427,11 +395,11 @@ fu_plugin_dell_dock_func (void) "Dock1,Cable,Cyp,IE_Cable,0 :Query 2 2 2 1 0", 43); out[0] = 0; out[1] = 1; - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, buf.buf, FALSE); - ret = fu_plugin_usb_device_added (plugin_dell, NULL, &error); + ret = fu_plugin_usb_device_added (self->plugin_dell, NULL, &error); g_assert (ret); g_assert_no_error (error); g_assert_cmpint (devices->len, ==, 3); @@ -460,11 +428,11 @@ fu_plugin_dell_dock_func (void) "Dock1,Cable,Cyp,IE_Cable,0 :Query 2 2 2 1 0", 43); out[0] = 0; out[1] = 1; - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, buf.buf, FALSE); - ret = fu_plugin_usb_device_added (plugin_dell, NULL, &error); + ret = fu_plugin_usb_device_added (self->plugin_dell, NULL, &error); g_assert (ret); g_assert_no_error (error); g_assert_cmpint (devices->len, ==, 2); @@ -487,26 +455,78 @@ fu_plugin_dell_dock_func (void) "Dock1,EC,MIPS32,FUT_Dock,0 :Query 2 0 2 2 0", 43); out[0] = 0; out[1] = 1; - fu_plugin_dell_inject_fake_data (plugin_dell, + fu_plugin_dell_inject_fake_data (self->plugin_dell, (guint32 *) &out, DOCK_NIC_VID, DOCK_NIC_PID, buf.buf, FALSE); - ret = fu_plugin_usb_device_added (plugin_dell, NULL, &error); + ret = fu_plugin_usb_device_added (self->plugin_dell, NULL, &error); g_assert_false (ret); g_assert_cmpint (devices->len, ==, 0); g_free (buf.record); + + /* all */ + g_signal_handler_disconnect (self->plugin_uefi, added_id); + g_signal_handler_disconnect (self->plugin_dell, register_id); } +static void +fu_test_self_init (FuTest *self) +{ + gboolean ret; + g_autoptr(GError) error = NULL; + g_autofree gchar *pluginfn_uefi = NULL; + g_autofree gchar *pluginfn_dell = NULL; + + self->plugin_uefi = fu_plugin_new (); + pluginfn_uefi = g_build_filename (PLUGINBUILDDIR, "..", "uefi", + "libfu_plugin_uefi." G_MODULE_SUFFIX, + NULL); + ret = fu_plugin_open (self->plugin_uefi, pluginfn_uefi, &error); + g_assert_no_error (error); + g_assert (ret); + ret = fu_plugin_runner_startup (self->plugin_uefi, &error); + g_assert_no_error (error); + g_assert (ret); + + self->plugin_dell = fu_plugin_new (); + pluginfn_dell = g_build_filename (PLUGINBUILDDIR, + "libfu_plugin_dell." G_MODULE_SUFFIX, + NULL); + ret = fu_plugin_open (self->plugin_dell, pluginfn_dell, &error); + g_assert_no_error (error); + g_assert (ret); + ret = fu_plugin_runner_startup (self->plugin_dell, &error); + g_assert_no_error (error); + g_assert (ret); +} + +static void +fu_test_self_free (FuTest *self) +{ + if (self->plugin_uefi != NULL) + g_object_unref (self->plugin_uefi); + if (self->plugin_dell != NULL) + g_object_unref (self->plugin_dell); + g_free (self); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuTest, fu_test_self_free) +#pragma clang diagnostic pop + int main (int argc, char **argv) { g_autofree gchar *sysfsdir = NULL; + g_autoptr(FuTest) self = g_new0 (FuTest, 1); + g_test_init (&argc, &argv, NULL); /* change path */ g_setenv ("FWUPD_SYSFSFWDIR", TESTDATADIR, TRUE); - /* change behaviour */ + /* change behavior */ sysfsdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); g_setenv ("FWUPD_UEFI_ESP_PATH", sysfsdir, TRUE); g_setenv ("FWUPD_UEFI_TEST", "1", TRUE); @@ -518,7 +538,8 @@ main (int argc, char **argv) g_assert_cmpint (g_mkdir_with_parents ("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0); /* tests go here */ - g_test_add_func ("/fwupd/plugin{dell:tpm}", fu_plugin_dell_tpm_func); - g_test_add_func ("/fwupd/plugin{dell:dock}", fu_plugin_dell_dock_func); + fu_test_self_init (self); + g_test_add_data_func ("/fwupd/plugin{dell:tpm}", self, fu_plugin_dell_tpm_func); + g_test_add_data_func ("/fwupd/plugin{dell:dock}", self, fu_plugin_dell_dock_func); return g_test_run (); } diff --git a/plugins/dell/meson.build b/plugins/dell/meson.build index bf4f3eab0..f32f7b873 100644 --- a/plugins/dell/meson.build +++ b/plugins/dell/meson.build @@ -26,7 +26,6 @@ shared_module('fu_plugin_dell', ], dependencies : [ plugin_deps, - efivar, libsmbios_c, tpm2tss, ], @@ -51,7 +50,6 @@ if get_option('tests') ], dependencies : [ plugin_deps, - efivar, sqlite, libsmbios_c, valgrind, diff --git a/plugins/dfu/README.md b/plugins/dfu/README.md index d3fce1b10..1170526ad 100644 --- a/plugins/dfu/README.md +++ b/plugins/dfu/README.md @@ -41,3 +41,7 @@ This plugin uses the following plugin-specific quirks: |`DfuFlags` | Optional quirks for a DFU device which doesn't follow the DFU 1.0 or 1.1 specification | 1.0.1| |`DfuForceVersion` | Forces a specific DFU version for the hardware device. This is required if the device does not set, or sets incorrectly, items in the DFU functional descriptor. |1.0.1| |`DfuForceTimeout` | Forces a specific device timeout, in ms | 1.4.0 | + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/dfu/dfu-device.c b/plugins/dfu/dfu-device.c index 34d332d84..c3b977a6a 100644 --- a/plugins/dfu/dfu-device.c +++ b/plugins/dfu/dfu-device.c @@ -1270,7 +1270,7 @@ dfu_device_probe (FuUsbDevice *device, GError **error) /* hardware rom Jabra literally reboots if you try to retry a failed * write -- there's no way to avoid blocking the daemon like this... */ if (fu_device_has_custom_flag (FU_DEVICE (device), "attach-extra-reset")) - g_usleep (10 * G_USEC_PER_SEC); + fu_device_sleep_with_progress (FU_DEVICE (self), 10); /* seconds */ /* success */ return TRUE; @@ -1679,12 +1679,20 @@ dfu_device_error_fixup (DfuDevice *device, GError **error) } } -static FuFirmware * -dfu_device_read_firmware (FuDevice *device, GError **error) +static GBytes * +dfu_device_dump_firmware (FuDevice *device, GError **error) { DfuDevice *self = DFU_DEVICE (device); g_autoptr(DfuFirmware) dfu_firmware = NULL; - g_autoptr(GBytes) fw = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* require detach -> attach */ + locker = fu_device_locker_new_full (device, + (FuDeviceLockerFunc) fu_device_detach, + (FuDeviceLockerFunc) fu_device_attach, + error); + if (locker == NULL) + return NULL; /* get data from hardware */ g_debug ("uploading from device->host"); @@ -1697,8 +1705,7 @@ dfu_device_read_firmware (FuDevice *device, GError **error) return NULL; /* get the checksum */ - fw = dfu_firmware_write_data (dfu_firmware, error); - return fu_firmware_new_from_bytes (fw); + return dfu_firmware_write_data (dfu_firmware, error); } static gboolean @@ -1719,7 +1726,7 @@ dfu_device_write_firmware (FuDevice *device, if (!dfu_device_refresh_and_clear (self, error)) return FALSE; - if (flags & FWUPD_INSTALL_FLAG_FORCE) { + if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) { transfer_flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID; transfer_flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID; } @@ -1825,7 +1832,7 @@ dfu_device_class_init (DfuDeviceClass *klass) FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); klass_device->set_quirk_kv = dfu_device_set_quirk_kv; klass_device->to_string = dfu_device_to_string; - klass_device->read_firmware = dfu_device_read_firmware; + klass_device->dump_firmware = dfu_device_dump_firmware; klass_device->write_firmware = dfu_device_write_firmware; klass_device->attach = dfu_device_attach; klass_device->detach = dfu_device_detach; diff --git a/plugins/dfu/dfu-self-test.c b/plugins/dfu/dfu-self-test.c index bb091526b..781aa38e5 100644 --- a/plugins/dfu/dfu-self-test.c +++ b/plugins/dfu/dfu-self-test.c @@ -20,14 +20,6 @@ #include "fwupd-error.h" -static gchar * -dfu_test_get_filename (const gchar *filename) -{ - g_autofree gchar *path = NULL; - path = g_build_filename (TESTDATADIR, filename, NULL); - return fu_common_realpath (path, NULL); -} - static void dfu_enums_func (void) { @@ -118,6 +110,7 @@ dfu_firmware_dfu_func (void) { gchar buf[256]; gboolean ret; + const gchar *ci = g_getenv ("CI_NETWORK"); g_autofree gchar *filename = NULL; g_autoptr(DfuFirmware) firmware1 = dfu_firmware_new (); g_autoptr(DfuFirmware) firmware2 = dfu_firmware_new (); @@ -162,7 +155,11 @@ dfu_firmware_dfu_func (void) g_assert_cmpint (dfu_firmware_get_size (firmware2), ==, 256); /* load a real firmware */ - filename = dfu_test_get_filename ("kiibohd.dfu.bin"); + filename = g_test_build_filename (G_TEST_DIST, "tests", "kiibohd.dfu.bin", NULL); + if (!g_file_test (filename, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing kiibohd.dfu.bin"); + return; + } g_assert (filename != NULL); file = g_file_new_for_path (filename); ret = dfu_firmware_parse_file (firmware3, file, @@ -192,6 +189,7 @@ static void dfu_firmware_dfuse_func (void) { gboolean ret; + const gchar *ci = g_getenv ("CI_NETWORK"); g_autofree gchar *filename = NULL; g_autoptr(DfuFirmware) firmware = NULL; g_autoptr(GBytes) roundtrip_orig = NULL; @@ -201,7 +199,11 @@ dfu_firmware_dfuse_func (void) /* load a DeFUse firmware */ g_setenv ("DFU_SELF_TEST_IMAGE_MEMCPY_NAME", "", FALSE); - filename = dfu_test_get_filename ("dev_VRBRAIN.dfu"); + filename = g_test_build_filename (G_TEST_DIST, "tests", "dev_VRBRAIN.dfu", NULL); + if (!g_file_test (filename, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing dev_VRBRAIN.dfu"); + return; + } g_assert (filename != NULL); file = g_file_new_for_path (filename); firmware = dfu_firmware_new (); diff --git a/plugins/dfu/dfu-tool.c b/plugins/dfu/dfu-tool.c index f5f221a93..655095cd0 100644 --- a/plugins/dfu/dfu-tool.c +++ b/plugins/dfu/dfu-tool.c @@ -924,8 +924,10 @@ dfu_tool_write (DfuToolPrivate *priv, gchar **values, GError **error) } /* allow wildcards */ - if (priv->force) - flags |= FWUPD_INSTALL_FLAG_FORCE; + if (priv->force) { + flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID; + flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM; + } /* transfer */ g_signal_connect (device, "notify::status", diff --git a/plugins/dfu/meson.build b/plugins/dfu/meson.build index 9705f0d63..4fca2246f 100644 --- a/plugins/dfu/meson.build +++ b/plugins/dfu/meson.build @@ -24,6 +24,7 @@ dfu = static_library( dependencies : [ giounix, libm, + libxmlb, gusb, gudev, ], @@ -111,8 +112,9 @@ if get_option('man') endif if get_option('tests') - testdatadir = join_paths(meson.current_source_dir(), 'tests') - cargs += '-DTESTDATADIR="' + testdatadir + '"' + testdatadirs = environment() + testdatadirs.set('G_TEST_SRCDIR', meson.current_source_dir()) + testdatadirs.set('G_TEST_BUILDDIR', meson.current_build_dir()) e = executable( 'dfu-self-test', fu_hash, @@ -136,9 +138,10 @@ if get_option('tests') fwupd, fwupdplugin, ], - c_args : cargs + install : true, + install_dir : installed_test_bindir, ) - test('dfu-self-test', e) + test('dfu-self-test', e, env : testdatadirs) # added to installed-tests endif plugindfu_incdir = include_directories('.') diff --git a/plugins/dfu/tests/dev_VRBRAIN.dfu b/plugins/dfu/tests/dev_VRBRAIN.dfu deleted file mode 100644 index 1c1a5d61d..000000000 Binary files a/plugins/dfu/tests/dev_VRBRAIN.dfu and /dev/null differ diff --git a/plugins/dfu/tests/example.bin b/plugins/dfu/tests/example.bin deleted file mode 100644 index 3b18e512d..000000000 --- a/plugins/dfu/tests/example.bin +++ /dev/null @@ -1 +0,0 @@ -hello world diff --git a/plugins/dfu/tests/example.dfu b/plugins/dfu/tests/example.dfu deleted file mode 100644 index 92ab2eb47..000000000 Binary files a/plugins/dfu/tests/example.dfu and /dev/null differ diff --git a/plugins/dfu/tests/example.xdfu b/plugins/dfu/tests/example.xdfu deleted file mode 100644 index b58fd963c..000000000 Binary files a/plugins/dfu/tests/example.xdfu and /dev/null differ diff --git a/plugins/dfu/tests/kiibohd.dfu.bin b/plugins/dfu/tests/kiibohd.dfu.bin deleted file mode 100644 index d11909cc9..000000000 Binary files a/plugins/dfu/tests/kiibohd.dfu.bin and /dev/null differ diff --git a/plugins/dfu/tests/metadata.dfu b/plugins/dfu/tests/metadata.dfu deleted file mode 100644 index 11421a4f8..000000000 Binary files a/plugins/dfu/tests/metadata.dfu and /dev/null differ diff --git a/plugins/ebitdo/README.md b/plugins/ebitdo/README.md index c26bce02e..d2a63e49e 100644 --- a/plugins/ebitdo/README.md +++ b/plugins/ebitdo/README.md @@ -44,3 +44,7 @@ values depending on the model and device mode. The list of USB VIDs used is: * `USB:0x1235` * `USB:0x2002` * `USB:0x8000` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/ebitdo/fu-ebitdo-device.c b/plugins/ebitdo/fu-ebitdo-device.c index 280afd952..97c916aa5 100644 --- a/plugins/ebitdo/fu-ebitdo-device.c +++ b/plugins/ebitdo/fu-ebitdo-device.c @@ -236,7 +236,7 @@ static gboolean fu_ebitdo_device_validate (FuEbitdoDevice *self, GError **error) { const gchar *ven; - const gchar *whitelist[] = { + const gchar *allowlist[] = { "8Bitdo", "SFC30", NULL }; @@ -245,7 +245,7 @@ fu_ebitdo_device_validate (FuEbitdoDevice *self, GError **error) if (fu_usb_device_get_vid (FU_USB_DEVICE (self)) == 0x2dc8) return TRUE; - /* verify the vendor prefix against a whitelist */ + /* verify the vendor prefix against a allowlist */ ven = fu_device_get_vendor (FU_DEVICE (self)); if (ven == NULL) { g_set_error (error, @@ -254,14 +254,14 @@ fu_ebitdo_device_validate (FuEbitdoDevice *self, GError **error) "could not check vendor descriptor: "); return FALSE; } - for (guint i = 0; whitelist[i] != NULL; i++) { - if (g_str_has_prefix (ven, whitelist[i])) + for (guint i = 0; allowlist[i] != NULL; i++) { + if (g_str_has_prefix (ven, allowlist[i])) return TRUE; } g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, - "vendor '%s' did not match whitelist, " + "vendor '%s' did not match allowlist, " "probably not a 8Bitdo device…", ven); return FALSE; } @@ -581,7 +581,6 @@ fu_ebitdo_device_prepare_firmware (FuDevice *device, GError **error) { g_autoptr(FuFirmware) firmware = fu_ebitdo_firmware_new (); - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; return g_steal_pointer (&firmware); diff --git a/plugins/elantp/README.md b/plugins/elantp/README.md new file mode 100644 index 000000000..15bafcf40 --- /dev/null +++ b/plugins/elantp/README.md @@ -0,0 +1,56 @@ +Elan TouchPad +============= + +Introduction +------------ + +This plugin allows updating Touchpad devices from Elan. Devices are enumerated +using HID and raw I²C nodes. The I²C mode is used for firmware recovery. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +an unspecified binary file format. + +This plugin supports the following protocol ID: + + * tw.com.emc.elantp + +GUID Generation +--------------- + +These device uses the standard DeviceInstanceId values, e.g. + + * `HIDRAW\VEN_04F3&DEV_3010` + +Additionally another instance ID is added which corresponds to the module ID: + + * `HIDRAW\VEN_04F3&DEV_3010&MOD_1234` + +These devices also use custom GUID values for the IC configuration, e.g. + + * `ELANTP\ICTYPE_09` + + Additionally another instance ID is added which corresponds to the IC type & module ID: + + * `ELANTP\ICTYPE_09&MOD_1234` + +Vendor ID Security +------------------ + +The vendor ID is set from the HID vendor, for example set to `HIDRAW:0x17EF` + +Quirk use +--------- + +This plugin uses the following plugin-specific quirks: + +| Quirk | Description | Minimum fwupd version | +|------------------------|-------------------------------------------|-----------------------| +| `ElantpIcPageCount` | The IC page count | 1.4.6 | +| `ElantpIapPassword` | The IAP password | 1.4.6 | + +External interface access +------------------------- +This plugin requires ioctl access to `HIDIOCSFEATURE` and `HIDIOCGFEATURE`. diff --git a/plugins/elantp/elantp.quirk b/plugins/elantp/elantp.quirk new file mode 100644 index 000000000..40ce25c96 --- /dev/null +++ b/plugins/elantp/elantp.quirk @@ -0,0 +1,85 @@ +[DeviceInstanceId=HIDRAW\VEN_04F3] +Plugin = elantp +GType = FuElantpHidDevice + +# ThinkPad X1 Carbon Gen 9 +[HwId=6c87726f-b545-549e-840a-189422ea21d0] +Flags = elantp-recovery + +# Lenovo X1 Nano Gen1 +[HwId=4c20262a-aee0-5d6e-ba72-6d29b23fe350] +Flags = elantp-recovery + +# Acer Aspire V3-372T +[HwId=513cde3d-d939-59bd-a634-5c1645ebb93b] +Flags = elantp-recovery + +# recovery device +[DeviceInstanceId=I2C\NAME_Synopsys-DesignWare-I2C-adapter] +Plugin = elantp +GType = FuElantpI2cDevice +ElantpI2cTargetAddress = 0x15 + +[DeviceInstanceId=ELANTP\ICTYPE_00] +ElantpIcPageCount = 0x200 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_03] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_06] +ElantpIcPageCount = 0x200 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_07] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_08] +ElantpIcPageCount = 0x200 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_0A] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0xE15A + +[DeviceInstanceId=ELANTP\ICTYPE_0B] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_0C] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_0E] +ElantpIcPageCount = 0x280 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_09] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_0D] +ElantpIcPageCount = 0x380 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_10] +ElantpIcPageCount = 0x400 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_11] +ElantpIcPageCount = 0x500 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_13] +ElantpIcPageCount = 0x800 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_14] +ElantpIcPageCount = 0x400 +ElantpIapPassword = 0x1EA5 + +[DeviceInstanceId=ELANTP\ICTYPE_15] +ElantpIcPageCount = 0x400 +ElantpIapPassword = 0x1EA5 diff --git a/plugins/elantp/fu-elantp-common.c b/plugins/elantp/fu-elantp-common.c new file mode 100644 index 000000000..538421752 --- /dev/null +++ b/plugins/elantp/fu-elantp-common.c @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-elantp-common.h" + +guint16 +fu_elantp_calc_checksum (const guint8 *data, gsize length) +{ + guint16 checksum = 0; + for (gsize i = 0; i < length; i += 2) + checksum += ((guint16) (data[i+1]) << 8) | (data[i]); + return checksum; +} diff --git a/plugins/elantp/fu-elantp-common.h b/plugins/elantp/fu-elantp-common.h new file mode 100644 index 000000000..05d7ed818 --- /dev/null +++ b/plugins/elantp/fu-elantp-common.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define ETP_CMD_GET_HID_DESCRIPTOR 0x0001 +#define ETP_CMD_GET_HARDWARE_ID 0x0100 +#define ETP_CMD_GET_MODULE_ID 0x0101 +#define ETP_CMD_I2C_FW_CHECKSUM 0x030F +#define ETP_CMD_I2C_FW_VERSION 0x0102 +#define ETP_CMD_I2C_IAP 0x0311 +#define ETP_CMD_I2C_IAP_CHECKSUM 0x0315 +#define ETP_CMD_I2C_IAP_CTRL 0x0310 +#define ETP_CMD_I2C_IAP_ICBODY 0x0110 +#define ETP_CMD_I2C_IAP_RESET 0x0314 +#define ETP_CMD_I2C_IAP_VERSION 0x0111 +#define ETP_CMD_I2C_IAP_VERSION_2 0x0110 +#define ETP_CMD_I2C_OSM_VERSION 0x0103 +#define ETP_CMD_I2C_GET_HID_ID 0x0100 +#define ETP_CMD_I2C_IAP_TYPE 0x0304 + +#define ETP_I2C_IAP_TYPE_REG 0x0040 + +#define ETP_I2C_ENABLE_REPORT 0x0800 + +#define ETP_I2C_IAP_RESET 0xF0F0 +#define ETP_I2C_MAIN_MODE_ON (1 << 9) + +#define ETP_I2C_IAP_REG_L 0x01 +#define ETP_I2C_IAP_REG_H 0x06 + +#define ETP_FW_IAP_INTF_ERR (1 << 4) +#define ETP_FW_IAP_PAGE_ERR (1 << 5) +#define ETP_FW_IAP_CHECK_PW (1 << 7) +#define ETP_FW_IAP_LAST_FIT (1 << 9) + + +#define ELANTP_DELAY_COMPLETE 1200 /* ms */ +#define ELANTP_DELAY_RESET 30 /* ms */ +#define ELANTP_DELAY_UNLOCK 100 /* ms */ +#define ELANTP_DELAY_WRITE_BLOCK 35 /* ms */ +#define ELANTP_DELAY_WRITE_BLOCK_512 50 /* ms */ + +guint16 fu_elantp_calc_checksum (const guint8 *data, + gsize length); diff --git a/plugins/elantp/fu-elantp-firmware.c b/plugins/elantp/fu-elantp-firmware.c new file mode 100644 index 000000000..bd9288b3f --- /dev/null +++ b/plugins/elantp/fu-elantp-firmware.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-common.h" + +#include "fu-elantp-common.h" +#include "fu-elantp-firmware.h" + +struct _FuElantpFirmware { + FuFirmwareClass parent_instance; + guint16 module_id; + guint16 iap_addr; +}; + +G_DEFINE_TYPE (FuElantpFirmware, fu_elantp_firmware, FU_TYPE_FIRMWARE) + +/* firmware block update */ +#define ETP_IAP_START_ADDR 0x0083 + +guint16 +fu_elantp_firmware_get_module_id (FuElantpFirmware *self) +{ + g_return_val_if_fail (FU_IS_ELANTP_FIRMWARE (self), 0); + return self->module_id; +} + +guint16 +fu_elantp_firmware_get_iap_addr (FuElantpFirmware *self) +{ + g_return_val_if_fail (FU_IS_ELANTP_FIRMWARE (self), 0); + return self->iap_addr; +} + +static void +fu_elantp_firmware_to_string (FuFirmware *firmware, guint idt, GString *str) +{ + FuElantpFirmware *self = FU_ELANTP_FIRMWARE (firmware); + fu_common_string_append_kx (str, idt, "IapAddr", self->iap_addr); + fu_common_string_append_kx (str, idt, "ModuleId", self->module_id); +} + +static gboolean +fu_elantp_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + FuElantpFirmware *self = FU_ELANTP_FIRMWARE (firmware); + gsize bufsz = 0; + guint16 iap_addr_wrds; + guint16 module_id_wrds; + const guint8 *buf = g_bytes_get_data (fw, &bufsz); + g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (fw); + + /* presumably in words */ + if (!fu_common_read_uint16_safe (buf, bufsz, ETP_IAP_START_ADDR * 2, + &iap_addr_wrds, G_LITTLE_ENDIAN, error)) + return FALSE; + self->iap_addr = iap_addr_wrds * 2; + + /* read module ID */ + if (!fu_common_read_uint16_safe (buf, bufsz, self->iap_addr, + &module_id_wrds, G_LITTLE_ENDIAN, error)) + return FALSE; + if (!fu_common_read_uint16_safe (buf, bufsz, module_id_wrds * 2, + &self->module_id, G_LITTLE_ENDIAN, error)) + return FALSE; + + /* whole image */ + fu_firmware_add_image (firmware, img); + return TRUE; +} + +static void +fu_elantp_firmware_init (FuElantpFirmware *self) +{ +} + +static void +fu_elantp_firmware_class_init (FuElantpFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_elantp_firmware_parse; + klass_firmware->to_string = fu_elantp_firmware_to_string; +} + +FuFirmware * +fu_elantp_firmware_new (void) +{ + return FU_FIRMWARE (g_object_new (FU_TYPE_ELANTP_FIRMWARE, NULL)); +} diff --git a/plugins/elantp/fu-elantp-firmware.h b/plugins/elantp/fu-elantp-firmware.h new file mode 100644 index 000000000..1881b9794 --- /dev/null +++ b/plugins/elantp/fu-elantp-firmware.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_ELANTP_FIRMWARE (fu_elantp_firmware_get_type ()) +G_DECLARE_FINAL_TYPE (FuElantpFirmware, fu_elantp_firmware, FU, ELANTP_FIRMWARE, FuFirmware) + +FuFirmware *fu_elantp_firmware_new (void); +guint16 fu_elantp_firmware_get_module_id (FuElantpFirmware *self); +guint16 fu_elantp_firmware_get_iap_addr (FuElantpFirmware *self); diff --git a/plugins/elantp/fu-elantp-hid-device.c b/plugins/elantp/fu-elantp-hid-device.c new file mode 100644 index 000000000..809cd24cb --- /dev/null +++ b/plugins/elantp/fu-elantp-hid-device.c @@ -0,0 +1,577 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-elantp-common.h" +#include "fu-elantp-firmware.h" +#include "fu-elantp-hid-device.h" + +#include "fu-chunk.h" + +struct _FuElantpHidDevice { + FuUdevDevice parent_instance; + guint16 ic_page_count; + guint16 iap_type; + guint16 iap_ctrl; + guint16 iap_password; + guint16 module_id; + guint16 fw_page_size; + guint8 pattern; +}; + +G_DEFINE_TYPE (FuElantpHidDevice, fu_elantp_hid_device, FU_TYPE_UDEV_DEVICE) + +static void +fu_elantp_hid_device_to_string (FuDevice *device, guint idt, GString *str) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE (device); + fu_common_string_append_kx (str, idt, "ModuleId", self->module_id); + fu_common_string_append_kx (str, idt, "Pattern", self->pattern); + fu_common_string_append_kx (str, idt, "FwPageSize", self->fw_page_size); + fu_common_string_append_kx (str, idt, "IcPageCount", self->ic_page_count); + fu_common_string_append_kx (str, idt, "IapType", self->iap_type); + fu_common_string_append_kx (str, idt, "IapCtrl", self->iap_ctrl); +} + +static gboolean +fu_elantp_hid_device_probe (FuUdevDevice *device, GError **error) +{ + /* check is valid */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "hidraw") != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not correct subsystem=%s, expected hidraw", + fu_udev_device_get_subsystem (device)); + return FALSE; + } + + /* i2c-hid */ + if (fu_udev_device_get_model (device) < 0x3000 || + fu_udev_device_get_model (device) >= 0x4000) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not i2c-hid touchpad"); + return FALSE; + } + + /* set the physical ID */ + return fu_udev_device_set_physical_id (device, "hid", error); +} + +static gboolean +fu_elantp_hid_device_send_cmd (FuElantpHidDevice *self, + guint8 *tx, gsize txsz, + guint8 *rx, gsize rxsz, + GError **error) +{ + g_autofree guint8 *buf = NULL; + gsize bufsz = rxsz + 3; + + if (g_getenv ("FWUPD_ELANTP_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "SetReport", tx, txsz); + if (!fu_udev_device_ioctl (FU_UDEV_DEVICE (self), + HIDIOCSFEATURE(txsz), tx, + NULL, error)) + return FALSE; + if (rxsz == 0) + return TRUE; + + /* GetFeature */ + buf = g_malloc0 (bufsz); + buf[0] = tx[0]; /* report number */ + if (!fu_udev_device_ioctl (FU_UDEV_DEVICE (self), + HIDIOCGFEATURE(bufsz), buf, + NULL, error)) + return FALSE; + if (g_getenv ("FWUPD_ELANTP_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "GetReport", buf, bufsz); + + /* success */ + return fu_memcpy_safe (rx, rxsz, 0x0, /* dst */ + buf, bufsz, 0x3, /* src */ + rxsz, error); +} + +static gboolean +fu_elantp_hid_device_read_cmd (FuElantpHidDevice *self, guint16 reg, + guint8 *rx, gsize rxsz, GError **error) +{ + guint8 buf[5] = { 0x0d, 0x05, 0x03 }; + fu_common_write_uint16 (buf + 0x3, reg, G_LITTLE_ENDIAN); + return fu_elantp_hid_device_send_cmd (self, buf, sizeof(buf), rx, rxsz, error); +} + +static gint +fu_elantp_hid_device_write_cmd (FuElantpHidDevice *self, + guint16 reg, guint16 cmd, + GError **error) +{ + guint8 buf[5] = { 0x0d }; + fu_common_write_uint16 (buf + 0x1, reg, G_LITTLE_ENDIAN); + fu_common_write_uint16 (buf + 0x3, cmd, G_LITTLE_ENDIAN); + return fu_elantp_hid_device_send_cmd (self, buf, sizeof(buf), NULL, 0, error); +} + +static gboolean +fu_elantp_hid_device_ensure_iap_ctrl (FuElantpHidDevice *self, GError **error) +{ + guint8 buf[2] = { 0x0 }; + if (!fu_elantp_hid_device_read_cmd (self, ETP_CMD_I2C_IAP_CTRL, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read IAPControl: "); + return FALSE; + } + self->iap_ctrl = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + + /* in bootloader mode? */ + if ((self->iap_ctrl & ETP_I2C_MAIN_MODE_ON) == 0) + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + else + fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + + return TRUE; +} + +static gboolean +fu_elantp_hid_device_setup (FuDevice *device, GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE (device); + FuUdevDevice *udev_device = FU_UDEV_DEVICE (device); + guint16 fwver; + guint16 iap_ver; + guint16 tmp; + guint8 buf[2] = { 0x0 }; + guint8 ic_type; + g_autofree gchar *instance_id1 = NULL; + g_autofree gchar *instance_id2 = NULL; + g_autofree gchar *instance_id_ic_type = NULL; + g_autofree gchar *version_bl = NULL; + g_autofree gchar *version = NULL; + + /* get pattern */ + if (!fu_elantp_hid_device_read_cmd (self, + ETP_CMD_I2C_GET_HID_ID, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read HID ID: "); + return FALSE; + } + tmp = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + self->pattern = tmp != 0xffff ? (tmp & 0xff00) >> 8 : 0; + + /* get current firmware version */ + if (!fu_elantp_hid_device_read_cmd (self, + ETP_CMD_I2C_FW_VERSION, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read fw version: "); + return FALSE; + } + fwver = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + if (fwver == 0xFFFF || fwver == ETP_CMD_I2C_FW_VERSION) + fwver = 0; + version = fu_common_version_from_uint16 (fwver, FWUPD_VERSION_FORMAT_HEX); + fu_device_set_version (device, version); + + /* get IAP firmware version */ + if (!fu_elantp_hid_device_read_cmd (self, + self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION : ETP_CMD_I2C_IAP_VERSION_2, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read bootloader version: "); + return FALSE; + } + if (self->pattern >= 1) { + iap_ver = buf[1]; + } else { + iap_ver = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + } + version_bl = fu_common_version_from_uint16 (iap_ver, FWUPD_VERSION_FORMAT_HEX); + fu_device_set_version_bootloader (device, version_bl); + + /* get module ID */ + if (!fu_elantp_hid_device_read_cmd (self, + ETP_CMD_GET_MODULE_ID, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read module ID: "); + return FALSE; + } + self->module_id = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + + /* define the extra instance IDs */ + instance_id1 = g_strdup_printf ("HIDRAW\\VEN_%04X&DEV_%04X&MOD_%04X", + fu_udev_device_get_vendor (udev_device), + fu_udev_device_get_model (udev_device), + self->module_id); + fu_device_add_instance_id (device, instance_id1); + + /* get OSM version */ + if (!fu_elantp_hid_device_read_cmd (self, ETP_CMD_I2C_OSM_VERSION, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read OSM version: "); + return FALSE; + } + tmp = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (!fu_elantp_hid_device_read_cmd (self, ETP_CMD_I2C_IAP_ICBODY, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read IC body: "); + return FALSE; + } + ic_type = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN) & 0xFF; + } else { + ic_type = (tmp >> 8) & 0xFF; + } + instance_id_ic_type = g_strdup_printf ("ELANTP\\ICTYPE_%02X", ic_type); + fu_device_add_instance_id (device, instance_id_ic_type); + + /* define the extra instance IDs (ic_type + module_id) */ + instance_id2 = g_strdup_printf ("ELANTP\\ICTYPE_%02X&MOD_%04X", + ic_type, self->module_id); + fu_device_add_instance_id (device, instance_id2); + + /* no quirk entry */ + if (self->ic_page_count == 0x0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no page count for ELANTP\\ICTYPE_%02X", + ic_type); + return FALSE; + } + + /* The ic_page_count is based on 64 bytes/page. */ + fu_device_set_firmware_size (device, (guint64) self->ic_page_count * (guint64) 64); + + /* is in bootloader mode */ + if (!fu_elantp_hid_device_ensure_iap_ctrl (self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_elantp_hid_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE (device); + guint16 module_id; + g_autoptr(FuFirmware) firmware = fu_elantp_firmware_new (); + + /* check is compatible with hardware */ + if (!fu_firmware_parse (firmware, fw, flags, error)) + return NULL; + module_id = fu_elantp_firmware_get_module_id (FU_ELANTP_FIRMWARE (firmware)); + if (self->module_id != module_id) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware incompatible, got 0x%04x, expected 0x%04x", + module_id, self->module_id); + return NULL; + } + + /* success */ + return g_steal_pointer (&firmware); +} + +static gboolean +fu_elantp_hid_device_write_firmware (FuDevice *device, + FuFirmware *firmware, + FwupdInstallFlags flags, + GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE (device); + FuElantpFirmware *firmware_elantp = FU_ELANTP_FIRMWARE (firmware); + gsize bufsz = 0; + guint16 checksum = 0; + guint16 checksum_device = 0; + guint16 iap_addr; + const guint8 *buf; + guint8 csum_buf[2] = { 0x0 }; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* simple image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + + /* write each block */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + buf = g_bytes_get_data (fw, &bufsz); + iap_addr = fu_elantp_firmware_get_iap_addr (firmware_elantp); + chunks = fu_chunk_array_new (buf + iap_addr, bufsz - iap_addr, 0x0, 0x0, self->fw_page_size); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + guint16 csum_tmp = fu_elantp_calc_checksum (chk->data, chk->data_sz); + gsize blksz = self->fw_page_size + 3; + g_autofree guint8 *blk = g_malloc0 (blksz); + + /* write block */ + blk[0] = 0x0B; /* report ID */ + if (!fu_memcpy_safe (blk, blksz, 0x1, /* dst */ + chk->data, chk->data_sz, 0x0, /* src */ + chk->data_sz, error)) + return FALSE; + fu_common_write_uint16 (blk + chk->data_sz + 1, csum_tmp, G_LITTLE_ENDIAN); + + if (!fu_elantp_hid_device_send_cmd (self, blk, blksz, NULL, 0, error)) + return FALSE; + g_usleep (self->fw_page_size == 512 ? + ELANTP_DELAY_WRITE_BLOCK_512 * 1000 : + ELANTP_DELAY_WRITE_BLOCK * 1000); + + if (!fu_elantp_hid_device_ensure_iap_ctrl (self, error)) + return FALSE; + if (self->iap_ctrl & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "bootloader reports failed write: 0x%x", + self->iap_ctrl); + return FALSE; + } + + /* update progress */ + checksum += csum_tmp; + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len); + } + + /* verify the written checksum */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_VERIFY); + if (!fu_elantp_hid_device_read_cmd (self, ETP_CMD_I2C_IAP_CHECKSUM, + csum_buf, sizeof(csum_buf), error)) + return FALSE; + checksum_device = fu_common_read_uint16 (csum_buf, G_LITTLE_ENDIAN); + if (checksum != checksum_device) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "checksum failed 0x%04x != 0x%04x", + checksum, checksum_device); + return FALSE; + } + + /* wait for a reset */ + fu_device_set_progress (device, 0); + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + g_usleep (ELANTP_DELAY_COMPLETE * 1000); + return TRUE; +} + +static gboolean +fu_elantp_hid_device_detach (FuDevice *device, GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE (device); + guint16 iap_ver; + guint16 ic_type; + guint8 buf[2] = { 0x0 }; + guint16 tmp; + + /* sanity check */ + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug ("in bootloader mode, reset IC"); + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + if (!fu_elantp_hid_device_write_cmd (self, + ETP_CMD_I2C_IAP_RESET, + ETP_I2C_IAP_RESET, + error)) + return FALSE; + g_usleep (ELANTP_DELAY_RESET * 1000); + } + + /* get OSM version */ + if (!fu_elantp_hid_device_read_cmd (self, ETP_CMD_I2C_OSM_VERSION, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read OSM version: "); + return FALSE; + } + tmp = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (!fu_elantp_hid_device_read_cmd (self, ETP_CMD_I2C_IAP_ICBODY, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read IC body: "); + return FALSE; + } + ic_type = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN) & 0xFF; + } else { + ic_type = (tmp >> 8) & 0xFF; + } + + /* get IAP firmware version */ + if (!fu_elantp_hid_device_read_cmd (self, + self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION : ETP_CMD_I2C_IAP_VERSION_2, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read bootloader version: "); + return FALSE; + } + if (self->pattern >= 1) { + iap_ver = buf[1]; + } else { + iap_ver = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + } + if (ic_type >= 0x10) { + if (iap_ver >= 1) { + /* set the IAP type, presumably some kind of ABI */ + if (iap_ver >= 2 && (ic_type == 0x14 || ic_type==0x15)) { + self->fw_page_size = 512; + } else { + self->fw_page_size = 128; + } + + if (!fu_elantp_hid_device_write_cmd (self, + ETP_CMD_I2C_IAP_TYPE, + self->fw_page_size / 2, + error)) + return FALSE; + if (!fu_elantp_hid_device_read_cmd (self, ETP_CMD_I2C_IAP_TYPE, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read IAP type: "); + return FALSE; + } + self->iap_type = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + if (self->iap_type != self->fw_page_size / 2) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to set IAP type"); + return FALSE; + } + + } + } + if (!fu_elantp_hid_device_write_cmd (self, + ETP_CMD_I2C_IAP, + self->iap_password, + error)) + return FALSE; + g_usleep (ELANTP_DELAY_UNLOCK * 1000); + if (!fu_elantp_hid_device_ensure_iap_ctrl (self, error)) + return FALSE; + if ((self->iap_ctrl & ETP_FW_IAP_CHECK_PW) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "unexpected bootloader password"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_hid_device_attach (FuDevice *device, GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE (device); + + /* sanity check */ + if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug ("already in runtime mode, skipping"); + return TRUE; + } + + /* reset back to runtime */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + if (!fu_elantp_hid_device_write_cmd (self, ETP_CMD_I2C_IAP_RESET, ETP_I2C_IAP_RESET, error)) + return FALSE; + g_usleep (ELANTP_DELAY_RESET * 1000); + if (!fu_elantp_hid_device_write_cmd (self, ETP_CMD_I2C_IAP_RESET, ETP_I2C_ENABLE_REPORT, error)) { + g_prefix_error (error, "cannot enable TP report: "); + return FALSE; + } + if (!fu_elantp_hid_device_write_cmd (self, 0x0306, 0x003, error)) { + g_prefix_error (error, "cannot switch to TP PTP mode: "); + return FALSE; + } + if (!fu_elantp_hid_device_ensure_iap_ctrl (self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_hid_device_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE (device); + if (g_strcmp0 (key, "ElantpIcPageCount") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp > 0xffff) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "ElantpIcPageCount only supports " + "values <= 0xffff"); + return FALSE; + } + self->ic_page_count = (guint16) tmp; + return TRUE; + } + if (g_strcmp0 (key, "ElantpIapPassword") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp > 0xffff) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "ElantpIapPassword only supports " + "values <= 0xffff"); + return FALSE; + } + self->iap_password = (guint16) tmp; + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_elantp_hid_device_init (FuElantpHidDevice *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_summary (FU_DEVICE (self), "Elan Touchpad"); + fu_device_add_icon (FU_DEVICE (self), "input-touchpad"); + fu_device_set_protocol (FU_DEVICE (self), "tw.com.emc.elantp"); + fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_HEX); + fu_device_set_priority (FU_DEVICE (self), 1); /* better than i2c */ + fu_udev_device_set_flags (FU_UDEV_DEVICE (self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | + FU_UDEV_DEVICE_FLAG_OPEN_WRITE | + FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK); +} + +static void +fu_elantp_hid_device_finalize (GObject *object) +{ + G_OBJECT_CLASS (fu_elantp_hid_device_parent_class)->finalize (object); +} + +static void +fu_elantp_hid_device_class_init (FuElantpHidDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUdevDeviceClass *klass_udev_device = FU_UDEV_DEVICE_CLASS (klass); + object_class->finalize = fu_elantp_hid_device_finalize; + klass_device->to_string = fu_elantp_hid_device_to_string; + klass_device->attach = fu_elantp_hid_device_attach; + klass_device->detach = fu_elantp_hid_device_detach; + klass_device->set_quirk_kv = fu_elantp_hid_device_set_quirk_kv; + klass_device->setup = fu_elantp_hid_device_setup; + klass_device->reload = fu_elantp_hid_device_setup; + klass_device->write_firmware = fu_elantp_hid_device_write_firmware; + klass_device->prepare_firmware = fu_elantp_hid_device_prepare_firmware; + klass_udev_device->probe = fu_elantp_hid_device_probe; +} diff --git a/plugins/elantp/fu-elantp-hid-device.h b/plugins/elantp/fu-elantp-hid-device.h new file mode 100644 index 000000000..06d058ca1 --- /dev/null +++ b/plugins/elantp/fu-elantp-hid-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +#define FU_TYPE_ELANTP_HID_DEVICE (fu_elantp_hid_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuElantpHidDevice, fu_elantp_hid_device, FU, ELANTP_HID_DEVICE, FuUdevDevice) diff --git a/plugins/elantp/fu-elantp-i2c-device.c b/plugins/elantp/fu-elantp-i2c-device.c new file mode 100644 index 000000000..362f46136 --- /dev/null +++ b/plugins/elantp/fu-elantp-i2c-device.c @@ -0,0 +1,616 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include + +#include "config.h" + +#include "fu-elantp-common.h" +#include "fu-elantp-firmware.h" +#include "fu-elantp-i2c-device.h" +#include "fu-chunk.h" + +struct _FuElantpI2cDevice { + FuUdevDevice parent_instance; + guint16 i2c_addr; + guint16 ic_page_count; + guint16 iap_type; + guint16 iap_ctrl; + guint16 iap_password; + guint16 module_id; + guint16 fw_page_size; + guint8 pattern; +}; + +G_DEFINE_TYPE (FuElantpI2cDevice, fu_elantp_i2c_device, FU_TYPE_UDEV_DEVICE) + +static void +fu_elantp_i2c_device_to_string (FuDevice *device, guint idt, GString *str) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE (device); + fu_common_string_append_kx (str, idt, "I2cAddr", self->i2c_addr); + fu_common_string_append_kx (str, idt, "ModuleId", self->module_id); + fu_common_string_append_kx (str, idt, "Pattern", self->pattern); + fu_common_string_append_kx (str, idt, "FwPageSize", self->fw_page_size); + fu_common_string_append_kx (str, idt, "IcPageCount", self->ic_page_count); + fu_common_string_append_kx (str, idt, "IapType", self->iap_type); + fu_common_string_append_kx (str, idt, "IapCtrl", self->iap_ctrl); +} + +static gboolean +fu_elantp_i2c_device_probe (FuUdevDevice *device, GError **error) +{ + /* check is valid */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "i2c-dev") != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not correct subsystem=%s, expected i2c-dev", + fu_udev_device_get_subsystem (device)); + return FALSE; + } + if (fu_udev_device_get_device_file (device) == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no device file"); + return FALSE; + } + + /* set the physical ID */ + return fu_udev_device_set_physical_id (device, "i2c", error); +} + +static gboolean +fu_elantp_i2c_device_send_cmd (FuElantpI2cDevice *self, + guint8 *tx, gssize txsz, + guint8 *rx, gssize rxsz, + GError **error) +{ + if (g_getenv ("FWUPD_ELANTP_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "Write", tx, txsz); + if (!fu_udev_device_pwrite_full (FU_UDEV_DEVICE (self), 0, tx, txsz, error)) + return FALSE; + if (rxsz == 0) + return TRUE; + if (!fu_udev_device_pread_full (FU_UDEV_DEVICE (self), 0, rx, rxsz, error)) + return FALSE; + if (g_getenv ("FWUPD_ELANTP_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "Read", rx, rxsz); + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_write_cmd (FuElantpI2cDevice *self, guint16 reg, guint16 cmd, GError **error) +{ + guint8 buf[4]; + fu_common_write_uint16 (buf + 0x0, reg, G_LITTLE_ENDIAN); + fu_common_write_uint16 (buf + 0x2, cmd, G_LITTLE_ENDIAN); + return fu_elantp_i2c_device_send_cmd (self, buf, sizeof(buf), NULL, 0, error); +} + +static gboolean +fu_elantp_i2c_device_read_cmd (FuElantpI2cDevice *self, guint16 reg, + guint8 *rx, gsize rxsz, GError **error) +{ + guint8 buf[2]; + fu_common_write_uint16 (buf + 0x0, reg, G_LITTLE_ENDIAN); + return fu_elantp_i2c_device_send_cmd (self, buf, sizeof(buf), + rx, rxsz, error); +} + +static gboolean +fu_elantp_i2c_device_ensure_iap_ctrl (FuElantpI2cDevice *self, GError **error) +{ + guint8 buf[2] = { 0x0 }; + if (!fu_elantp_i2c_device_read_cmd (self, ETP_CMD_I2C_IAP_CTRL, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read IAPControl: "); + return FALSE; + } + self->iap_ctrl = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + + /* in bootloader mode? */ + if ((self->iap_ctrl & ETP_I2C_MAIN_MODE_ON) == 0) + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + else + fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_setup (FuDevice *device, GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE (device); + guint16 fwver; + guint16 iap_ver; + guint16 tmp; + guint32 pid; + guint32 vid; + guint8 buf[30] = { 0x0 }; + guint8 ic_type; + g_autofree gchar *instance_id1 = NULL; + g_autofree gchar *instance_id2 = NULL; + g_autofree gchar *instance_id_ic_type = NULL; + g_autofree gchar *version_bl = NULL; + g_autofree gchar *version = NULL; + + /* read the I2C descriptor */ + if (!fu_elantp_i2c_device_read_cmd (self, ETP_CMD_GET_HID_DESCRIPTOR, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to get HID descriptor: "); + return FALSE; + } + vid = fu_common_read_uint16 (buf + 20, G_LITTLE_ENDIAN); + pid = fu_common_read_uint16 (buf + 22, G_LITTLE_ENDIAN); + + /* set the vendor ID */ + if (vid != 0x0000) { + g_autofree gchar *vendor_id = NULL; + vendor_id = g_strdup_printf ("HIDRAW:0x%04X", vid); + fu_device_set_vendor_id (device, vendor_id); + } + + /* add GUIDs in order of priority */ + if (vid != 0x0 && pid != 0x0) { + g_autofree gchar *devid = NULL; + devid = g_strdup_printf ("HIDRAW\\VID_%04X&PID_%04X", + vid, pid); + fu_device_add_instance_id (device, devid); + } + + /* get pattern */ + if (!fu_elantp_i2c_device_read_cmd (self, + ETP_CMD_I2C_GET_HID_ID, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read I2C ID: "); + return FALSE; + } + tmp = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + self->pattern = tmp != 0xffff ? (tmp & 0xff00) >> 8 : 0; + + /* get current firmware version */ + if (!fu_elantp_i2c_device_read_cmd (self, + ETP_CMD_I2C_FW_VERSION, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read fw version: "); + return FALSE; + } + fwver = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + if (fwver == 0xFFFF || fwver == ETP_CMD_I2C_FW_VERSION) + fwver = 0; + version = fu_common_version_from_uint16 (fwver, FWUPD_VERSION_FORMAT_HEX); + fu_device_set_version (device, version); + + /* get IAP firmware version */ + if (!fu_elantp_i2c_device_read_cmd (self, + self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION : ETP_CMD_I2C_IAP_VERSION_2, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read bootloader version: "); + return FALSE; + } + if (self->pattern >= 1) { + iap_ver = buf[1]; + } else { + iap_ver = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + } + version_bl = fu_common_version_from_uint16 (iap_ver, FWUPD_VERSION_FORMAT_HEX); + fu_device_set_version_bootloader (device, version_bl); + + /* get module ID */ + if (!fu_elantp_i2c_device_read_cmd (self, + ETP_CMD_GET_MODULE_ID, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read module ID: "); + return FALSE; + } + self->module_id = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + + /* define the extra instance IDs */ + instance_id1 = g_strdup_printf ("HIDRAW\\VEN_%04X&DEV_%04X&MOD_%04X", + vid, pid, self->module_id); + fu_device_add_instance_id (device, instance_id1); + + /* get OSM version */ + if (!fu_elantp_i2c_device_read_cmd (self, ETP_CMD_I2C_OSM_VERSION, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read OSM version: "); + return FALSE; + } + tmp = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (!fu_elantp_i2c_device_read_cmd (self, ETP_CMD_I2C_IAP_ICBODY, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read IC body: "); + return FALSE; + } + ic_type = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN) & 0xFF; + } else { + ic_type = (tmp >> 8) & 0xFF; + } + instance_id_ic_type = g_strdup_printf ("ELANTP\\ICTYPE_%02X", ic_type); + fu_device_add_instance_id (device, instance_id_ic_type); + + /* define the extra instance IDs (ic_type + module_id) */ + instance_id2 = g_strdup_printf ("ELANTP\\ICTYPE_%02X&MOD_%04X", + ic_type, self->module_id); + fu_device_add_instance_id (device, instance_id2); + + /* no quirk entry */ + if (self->ic_page_count == 0x0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no page count for ELANTP\\ICTYPE_%02X", + ic_type); + return FALSE; + } + fu_device_set_firmware_size (device, (guint64) self->ic_page_count * (guint64) 64); + + /* is in bootloader mode */ + if (!fu_elantp_i2c_device_ensure_iap_ctrl (self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_open (FuUdevDevice *device, GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE (device); + gint addr = self->i2c_addr; + guint8 tx_buf[] = { 0x02, 0x01 }; + + /* set target address */ + if (!fu_udev_device_ioctl (device, I2C_SLAVE, + GINT_TO_POINTER (addr), NULL, NULL)) { + if (!fu_udev_device_ioctl (device, I2C_SLAVE_FORCE, + GINT_TO_POINTER (addr), NULL, error)) { + g_prefix_error (error, + "failed to set target address to 0x%x: ", + self->i2c_addr); + return FALSE; + } + } + + /* read i2c device */ + return fu_udev_device_pwrite_full (device, 0x0, tx_buf, sizeof(tx_buf), error); +} + +static FuFirmware * +fu_elantp_i2c_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE (device); + guint16 module_id; + g_autoptr(FuFirmware) firmware = fu_elantp_firmware_new (); + + /* check is compatible with hardware */ + if (!fu_firmware_parse (firmware, fw, flags, error)) + return NULL; + module_id = fu_elantp_firmware_get_module_id (FU_ELANTP_FIRMWARE (firmware)); + if (self->module_id != module_id) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware incompatible, got 0x%04x, expected 0x%04x", + module_id, self->module_id); + return NULL; + } + + /* success */ + return g_steal_pointer (&firmware); +} + +static gboolean +fu_elantp_i2c_device_write_firmware (FuDevice *device, + FuFirmware *firmware, + FwupdInstallFlags flags, + GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE (device); + FuElantpFirmware *firmware_elantp = FU_ELANTP_FIRMWARE (firmware); + gsize bufsz = 0; + guint16 checksum = 0; + guint16 checksum_device = 0; + guint16 iap_addr; + const guint8 *buf; + guint8 csum_buf[2] = { 0x0 }; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* simple image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + + /* write each block */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + buf = g_bytes_get_data (fw, &bufsz); + iap_addr = fu_elantp_firmware_get_iap_addr (firmware_elantp); + chunks = fu_chunk_array_new (buf + iap_addr, bufsz - iap_addr, 0x0, 0x0, self->fw_page_size); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + guint16 csum_tmp = fu_elantp_calc_checksum (chk->data, chk->data_sz); + gsize blksz = self->fw_page_size + 4; + g_autofree guint8 *blk = g_malloc0 (blksz); + + /* write block */ + blk[0] = ETP_I2C_IAP_REG_L; + blk[1] = ETP_I2C_IAP_REG_H; + if (!fu_memcpy_safe (blk, blksz, 0x2, /* dst */ + chk->data, chk->data_sz, 0x0, /* src */ + chk->data_sz, error)) + return FALSE; + + fu_common_write_uint16 (blk + chk->data_sz + 2, csum_tmp, G_LITTLE_ENDIAN); + + if (!fu_elantp_i2c_device_send_cmd (self, blk, blksz, NULL, 0, error)) + return FALSE; + g_usleep (self->fw_page_size == 512 ? + ELANTP_DELAY_WRITE_BLOCK_512 * 1000 : + ELANTP_DELAY_WRITE_BLOCK * 1000); + + if (!fu_elantp_i2c_device_ensure_iap_ctrl (self, error)) + return FALSE; + if (self->iap_ctrl & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "bootloader reports failed write: 0x%x", + self->iap_ctrl); + return FALSE; + } + + /* update progress */ + checksum += csum_tmp; + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len); + } + + /* verify the written checksum */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_VERIFY); + if (!fu_elantp_i2c_device_read_cmd (self, ETP_CMD_I2C_IAP_CHECKSUM, + csum_buf, sizeof(csum_buf), error)) + return FALSE; + checksum_device = fu_common_read_uint16 (csum_buf, G_LITTLE_ENDIAN); + if (checksum != checksum_device) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "checksum failed 0x%04x != 0x%04x", + checksum, checksum_device); + return FALSE; + } + + /* wait for a reset */ + fu_device_set_progress (device, 0); + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + g_usleep (ELANTP_DELAY_COMPLETE * 1000); + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_detach (FuDevice *device, GError **error) +{ + guint16 iap_ver; + guint16 ic_type; + guint8 buf[2] = { 0x0 }; + guint16 tmp; + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE (device); + + /* sanity check */ + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug ("in bootloader mode, reset IC"); + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + if (!fu_elantp_i2c_device_write_cmd (self, + ETP_CMD_I2C_IAP_RESET, + ETP_I2C_IAP_RESET, + error)) + return FALSE; + g_usleep (ELANTP_DELAY_RESET * 1000); + } + /* get OSM version */ + if (!fu_elantp_i2c_device_read_cmd (self, ETP_CMD_I2C_OSM_VERSION, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read OSM version: "); + return FALSE; + } + tmp = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (!fu_elantp_i2c_device_read_cmd (self, ETP_CMD_I2C_IAP_ICBODY, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read IC body: "); + return FALSE; + } + ic_type = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN) & 0xFF; + } else { + ic_type = (tmp >> 8) & 0xFF; + } + + /* get IAP firmware version */ + if (!fu_elantp_i2c_device_read_cmd (self, + self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION : ETP_CMD_I2C_IAP_VERSION_2, + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read bootloader version: "); + return FALSE; + } + if (self->pattern >= 1) { + iap_ver = buf[1]; + } else { + iap_ver = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + } + + /* set the page size */ + self->fw_page_size = 64; + if (ic_type >= 0x10) { + if (iap_ver >= 1) { + if (iap_ver >= 2 && (ic_type == 0x14 || ic_type==0x15)) { + self->fw_page_size = 512; + } else { + self->fw_page_size = 128; + } + /* set the IAP type, presumably some kind of ABI */ + if (!fu_elantp_i2c_device_write_cmd (self, + ETP_CMD_I2C_IAP_TYPE, + self->fw_page_size / 2, + error)) + return FALSE; + if (!fu_elantp_i2c_device_read_cmd (self, ETP_CMD_I2C_IAP_TYPE , + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to read IAP type: "); + return FALSE; + } + self->iap_type = fu_common_read_uint16 (buf, G_LITTLE_ENDIAN); + if (self->iap_type != self->fw_page_size / 2) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to set IAP type"); + return FALSE; + } + } + } + if (!fu_elantp_i2c_device_write_cmd (self, + ETP_CMD_I2C_IAP, + self->iap_password, + error)) + return FALSE; + g_usleep (ELANTP_DELAY_UNLOCK * 1000); + if (!fu_elantp_i2c_device_ensure_iap_ctrl (self, error)) + return FALSE; + if ((self->iap_ctrl & ETP_FW_IAP_CHECK_PW) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "unexpected bootloader password"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_attach (FuDevice *device, GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE (device); + + /* sanity check */ + if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug ("already in runtime mode, skipping"); + return TRUE; + } + + /* reset back to runtime */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + if (!fu_elantp_i2c_device_write_cmd (self, ETP_CMD_I2C_IAP_RESET, ETP_I2C_IAP_RESET, error)) + return FALSE; + g_usleep (ELANTP_DELAY_RESET * 1000); + if (!fu_elantp_i2c_device_write_cmd (self, ETP_CMD_I2C_IAP_RESET, ETP_I2C_ENABLE_REPORT, error)) { + g_prefix_error (error, "cannot enable TP report: "); + return FALSE; + } + if (!fu_elantp_i2c_device_write_cmd (self, 0x0306, 0x003, error)) { + g_prefix_error (error, "cannot switch to TP PTP mode: "); + return FALSE; + } + if (!fu_elantp_i2c_device_ensure_iap_ctrl (self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE (device); + if (g_strcmp0 (key, "ElantpIcPageCount") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp > 0xffff) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "ElantpIcPageCount only supports " + "values <= 0xffff"); + return FALSE; + } + self->ic_page_count = (guint16) tmp; + return TRUE; + } + if (g_strcmp0 (key, "ElantpIapPassword") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp > 0xffff) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "ElantpIapPassword only supports " + "values <= 0xffff"); + return FALSE; + } + self->iap_password = (guint16) tmp; + return TRUE; + } + if (g_strcmp0 (key, "ElantpI2cTargetAddress") == 0) { + guint64 tmp = fu_common_strtoull (value); + if (tmp > 0xffff) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "ElantpI2cTargetAddress only supports " + "values <= 0xffff"); + return FALSE; + } + self->i2c_addr = (guint16) tmp; + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_elantp_i2c_device_init (FuElantpI2cDevice *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_summary (FU_DEVICE (self), "Elan Touchpad (I²C Recovery)"); + fu_device_add_icon (FU_DEVICE (self), "input-touchpad"); + fu_device_set_protocol (FU_DEVICE (self), "tw.com.emc.elantp"); + fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_HEX); + fu_udev_device_set_flags (FU_UDEV_DEVICE (self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | + FU_UDEV_DEVICE_FLAG_OPEN_WRITE); +} + +static void +fu_elantp_i2c_device_finalize (GObject *object) +{ + G_OBJECT_CLASS (fu_elantp_i2c_device_parent_class)->finalize (object); +} + +static void +fu_elantp_i2c_device_class_init (FuElantpI2cDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUdevDeviceClass *klass_udev_device = FU_UDEV_DEVICE_CLASS (klass); + object_class->finalize = fu_elantp_i2c_device_finalize; + klass_device->to_string = fu_elantp_i2c_device_to_string; + klass_device->attach = fu_elantp_i2c_device_attach; + klass_device->detach = fu_elantp_i2c_device_detach; + klass_device->set_quirk_kv = fu_elantp_i2c_device_set_quirk_kv; + klass_device->setup = fu_elantp_i2c_device_setup; + klass_device->reload = fu_elantp_i2c_device_setup; + klass_device->write_firmware = fu_elantp_i2c_device_write_firmware; + klass_device->prepare_firmware = fu_elantp_i2c_device_prepare_firmware; + klass_udev_device->probe = fu_elantp_i2c_device_probe; + klass_udev_device->open = fu_elantp_i2c_device_open; +} diff --git a/plugins/elantp/fu-elantp-i2c-device.h b/plugins/elantp/fu-elantp-i2c-device.h new file mode 100644 index 000000000..0d2147a94 --- /dev/null +++ b/plugins/elantp/fu-elantp-i2c-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +#define FU_TYPE_ELANTP_I2C_DEVICE (fu_elantp_i2c_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuElantpI2cDevice, fu_elantp_i2c_device, FU, ELANTP_I2C_DEVICE, FuUdevDevice) diff --git a/plugins/elantp/fu-plugin-elantp.c b/plugins/elantp/fu-plugin-elantp.c new file mode 100644 index 000000000..e29db3e7c --- /dev/null +++ b/plugins/elantp/fu-plugin-elantp.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" + +#include "fu-elantp-firmware.h" +#include "fu-elantp-hid-device.h" +#include "fu-elantp-i2c-device.h" + +gboolean +fu_plugin_device_created (FuPlugin *plugin, FuDevice *dev, GError **error) +{ + if (fu_device_get_specialized_gtype (dev) == FU_TYPE_ELANTP_I2C_DEVICE && + !fu_plugin_has_custom_flag (plugin, "elantp-recovery")) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not required"); + return FALSE; + } + return TRUE; +} + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_udev_subsystem (plugin, "i2c-dev"); + fu_plugin_add_udev_subsystem (plugin, "hidraw"); + fu_plugin_add_firmware_gtype (plugin, "elantp", FU_TYPE_ELANTP_FIRMWARE); + fu_plugin_set_device_gtype (plugin, FU_TYPE_ELANTP_I2C_DEVICE); + fu_plugin_set_device_gtype (plugin, FU_TYPE_ELANTP_HID_DEVICE); +} diff --git a/plugins/elantp/meson.build b/plugins/elantp/meson.build new file mode 100644 index 000000000..c982ede1b --- /dev/null +++ b/plugins/elantp/meson.build @@ -0,0 +1,36 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginElantp"'] + +install_data([ + 'elantp.quirk', + ], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_elantp', + fu_hash, + sources : [ + 'fu-plugin-elantp.c', + 'fu-elantp-common.c', + 'fu-elantp-firmware.c', + 'fu-elantp-hid-device.c', + 'fu-elantp-i2c-device.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + c_args : [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + link_with : [ + fwupd, + fwupdplugin, + ], + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/emmc/README.md b/plugins/emmc/README.md index 249f7056b..0ce5aa33c 100644 --- a/plugins/emmc/README.md +++ b/plugins/emmc/README.md @@ -24,3 +24,7 @@ Vendor ID Security ------------------ The vendor ID is set from the EMMC vendor, for example set to `EMMC:{$manfid}` + +External interface access +------------------------- +This plugin requires ioctl `MMC_IOC_CMD` and `MMC_IOC_MULTI_CMD` access. diff --git a/plugins/emmc/fu-emmc-device.c b/plugins/emmc/fu-emmc-device.c index 301f55f05..776645d06 100644 --- a/plugins/emmc/fu-emmc-device.c +++ b/plugins/emmc/fu-emmc-device.c @@ -214,7 +214,7 @@ fu_emmc_device_probe (FuUdevDevice *device, GError **error) if (flag == 0) fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_INTERNAL); - /* firwmare version */ + /* firmware version */ tmp = g_udev_device_get_sysfs_attr (udev_parent, "fwrev"); if (tmp != NULL) { fu_device_set_version_format (FU_DEVICE (device), FWUPD_VERSION_FORMAT_NUMBER); diff --git a/plugins/ep963x/README.md b/plugins/ep963x/README.md index c510cc19f..14bb362c9 100644 --- a/plugins/ep963x/README.md +++ b/plugins/ep963x/README.md @@ -29,3 +29,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, in this instance set to `USB:0x17EF` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/ep963x/fu-ep963x-device.c b/plugins/ep963x/fu-ep963x-device.c index 916b64c31..2c833b1ed 100644 --- a/plugins/ep963x/fu-ep963x-device.c +++ b/plugins/ep963x/fu-ep963x-device.c @@ -182,7 +182,6 @@ fu_ep963x_device_prepare_firmware (FuDevice *device, GError **error) { g_autoptr(FuFirmware) firmware = fu_ep963x_firmware_new (); - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; return g_steal_pointer (&firmware); diff --git a/plugins/fastboot/README.md b/plugins/fastboot/README.md index 2850a9246..d99cc8012 100644 --- a/plugins/fastboot/README.md +++ b/plugins/fastboot/README.md @@ -43,3 +43,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, for example `USB:0x18D1` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/fastboot/fu-fastboot-device.c b/plugins/fastboot/fu-fastboot-device.c index c7857f1f8..5887819af 100644 --- a/plugins/fastboot/fu-fastboot-device.c +++ b/plugins/fastboot/fu-fastboot-device.c @@ -656,7 +656,7 @@ fu_fastboot_device_set_quirk_kv (FuDevice *device, { FuFastbootDevice *self = FU_FASTBOOT_DEVICE (device); - /* load slave address from quirks */ + /* load from quirks */ if (g_strcmp0 (key, "FastbootBlockSize") == 0) { guint64 tmp = fu_common_strtoull (value); if (tmp >= 0x40 && tmp < 0x100000) { diff --git a/plugins/flashrom/README.md b/plugins/flashrom/README.md index f8b718244..b3fb1a257 100644 --- a/plugins/flashrom/README.md +++ b/plugins/flashrom/README.md @@ -28,3 +28,8 @@ Vendor ID Security ------------------ The vendor ID is set from the BIOS vendor, for example `DMI:Google` + +External interface access +--- +This plugin requires access to all interfaces that `libflashrom` has been compiled for. +This typically is `/sys/bus/spi` but there may be other interfaces as well. diff --git a/plugins/fresco-pd/README.md b/plugins/fresco-pd/README.md index c592e9059..6f87a3ea5 100644 --- a/plugins/fresco-pd/README.md +++ b/plugins/fresco-pd/README.md @@ -33,3 +33,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, in this instance set to `USB:0x1D5C` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/fresco-pd/fu-fresco-pd-device.c b/plugins/fresco-pd/fu-fresco-pd-device.c index 745f6f251..df1d9977c 100644 --- a/plugins/fresco-pd/fu-fresco-pd-device.c +++ b/plugins/fresco-pd/fu-fresco-pd-device.c @@ -203,19 +203,7 @@ fu_fresco_pd_device_prepare_firmware (FuDevice *device, guint8 customer_id; g_autoptr(FuFirmware) firmware = fu_fresco_pd_firmware_new (); - /* check size */ - if (g_bytes_get_size (fw) < fu_device_get_firmware_size_min (device)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware too small, got 0x%x, expected >= 0x%x", - (guint) g_bytes_get_size (fw), - (guint) fu_device_get_firmware_size_min (device)); - return NULL; - } - /* check firmware is suitable */ - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; customer_id = fu_fresco_pd_firmware_get_customer_id (FU_FRESCO_PD_FIRMWARE (firmware)); diff --git a/plugins/goodix-moc/README.md b/plugins/goodix-moc/README.md new file mode 100644 index 000000000..17c0cd700 --- /dev/null +++ b/plugins/goodix-moc/README.md @@ -0,0 +1,35 @@ +Goodix Fingerprint Sensor Support +================================= + +Introduction +------------ + +The plugin used for update firmware for fingerprint sensors from Goodix. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +a packed binary file format. + +This plugin supports the following protocol ID: + + * com.goodix.goodixmoc + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_27C6&PID_6001&REV_0001` + * `USB\VID_27C6&PID_6001` + * `USB\VID_27C6` + +Vendor ID Security +------------------ + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x27C6` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/goodix-moc/fu-goodixmoc-common.c b/plugins/goodix-moc/fu-goodixmoc-common.c new file mode 100644 index 000000000..edcb582d6 --- /dev/null +++ b/plugins/goodix-moc/fu-goodixmoc-common.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * Copyright (C) 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-common.h" +#include "fu-goodixmoc-common.h" + +void +fu_goodixmoc_build_header (GxfpPkgHeader *pheader, + guint16 len, + guint8 cmd0, + guint8 cmd1, + GxPkgType type) +{ + static guint8 dummy_seq = 0; + + g_return_if_fail (pheader != NULL); + + pheader->cmd0 = (cmd0); + pheader->cmd1 = (cmd1); + pheader->pkg_flag = (guint8)type; + pheader->reserved = dummy_seq++; + pheader->len = len + GX_SIZE_CRC32; + pheader->crc8 = fu_common_crc8 ((guint8 *)pheader, 6); + pheader->rev_crc8 = ~pheader->crc8; +} + +gboolean +fu_goodixmoc_parse_header (guint8 *buf, guint32 bufsz, + GxfpPkgHeader *pheader, GError **error) +{ + g_return_val_if_fail (buf != NULL, FALSE); + g_return_val_if_fail (pheader != NULL, FALSE); + + if (!fu_memcpy_safe ((guint8 *) &pheader, sizeof(*pheader), 0x0, /* dst */ + buf, bufsz, 0x01, /* src */ + sizeof(*pheader), error)) + return FALSE; + memcpy (pheader, buf, sizeof(*pheader)); + pheader->len = GUINT16_FROM_LE(*(buf + 4)); + pheader->len -= GX_SIZE_CRC32; + return TRUE; +} + +gboolean +fu_goodixmoc_parse_body (guint8 cmd, guint8 *buf, guint32 bufsz, + GxfpCmdResp *presp, GError **error) +{ + g_return_val_if_fail (buf != NULL, FALSE); + g_return_val_if_fail (presp != NULL, FALSE); + + presp->result = buf[0]; + switch (cmd) { + case GX_CMD_ACK: + if (bufsz == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid bufsz"); + return FALSE; + } + presp->ack_msg.cmd = buf[1]; + break; + case GX_CMD_VERSION: + if (!fu_memcpy_safe ((guint8 *) &presp->version_info, + sizeof(presp->version_info), 0x0, /* dst */ + buf, bufsz, 0x01, /* src */ + sizeof(GxfpVersiomInfo), error)) + return FALSE; + break; + default: + break; + } + return TRUE; +} diff --git a/plugins/goodix-moc/fu-goodixmoc-common.h b/plugins/goodix-moc/fu-goodixmoc-common.h new file mode 100644 index 000000000..499330247 --- /dev/null +++ b/plugins/goodix-moc/fu-goodixmoc-common.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016-2018 Richard Hughes + * Copyright (C) 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* protocol */ +#define GX_CMD_ACK 0xAA +#define GX_CMD_VERSION 0xD0 +#define GX_CMD_RESET 0xB4 +#define GX_CMD_UPGRADE 0x80 +#define GX_CMD_UPGRADE_INIT 0x00 +#define GX_CMD_UPGRADE_DATA 0x01 +#define GX_CMD1_DEFAULT 0x00 + +#define GX_SIZE_CRC32 4 + +/* type covert */ +#define MAKE_CMD_EX(cmd0, cmd1) ((guint16)(((cmd0) << 8) | (cmd1))) + +typedef struct { + guint8 format[2]; + guint8 fwtype[8]; + guint8 fwversion[8]; + guint8 customer[8]; + guint8 mcu[8]; + guint8 sensor[8]; + guint8 algversion[8]; + guint8 interface[8]; + guint8 protocol[8]; + guint8 flashVersion[8]; + guint8 reserved[62]; +} GxfpVersiomInfo; + +typedef struct { + guint8 cmd; + gboolean configured; +} GxfpAckMsg; + +typedef struct { + guint8 result; + union { + GxfpAckMsg ack_msg; + GxfpVersiomInfo version_info; + }; +} GxfpCmdResp; + +typedef enum { + GX_PKG_TYPE_NORMAL = 0x80, + GX_PKG_TYPE_EOP = 0, +} GxPkgType; + +typedef struct __attribute__((__packed__)) { + guint8 cmd0; + guint8 cmd1; + guint8 pkg_flag; + guint8 reserved; + guint16 len; + guint8 crc8; + guint8 rev_crc8; +} GxfpPkgHeader; + +void fu_goodixmoc_build_header (GxfpPkgHeader *pheader, + guint16 len, + guint8 cmd0, + guint8 cmd1, + GxPkgType type); +gboolean fu_goodixmoc_parse_header (guint8 *buf, + guint32 bufsz, + GxfpPkgHeader *pheader, + GError **error); +gboolean fu_goodixmoc_parse_body (guint8 cmd, + guint8 *buf, + guint32 bufsz, + GxfpCmdResp *presp, + GError **error); diff --git a/plugins/goodix-moc/fu-goodixmoc-device.c b/plugins/goodix-moc/fu-goodixmoc-device.c new file mode 100644 index 000000000..c37826d09 --- /dev/null +++ b/plugins/goodix-moc/fu-goodixmoc-device.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2016-2017 Richard Hughes + * Copyright (C) 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-chunk.h" + +#include "fu-goodixmoc-common.h" +#include "fu-goodixmoc-device.h" + +struct _FuGoodixMocDevice { + FuUsbDevice parent_instance; +}; + +G_DEFINE_TYPE (FuGoodixMocDevice, fu_goodixmoc_device, FU_TYPE_USB_DEVICE) + +#define GX_USB_BULK_EP_IN (3 | 0x80) +#define GX_USB_BULK_EP_OUT (1 | 0x00) +#define GX_USB_INTERFACE 0 + +#define GX_USB_DATAIN_TIMEOUT 2000 /* ms */ +#define GX_USB_DATAOUT_TIMEOUT 200 /* ms */ +#define GX_FLASH_TRANSFER_BLOCK_SIZE 1000 /* 1000 */ + +static gboolean +goodixmoc_device_cmd_send (GUsbDevice *usbdevice, + guint8 cmd0, + guint8 cmd1, + GxPkgType type, + GByteArray *req, + GError **error) +{ + GxfpPkgHeader header = { 0 }; + guint32 crc_actual = 0; + gsize actual_len = 0; + g_autoptr(GByteArray) buf = g_byte_array_new (); + + fu_goodixmoc_build_header (&header, req->len, cmd0, cmd1, type); + g_byte_array_append (buf, (guint8 *)&header, sizeof(header)); + g_byte_array_append (buf, req->data, req->len); + crc_actual = fu_common_crc32 (buf->data, sizeof(header) + req->len); + fu_byte_array_append_uint32 (buf, crc_actual, G_LITTLE_ENDIAN); + + /* send zero length package */ + if (!g_usb_device_bulk_transfer (usbdevice, + GX_USB_BULK_EP_OUT, + NULL, + 0, + NULL, + GX_USB_DATAOUT_TIMEOUT, NULL, error)) { + g_prefix_error (error, "failed to req: "); + return FALSE; + } + if (g_getenv ("FWUPD_GOODIXFP_VERBOSE") != NULL) { + fu_common_dump_full (G_LOG_DOMAIN, "REQST", + buf->data, buf->len, 16, + FU_DUMP_FLAGS_SHOW_ADDRESSES); + } + + /* send data */ + if (!g_usb_device_bulk_transfer (usbdevice, + GX_USB_BULK_EP_OUT, + buf->data, + buf->len, + &actual_len, + GX_USB_DATAOUT_TIMEOUT, NULL, error)) { + g_prefix_error (error, "failed to req: "); + return FALSE; + } + if (actual_len != buf->len) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid length"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +goodixmoc_device_cmd_recv (GUsbDevice *usbdevice, + GxfpCmdResp *presponse, + gboolean data_reply, + GError **error) +{ + GxfpPkgHeader header = { 0 }; + guint32 crc_actual = 0; + gsize actual_len = 0; + gsize offset = 0; + + g_return_val_if_fail (presponse != NULL, FALSE); + + /* + * package format + * | zlp | ack | zlp | data | + */ + while (1) { + g_autoptr(GByteArray) reply = g_byte_array_new (); + fu_byte_array_set_size (reply, GX_FLASH_TRANSFER_BLOCK_SIZE); + if (!g_usb_device_bulk_transfer (usbdevice, + GX_USB_BULK_EP_IN, + reply->data, + reply->len, + &actual_len, /* allowed to return short read */ + GX_USB_DATAIN_TIMEOUT, + NULL, error)) { + g_prefix_error (error, "failed to reply: "); + return FALSE; + } + + /* receive zero length package */ + if (actual_len == 0) + continue; + if (g_getenv ("FWUPD_GOODIXFP_VERBOSE") != NULL) { + fu_common_dump_full (G_LOG_DOMAIN, "REPLY", + reply->data, actual_len, 16, + FU_DUMP_FLAGS_SHOW_ADDRESSES); + } + + /* parse package header */ + if (!fu_goodixmoc_parse_header (reply->data, + actual_len, + &header, + error)) + return FALSE; + offset = sizeof(header) + header.len; + crc_actual = fu_common_crc32 (reply->data, offset); + if (crc_actual != GUINT32_FROM_LE(*(guint32 *)(reply->data + offset))) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid checksum"); + return FALSE; + } + + /* parse package data */ + if (!fu_goodixmoc_parse_body (header.cmd0, + reply->data + sizeof(header), + header.len, + presponse, + error)) + return FALSE; + + /* continue after ack received */ + if (header.cmd0 == GX_CMD_ACK && data_reply) + continue; + break; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_goodixmoc_device_cmd_xfer (FuGoodixMocDevice *device, + guint8 cmd0, + guint8 cmd1, + GxPkgType type, + GByteArray *req, + GxfpCmdResp *presponse, + gboolean data_reply, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE(device)); + if (!goodixmoc_device_cmd_send (usb_device, cmd0, cmd1, type, req, error)) + return FALSE; + return goodixmoc_device_cmd_recv (usb_device, presponse, data_reply, error); +} + +static gchar * +fu_goodixmoc_device_get_version (FuGoodixMocDevice *self, GError **error) +{ + GxfpCmdResp rsp = { 0 }; + gchar ver[9] = { 0 }; + guint8 dummy = 0; + g_autoptr(GByteArray) req = g_byte_array_new (); + + fu_byte_array_append_uint8 (req, dummy); + if (!fu_goodixmoc_device_cmd_xfer (self, GX_CMD_VERSION, GX_CMD1_DEFAULT, + GX_PKG_TYPE_EOP, + req, + &rsp, + TRUE, + error)) + return NULL; + if (!fu_memcpy_safe ((guint8 *) ver, sizeof(ver), 0x0, + rsp.version_info.fwversion, + sizeof(rsp.version_info.fwversion), + 0x0, + sizeof(rsp.version_info.fwversion), + error)) + return NULL; + return g_strndup (ver, sizeof(ver)); +} + +static gboolean +fu_goodixmoc_device_update_init (FuGoodixMocDevice *self, GError **error) +{ + GxfpCmdResp rsp = { 0 }; + g_autoptr(GByteArray) req = g_byte_array_new (); + + /* update initial */ + if (!fu_goodixmoc_device_cmd_xfer (self, GX_CMD_UPGRADE, GX_CMD_UPGRADE_INIT, + GX_PKG_TYPE_EOP, + req, + &rsp, + TRUE, + error)) { + g_prefix_error (error, "failed to send initial update: "); + return FALSE; + } + + /* check result */ + if (rsp.result != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "initial update failed [0x%x]", + rsp.result); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_goodixmoc_device_attach (FuDevice *device, GError **error) +{ + FuGoodixMocDevice *self = FU_GOODIXMOC_DEVICE(device); + GxfpCmdResp rsp = { 0 }; + g_autoptr(GByteArray) req = g_byte_array_new (); + + /* reset device */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + if (!fu_goodixmoc_device_cmd_xfer (self, GX_CMD_RESET, 0x03, + GX_PKG_TYPE_EOP, + req, + &rsp, + FALSE, + error)) { + g_prefix_error (error, "failed to send reset device: "); + return FALSE; + } + + /* check result */ + if (rsp.result != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to reset device [0x%x]", + rsp.result); + return FALSE; + } + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_goodixmoc_device_open (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + return g_usb_device_claim_interface (usb_device, GX_USB_INTERFACE, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error); +} + +static gboolean +fu_goodixmoc_device_setup (FuDevice *device, GError **error) +{ + FuGoodixMocDevice *self = FU_GOODIXMOC_DEVICE(device); + g_autofree gchar *version = NULL; + + version = fu_goodixmoc_device_get_version (self, error); + if (version == NULL) { + g_prefix_error (error, "failed to get firmware version: "); + return FALSE; + } + fu_device_set_version (device, version); + + /* success */ + return TRUE; +} + +static gboolean +fu_goodixmoc_device_write_firmware (FuDevice *device, + FuFirmware *firmware, + FwupdInstallFlags flags, + GError **error) +{ + FuGoodixMocDevice *self = FU_GOODIXMOC_DEVICE(device); + GxPkgType pkg_eop = GX_PKG_TYPE_NORMAL; + GxfpCmdResp rsp = { 0 }; + gboolean wait_data_reply = FALSE; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + + /* build packets */ + chunks = fu_chunk_array_new_from_bytes (fw, + 0x00, + 0x00, /* page_sz */ + GX_FLASH_TRANSFER_BLOCK_SIZE); + + /* don't auto-boot firmware */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + if (!fu_goodixmoc_device_update_init (self, &error_local)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to initial update: %s", + error_local->message); + return FALSE; + } + + /* write each block */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + g_autoptr(GByteArray) req = g_byte_array_new (); + g_autoptr(GError) error_block = NULL; + + g_byte_array_append (req, chk->data, chk->data_sz); + + /* the last chunk */ + if (i == chunks->len - 1) { + wait_data_reply = TRUE; + pkg_eop = GX_PKG_TYPE_EOP; + } + if (!fu_goodixmoc_device_cmd_xfer (self, + GX_CMD_UPGRADE, + GX_CMD_UPGRADE_DATA, + pkg_eop, + req, + &rsp, + wait_data_reply, + &error_block)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to write: %s", + error_block->message); + return FALSE; + } + + /* check update status */ + if (wait_data_reply && rsp.result != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to verify firmware [0x%x]", + rsp.result); + return FALSE; + } + + /* update progress */ + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len); + } + + /* success! */ + return TRUE; +} + +static void +fu_goodixmoc_device_init (FuGoodixMocDevice *self) +{ + fu_device_add_flag (FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag (FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); + fu_device_add_flag (FU_DEVICE(self), FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION); + fu_device_set_version_format (FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_remove_delay (FU_DEVICE(self), 5000); + fu_device_set_protocol (FU_DEVICE (self), "com.goodix.goodixmoc"); + fu_device_set_name (FU_DEVICE(self), "Fingerprint Sensor"); + fu_device_set_summary (FU_DEVICE(self), "Match-On-Chip Fingerprint Sensor"); + fu_device_set_vendor (FU_DEVICE(self), "Goodix"); + fu_device_set_install_duration (FU_DEVICE(self), 10); + fu_device_set_firmware_size_min (FU_DEVICE(self), 0x20000); + fu_device_set_firmware_size_max (FU_DEVICE(self), 0x30000); +} + +static void +fu_goodixmoc_device_class_init(FuGoodixMocDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_goodixmoc_device_write_firmware; + klass_device->setup = fu_goodixmoc_device_setup; + klass_device->attach = fu_goodixmoc_device_attach; + klass_usb_device->open = fu_goodixmoc_device_open; +} diff --git a/plugins/goodix-moc/fu-goodixmoc-device.h b/plugins/goodix-moc/fu-goodixmoc-device.h new file mode 100644 index 000000000..0897b65a8 --- /dev/null +++ b/plugins/goodix-moc/fu-goodixmoc-device.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2016-2017 Richard Hughes + * Copyright (C) 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +#define FU_TYPE_GOODIXMOC_DEVICE (fu_goodixmoc_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuGoodixMocDevice, fu_goodixmoc_device, FU, GOODIXMOC_DEVICE, FuUsbDevice) diff --git a/plugins/goodix-moc/fu-plugin-goodixmoc.c b/plugins/goodix-moc/fu-plugin-goodixmoc.c new file mode 100644 index 000000000..61e799c4e --- /dev/null +++ b/plugins/goodix-moc/fu-plugin-goodixmoc.c @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2015-2017 Richard Hughes + * Copyright (C) 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" +#include "fu-goodixmoc-device.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_set_device_gtype (plugin, FU_TYPE_GOODIXMOC_DEVICE); +} diff --git a/plugins/goodix-moc/goodixmoc.quirk b/plugins/goodix-moc/goodixmoc.quirk new file mode 100644 index 000000000..fd802133d --- /dev/null +++ b/plugins/goodix-moc/goodixmoc.quirk @@ -0,0 +1,13 @@ +# Goodix Fingerprint sensor +[DeviceInstanceId=USB\VID_27C6&PID_60A2] +Plugin = goodixmoc +[DeviceInstanceId=USB\VID_27C6&PID_6384] +Plugin = goodixmoc +[DeviceInstanceId=USB\VID_27C6&PID_639C] +Plugin = goodixmoc +[DeviceInstanceId=USB\VID_27C6&PID_63AC] +Plugin = goodixmoc +[DeviceInstanceId=USB\VID_27C6&PID_6594] +Plugin = goodixmoc +[DeviceInstanceId=USB\VID_27C6&PID_6496] +Plugin = goodixmoc diff --git a/plugins/goodix-moc/meson.build b/plugins/goodix-moc/meson.build new file mode 100644 index 000000000..4e1287e47 --- /dev/null +++ b/plugins/goodix-moc/meson.build @@ -0,0 +1,31 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginGoodixMoc"'] + +install_data([ + 'goodixmoc.quirk', + ], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_goodixmoc', + fu_hash, + sources : [ + 'fu-goodixmoc-common.c', + 'fu-goodixmoc-device.c', + 'fu-plugin-goodixmoc.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/iommu/README.md b/plugins/iommu/README.md new file mode 100644 index 000000000..407d55b8d --- /dev/null +++ b/plugins/iommu/README.md @@ -0,0 +1,11 @@ +Linux IOMMU +================== + +Introduction +------------ + +This plugin checks if an IOMMU is available on the system. + +External interface access +------------------------- +This plugin requires no extra access. diff --git a/plugins/iommu/fu-plugin-iommu.c b/plugins/iommu/fu-plugin-iommu.c new file mode 100644 index 000000000..bfbaf4aa9 --- /dev/null +++ b/plugins/iommu/fu-plugin-iommu.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-hash.h" +#include "fu-plugin-vfuncs.h" + +struct FuPluginData { + gboolean has_iommu; +}; + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_udev_subsystem (plugin, "iommu"); +} + +gboolean +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + + /* interesting device? */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "iommu") != 0) + return TRUE; + priv->has_iommu = TRUE; + + return TRUE; +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_IOMMU); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fu_security_attrs_append (attrs, attr); + + if (!data->has_iommu) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} diff --git a/plugins/iommu/iommu.quirk b/plugins/iommu/iommu.quirk new file mode 100644 index 000000000..26245a6a3 --- /dev/null +++ b/plugins/iommu/iommu.quirk @@ -0,0 +1,3 @@ +# match all devices with this udev subsystem +[DeviceInstanceId=IOMMU] +Plugin = iommu diff --git a/plugins/iommu/meson.build b/plugins/iommu/meson.build new file mode 100644 index 000000000..f60289650 --- /dev/null +++ b/plugins/iommu/meson.build @@ -0,0 +1,29 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginIommu"'] + +install_data([ + 'iommu.quirk', + ], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_iommu', + fu_hash, + sources : [ + 'fu-plugin-iommu.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupdplugin, + fwupd, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/jabra/README.md b/plugins/jabra/README.md index 3a5ab7fa8..f0466349e 100644 --- a/plugins/jabra/README.md +++ b/plugins/jabra/README.md @@ -26,3 +26,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, in this instance set to `USB:0x0A12` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/jabra/fu-plugin-jabra.c b/plugins/jabra/fu-plugin-jabra.c index 7add28c7d..477806fc6 100644 --- a/plugins/jabra/fu-plugin-jabra.c +++ b/plugins/jabra/fu-plugin-jabra.c @@ -37,7 +37,7 @@ fu_plugin_update_cleanup (FuPlugin *plugin, locker = fu_device_locker_new (device, error); if (locker == NULL) return FALSE; - g_debug ("performing extra reset into firmware mode"); + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); if (!g_usb_device_reset (usb_device, &error_local)) { g_set_error (error, @@ -50,7 +50,6 @@ fu_plugin_update_cleanup (FuPlugin *plugin, } /* wait for device to re-appear */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); return TRUE; } diff --git a/plugins/linux-lockdown/README.md b/plugins/linux-lockdown/README.md new file mode 100644 index 000000000..8fef436f6 --- /dev/null +++ b/plugins/linux-lockdown/README.md @@ -0,0 +1,12 @@ +Linux Kernel Lockdown +===================== + +Introduction +------------ + +This plugin checks if the currently running kernel is locked down. The result +will be stored in an security attribute for HSI. + +External interface access +------------------------- +This plugin requires read access to `/sys/sys/kernel/security`. diff --git a/plugins/linux-lockdown/fu-plugin-linux-lockdown.c b/plugins/linux-lockdown/fu-plugin-linux-lockdown.c new file mode 100644 index 000000000..633feec2c --- /dev/null +++ b/plugins/linux-lockdown/fu-plugin-linux-lockdown.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" + +struct FuPluginData { + GFile *file; + GFileMonitor *monitor; +}; + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + +void +fu_plugin_destroy (FuPlugin *plugin) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + if (data->file != NULL) + g_object_unref (data->file); + if (data->monitor != NULL) { + g_file_monitor_cancel (data->monitor); + g_object_unref (data->monitor); + } +} + +static void +fu_plugin_linux_lockdown_changed_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN (user_data); + fu_plugin_security_changed (plugin); +} + +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + g_autofree gchar *path = NULL; + g_autofree gchar *fn = NULL; + + path = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY); + fn = g_build_filename (path, "lockdown", NULL); + data->file = g_file_new_for_path (fn); + data->monitor = g_file_monitor (data->file, G_FILE_MONITOR_NONE, NULL, error); + if (data->monitor == NULL) + return FALSE; + g_signal_connect (data->monitor, "changed", + G_CALLBACK (fu_plugin_linux_lockdown_changed_cb), plugin); + return TRUE; +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fu_security_attrs_append (attrs, attr); + + /* load file */ + if (!g_file_load_contents (data->file, NULL, &buf, &bufsz, NULL, &error_local)) { + g_autofree gchar *fn = g_file_get_path (data->file); + g_warning ("could not open %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (g_strstr_len (buf, bufsz, "[integrity]") == NULL && + g_strstr_len (buf, bufsz, "[confidentiality]") == NULL) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} diff --git a/plugins/linux-lockdown/meson.build b/plugins/linux-lockdown/meson.build new file mode 100644 index 000000000..82857848f --- /dev/null +++ b/plugins/linux-lockdown/meson.build @@ -0,0 +1,23 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxLockdown"'] + +shared_module('fu_plugin_linux_lockdown', + fu_hash, + sources : [ + 'fu-plugin-linux-lockdown.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/linux-sleep/README.md b/plugins/linux-sleep/README.md new file mode 100644 index 000000000..e87d422fa --- /dev/null +++ b/plugins/linux-sleep/README.md @@ -0,0 +1,12 @@ +Linux Kernel Sleep +================== + +Introduction +------------ + +This plugin checks if s3 sleep is available. The result will be stored in an +security attribute for HSI. + +External interface access +------------------------- +This plugin requires read access to `/sys/power/mem_sleep`. diff --git a/plugins/linux-sleep/fu-plugin-linux-sleep.c b/plugins/linux-sleep/fu-plugin-linux-sleep.c new file mode 100644 index 000000000..09cbd485b --- /dev/null +++ b/plugins/linux-sleep/fu-plugin-linux-sleep.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GFile) file = g_file_new_for_path ("/sys/power/mem_sleep"); + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL); + fu_security_attrs_append (attrs, attr); + + /* load file */ + if (!g_file_load_contents (file, NULL, &buf, &bufsz, NULL, &error_local)) { + g_autofree gchar *fn = g_file_get_path (file); + g_warning ("could not open %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (g_strstr_len (buf, bufsz, "[deep]") != NULL) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); +} diff --git a/plugins/linux-sleep/meson.build b/plugins/linux-sleep/meson.build new file mode 100644 index 000000000..a1a288d3f --- /dev/null +++ b/plugins/linux-sleep/meson.build @@ -0,0 +1,23 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxSleep"'] + +shared_module('fu_plugin_linux_sleep', + fu_hash, + sources : [ + 'fu-plugin-linux-sleep.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/linux-swap/README.md b/plugins/linux-swap/README.md new file mode 100644 index 000000000..f3b3f770d --- /dev/null +++ b/plugins/linux-swap/README.md @@ -0,0 +1,12 @@ +Linux Swap Support +================== + +Introduction +------------ + +This plugin checks if the currently available swap partitions and files are +all encrypted. The result will be stored in an security attribute for HSI. + +External interface access +------------------------- +This plugin requires read access to `/proc` diff --git a/plugins/linux-swap/fu-linux-swap.c b/plugins/linux-swap/fu-linux-swap.c new file mode 100644 index 000000000..12e724dc0 --- /dev/null +++ b/plugins/linux-swap/fu-linux-swap.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-common.h" +#include "fu-linux-swap.h" + +struct _FuLinuxSwap { + GObject parent_instance; + guint encrypted_cnt; + guint enabled_cnt; +}; + +G_DEFINE_TYPE (FuLinuxSwap, fu_linux_swap, G_TYPE_OBJECT) + +static gchar * +fu_strdup_nospaces (const gchar *line) +{ + GString *str = g_string_new (NULL); + for (guint i = 0; line[i] != '\0' && !g_ascii_isspace (line[i]); i++) + g_string_append_c (str, line[i]); + return g_string_free (str, FALSE); +} + +static gboolean +fu_linux_swap_verify_partition (FuLinuxSwap *self, const gchar *fn, GError **error) +{ + g_autoptr(FuVolume) volume = NULL; + + /* find the device */ + volume = fu_common_get_volume_by_device (fn, error); + if (volume == NULL) + return FALSE; + + /* this isn't technically encrypted, but isn't on disk in plaintext */ + if (g_str_has_prefix (fn, "/dev/zram")) { + g_debug ("%s is zram, assuming encrypted", fn); + self->encrypted_cnt++; + return TRUE; + } + + /* is this mount point encrypted */ + if (fu_volume_is_encrypted (volume)) { + g_debug ("%s partition is encrypted", fn); + self->encrypted_cnt++; + } else { + g_debug ("%s partition is unencrypted", fn); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_linux_swap_verify_file (FuLinuxSwap *self, const gchar *fn, GError **error) +{ + guint32 devnum; + g_autoptr(GFile) file = NULL; + g_autoptr(GFileInfo) info = NULL; + g_autoptr(FuVolume) volume = NULL; + + /* get the device number for the file */ + file = g_file_new_for_path (fn); + info = g_file_query_info (file, G_FILE_ATTRIBUTE_UNIX_DEVICE, + G_FILE_QUERY_INFO_NONE, NULL, error); + if (info == NULL) + return FALSE; + devnum = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_DEVICE); + + /* find the device */ + volume = fu_common_get_volume_by_devnum (devnum, error); + if (volume == NULL) + return FALSE; + + /* is this mount point encrypted */ + if (fu_volume_is_encrypted (volume)) { + g_debug ("%s file is encrypted", fn); + self->encrypted_cnt++; + } else { + g_debug ("%s file is unencrypted", fn); + } + + /* success */ + return TRUE; +} + +FuLinuxSwap * +fu_linux_swap_new (const gchar *buf, gsize bufsz, GError **error) +{ + FuLinuxSwap *self = g_object_new (FU_TYPE_LINUX_SWAP, NULL); + g_auto(GStrv) lines = NULL; + + /* look at each line in /proc/swaps */ + if (bufsz == 0) + bufsz = strlen (buf); + lines = fu_common_strnsplit (buf, bufsz, "\n", -1); + if (g_strv_length (lines) > 2) { + for (guint i = 1; lines[i] != NULL && lines[i][0] != '\0'; i++) { + g_autofree gchar *fn = NULL; + g_autofree gchar *ty = NULL; + + /* split */ + if (g_utf8_strlen (lines[i], -1) < 45) + continue; + fn = fu_strdup_nospaces (lines[i]); + ty = fu_strdup_nospaces (lines[i] + 40); + + /* partition, so use UDisks to see if backed by crypto */ + if (g_strcmp0 (ty, "partition") == 0) { + self->enabled_cnt++; + if (!fu_linux_swap_verify_partition (self, fn, error)) + return NULL; + } else if (g_strcmp0 (ty, "file") == 0) { + self->enabled_cnt++; + if (!fu_linux_swap_verify_file (self, fn, error)) + return NULL; + } else { + g_warning ("unknown swap type: %s [%s]", ty, fn); + } + } + } + return self; +} + +/* success if *all* the swap devices are encrypted */ +gboolean +fu_linux_swap_get_encrypted (FuLinuxSwap *self) +{ + g_return_val_if_fail (FU_IS_LINUX_SWAP (self), FALSE); + return self->enabled_cnt > 0 && self->enabled_cnt == self->encrypted_cnt; +} + +gboolean +fu_linux_swap_get_enabled (FuLinuxSwap *self) +{ + g_return_val_if_fail (FU_IS_LINUX_SWAP (self), FALSE); + return self->enabled_cnt > 0; +} + +static void +fu_linux_swap_class_init (FuLinuxSwapClass *klass) +{ +} + +static void +fu_linux_swap_init (FuLinuxSwap *self) +{ +} diff --git a/plugins/linux-swap/fu-linux-swap.h b/plugins/linux-swap/fu-linux-swap.h new file mode 100644 index 000000000..5d66e24fc --- /dev/null +++ b/plugins/linux-swap/fu-linux-swap.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_LINUX_SWAP (fu_linux_swap_get_type ()) +G_DECLARE_FINAL_TYPE (FuLinuxSwap, fu_linux_swap, FU, LINUX_SWAP, GObject) + +FuLinuxSwap *fu_linux_swap_new (const gchar *buf, + gsize bufsz, + GError **error); +gboolean fu_linux_swap_get_enabled (FuLinuxSwap *self); +gboolean fu_linux_swap_get_encrypted (FuLinuxSwap *self); diff --git a/plugins/linux-swap/fu-plugin-linux-swap.c b/plugins/linux-swap/fu-plugin-linux-swap.c new file mode 100644 index 000000000..b0f11edca --- /dev/null +++ b/plugins/linux-swap/fu-plugin-linux-swap.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" +#include "fu-linux-swap.h" + +struct FuPluginData { + GFile *file; + GFileMonitor *monitor; +}; + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + +void +fu_plugin_destroy (FuPlugin *plugin) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + if (data->file != NULL) + g_object_unref (data->file); + if (data->monitor != NULL) { + g_file_monitor_cancel (data->monitor); + g_object_unref (data->monitor); + } +} + +static void +fu_plugin_linux_swap_changed_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN (user_data); + fu_plugin_security_changed (plugin); +} + +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + g_autofree gchar *fn = NULL; + g_autofree gchar *procfs = NULL; + + procfs = fu_common_get_path (FU_PATH_KIND_PROCFS); + fn = g_build_filename (procfs, "swaps", NULL); + data->file = g_file_new_for_path (fn); + data->monitor = g_file_monitor (data->file, G_FILE_MONITOR_NONE, NULL, error); + if (data->monitor == NULL) + return FALSE; + g_signal_connect (data->monitor, "changed", + G_CALLBACK (fu_plugin_linux_swap_changed_cb), plugin); + return TRUE; +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autoptr(FuLinuxSwap) swap = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fu_security_attrs_append (attrs, attr); + + /* load list of swaps */ + if (!g_file_load_contents (data->file, NULL, &buf, &bufsz, NULL, &error_local)) { + g_autofree gchar *fn = g_file_get_path (data->file); + g_warning ("could not open %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + swap = fu_linux_swap_new (buf, bufsz, &error_local); + if (swap == NULL) { + g_autofree gchar *fn = g_file_get_path (data->file); + g_warning ("could not parse %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* none configured */ + if (!fu_linux_swap_get_enabled (swap)) { + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* add security attribute */ + if (!fu_linux_swap_get_encrypted (swap)) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED); +} diff --git a/plugins/linux-swap/fu-self-test.c b/plugins/linux-swap/fu-self-test.c new file mode 100644 index 000000000..3d02805d8 --- /dev/null +++ b/plugins/linux-swap/fu-self-test.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-linux-swap.h" + +#include "fwupd-error.h" + +static void +fu_linux_swap_none_func (void) +{ + g_autoptr(FuLinuxSwap) swap = NULL; + g_autoptr(GError) error = NULL; + + swap = fu_linux_swap_new ("Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n", 0, &error); + g_assert_no_error (error); + g_assert_nonnull (swap); + g_assert_false (fu_linux_swap_get_enabled (swap)); + g_assert_false (fu_linux_swap_get_encrypted (swap)); +} + +static void +fu_linux_swap_plain_func (void) +{ + g_autoptr(FuLinuxSwap) swap = NULL; + g_autoptr(GError) error = NULL; + + swap = fu_linux_swap_new ("Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n" + "/dev/nvme0n1p4 partition\t5962748\t0\t-2\n", + 0, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + g_test_skip (error->message); + return; + } + if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { + g_test_skip (error->message); + return; + } + g_assert_no_error (error); + g_assert_nonnull (swap); + g_assert_true (fu_linux_swap_get_enabled (swap)); + g_assert_false (fu_linux_swap_get_encrypted (swap)); +} + +static void +fu_linux_swap_encrypted_func (void) +{ + g_autoptr(FuLinuxSwap) swap = NULL; + g_autoptr(GError) error = NULL; + + swap = fu_linux_swap_new ("Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n" + "/dev/dm-1 partition\t5962748\t0\t-2\n", + 0, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) || + g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { + g_test_skip (error->message); + return; + } + g_assert_no_error (error); + g_assert_nonnull (swap); + g_assert_true (fu_linux_swap_get_enabled (swap)); + g_assert_true (fu_linux_swap_get_encrypted (swap)); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func ("/linux-swap/none", fu_linux_swap_none_func); + g_test_add_func ("/linux-swap/plain", fu_linux_swap_plain_func); + g_test_add_func ("/linux-swap/encrypted", fu_linux_swap_encrypted_func); + return g_test_run (); +} diff --git a/plugins/linux-swap/meson.build b/plugins/linux-swap/meson.build new file mode 100644 index 000000000..099ff53d0 --- /dev/null +++ b/plugins/linux-swap/meson.build @@ -0,0 +1,50 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxSwap"'] + +shared_module('fu_plugin_linux_swap', + fu_hash, + sources : [ + 'fu-plugin-linux-swap.c', + 'fu-linux-swap.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) + +if get_option('tests') + e = executable( + 'linux-swap-self-test', + fu_hash, + sources : [ + 'fu-self-test.c', + 'fu-linux-swap.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + dependencies : [ + plugin_deps, + ], + link_with : [ + fwupd, + fwupdplugin, + ], + install : true, + install_dir : installed_test_bindir, + ) + test('linux-swap-self-test', e) # added to installed-tests +endif diff --git a/plugins/linux-tainted/README.md b/plugins/linux-tainted/README.md new file mode 100644 index 000000000..e240061df --- /dev/null +++ b/plugins/linux-tainted/README.md @@ -0,0 +1,12 @@ +Linux Kernel Tainted +==================== + +Introduction +------------ + +This plugin checks if the currently running kernel is tainted. The result will +be stored in an security attribute for HSI. + +External interface access +------------------------- +This plugin requires read access to `/sys/kernel/tainted`. diff --git a/plugins/linux-tainted/fu-plugin-linux-tainted.c b/plugins/linux-tainted/fu-plugin-linux-tainted.c new file mode 100644 index 000000000..6100edef4 --- /dev/null +++ b/plugins/linux-tainted/fu-plugin-linux-tainted.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" + +struct FuPluginData { + GFile *file; + GFileMonitor *monitor; +}; + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + +void +fu_plugin_destroy (FuPlugin *plugin) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + if (data->file != NULL) + g_object_unref (data->file); + if (data->monitor != NULL) { + g_file_monitor_cancel (data->monitor); + g_object_unref (data->monitor); + } +} + +static void +fu_plugin_linux_tainted_changed_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN (user_data); + fu_plugin_security_changed (plugin); +} + +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + g_autofree gchar *fn = NULL; + g_autofree gchar *procfs = NULL; + + procfs = fu_common_get_path (FU_PATH_KIND_PROCFS); + fn = g_build_filename (procfs, "sys", "kernel", "tainted", NULL); + data->file = g_file_new_for_path (fn); + data->monitor = g_file_monitor (data->file, G_FILE_MONITOR_NONE, NULL, error); + if (data->monitor == NULL) + return FALSE; + g_signal_connect (data->monitor, "changed", + G_CALLBACK (fu_plugin_linux_tainted_changed_cb), plugin); + return TRUE; +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fu_security_attrs_append (attrs, attr); + + /* load file */ + if (!g_file_load_contents (data->file, NULL, &buf, &bufsz, NULL, &error_local)) { + g_autofree gchar *fn = g_file_get_path (data->file); + g_warning ("could not open %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (g_strcmp0 (buf, "0\n") != 0) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_TAINTED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED); +} diff --git a/plugins/linux-tainted/meson.build b/plugins/linux-tainted/meson.build new file mode 100644 index 000000000..41d12a073 --- /dev/null +++ b/plugins/linux-tainted/meson.build @@ -0,0 +1,23 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxTainted"'] + +shared_module('fu_plugin_linux_tainted', + fu_hash, + sources : [ + 'fu-plugin-linux-tainted.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/logind/README.md b/plugins/logind/README.md index bd66d8bba..d7d5456ae 100644 --- a/plugins/logind/README.md +++ b/plugins/logind/README.md @@ -11,3 +11,7 @@ Vendor ID Security ------------------ This protocol does not create a device and thus requires no vendor ID set. + +External interface access +------------------------- +This plugin requires access to the dbus interface `org.freedesktop.login1`. diff --git a/plugins/logitech-hidpp/README.md b/plugins/logitech-hidpp/README.md index 89f918323..3a00e6ae0 100644 --- a/plugins/logitech-hidpp/README.md +++ b/plugins/logitech-hidpp/README.md @@ -58,3 +58,7 @@ paired devices. [1] https://www.mousejack.com/ [2] https://pwr-Solaar.github.io/Solaar/ + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/meson.build b/plugins/meson.build index 5209e2fb1..132bc34ad 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -1,4 +1,9 @@ +subdir('acpi-dmar') +subdir('acpi-facp') +subdir('bcm57xx') +subdir('bios') subdir('ccgx') +subdir('cros-ec') subdir('cpu') subdir('dfu') subdir('colorhug') @@ -6,10 +11,18 @@ subdir('ebitdo') subdir('ep963x') subdir('fastboot') subdir('fresco-pd') +subdir('iommu') subdir('jabra') +subdir('linux-lockdown') +subdir('linux-sleep') +subdir('linux-swap') +subdir('linux-tainted') subdir('steelseries') subdir('dell-dock') subdir('nitrokey') +subdir('pci-bcr') +subdir('pci-mei') +subdir('platform-integrity') subdir('rts54hid') subdir('rts54hub') subdir('solokey') @@ -19,9 +32,15 @@ subdir('test') subdir('upower') subdir('wacom-usb') subdir('vli') +subdir('goodix-moc') + +if get_option('plugin_msr') +subdir('msr') +endif if get_option('gudev') subdir('ata') +subdir('elantp') subdir('logitech-hidpp') subdir('optionrom') subdir('superio') @@ -37,16 +56,25 @@ endif # depends on dfu subdir('csr') -if get_option('plugin_tpm') and get_option('gudev') +if get_option('tpm') +if not get_option('gudev') + error('gudev is required for tpm') +endif subdir('tpm') subdir('tpm-eventlog') endif -if get_option('plugin_emmc') and get_option('gudev') +if get_option('plugin_emmc') +if not get_option('gudev') + error('gudev is required for plugin_emmc') +endif subdir('emmc') endif -if get_option('plugin_nvme') and get_option('gudev') +if get_option('plugin_nvme') +if not get_option('gudev') + error('gudev is required for plugin_nvme') +endif subdir('nvme') endif @@ -54,7 +82,10 @@ if get_option('plugin_modem_manager') subdir('modem-manager') endif -if get_option('plugin_altos') and get_option('gudev') +if get_option('plugin_altos') +if not get_option('gudev') + error('gudev is required for plugin_altos') +endif subdir('altos') endif @@ -62,7 +93,10 @@ if get_option('plugin_amt') subdir('amt') endif -if get_option('plugin_thunderbolt') and get_option('gudev') +if get_option('plugin_thunderbolt') +if not get_option('gudev') + error('gudev is required for plugin_thunderbolt') +endif subdir('thunderbolt') endif @@ -75,7 +109,10 @@ subdir('dell') subdir('dell-esrt') endif -if get_option('plugin_synaptics') and get_option('gudev') +if get_option('plugin_synaptics') +if not get_option('gudev') + error('gudev is required for plugin_synaptics_mst') +endif subdir('synaptics-mst') endif diff --git a/plugins/modem-manager/README.md b/plugins/modem-manager/README.md index a2ab7645e..b97eeafe9 100644 --- a/plugins/modem-manager/README.md +++ b/plugins/modem-manager/README.md @@ -48,3 +48,7 @@ partition where the MCFG files are stored can be wiped out before installing the new ones. Update protocol: com.qualcomm.qmi_pdc + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/msr/README.md b/plugins/msr/README.md new file mode 100644 index 000000000..0fbfe725e --- /dev/null +++ b/plugins/msr/README.md @@ -0,0 +1,18 @@ +MSR +=== + +Introduction +------------ + +This plugin checks if the Model-specific registers (MSRs) indicate the +Direct Connect Interface (DCI) is enabled. + +DCI allows debugging of Intel processors using the USB3 port. DCI should +always be disabled and locked on production hardware as it allows the +attacker to disable other firmware protection methods. + +The result will be stored in a security attribute for HSI. + +External interface access +------------------------- +This plugin requires read access to `/sys/class/msr`. diff --git a/plugins/msr/fu-plugin-msr.c b/plugins/msr/fu-plugin-msr.c new file mode 100644 index 000000000..cf529321a --- /dev/null +++ b/plugins/msr/fu-plugin-msr.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" + +typedef union { + guint32 data; + struct { + guint32 enabled : 1; + guint32 rsrvd : 29; + guint32 locked : 1; + guint32 debug_occurred : 1; + } __attribute__((packed)) fields; +} FuMsrIa32Debug; + +struct FuPluginData { + gboolean ia32_debug_supported; + FuMsrIa32Debug ia32_debug; +}; + +#define PCI_MSR_IA32_DEBUG_INTERFACE 0xc80 +#define PCI_MSR_IA32_BIOS_SIGN_ID 0x8b + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_udev_subsystem (plugin, "msr"); +} + +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + guint ecx = 0; + + /* sdbg is supported: https://en.wikipedia.org/wiki/CPUID */ + if (!fu_common_cpuid (0x01, NULL, NULL, &ecx, NULL, error)) + return FALSE; + priv->ia32_debug_supported = ((ecx >> 11) & 0x1) > 0; + return TRUE; +} + +gboolean +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) +{ + FuDevice *device_cpu = fu_plugin_cache_lookup (plugin, "cpu"); + FuPluginData *priv = fu_plugin_get_data (plugin); + guint8 buf[8] = { 0x0 }; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autofree gchar *basename = NULL; + + /* interesting device? */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "msr") != 0) + return TRUE; + + /* we only care about the first processor */ + basename = g_path_get_basename (fu_udev_device_get_sysfs_path (device)); + if (g_strcmp0 (basename, "msr0") != 0) + return TRUE; + + /* open the config */ + fu_device_set_physical_id (FU_DEVICE (device), "msr"); + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + + /* grab MSR */ + if (priv->ia32_debug_supported) { + if (!fu_udev_device_pread_full (device, PCI_MSR_IA32_DEBUG_INTERFACE, + buf, sizeof(buf), error)) { + g_prefix_error (error, "could not read IA32_DEBUG_INTERFACE: "); + return FALSE; + } + if (!fu_common_read_uint32_safe (buf, sizeof(buf), 0x0, + &priv->ia32_debug.data, G_LITTLE_ENDIAN, + error)) + return FALSE; + g_debug ("IA32_DEBUG_INTERFACE: enabled=%i, locked=%i, debug_occurred=%i", + priv->ia32_debug.fields.enabled, + priv->ia32_debug.fields.locked, + priv->ia32_debug.fields.debug_occurred); + } + + /* get microcode version */ + if (device_cpu != NULL) { + guint32 ver_raw; + if (!fu_udev_device_pread_full (device, PCI_MSR_IA32_BIOS_SIGN_ID, + buf, sizeof(buf), error)) { + g_prefix_error (error, "could not read IA32_BIOS_SIGN_ID: "); + return FALSE; + } + fu_common_dump_raw (G_LOG_DOMAIN, "IA32_BIOS_SIGN_ID", buf, sizeof(buf)); + if (!fu_common_read_uint32_safe (buf, sizeof(buf), 0x4, + &ver_raw, G_LITTLE_ENDIAN, + error)) + return FALSE; + if (ver_raw != 0) { + FwupdVersionFormat verfmt = fu_device_get_version_format (device_cpu); + g_autofree gchar *ver_str = NULL; + ver_str = fu_common_version_from_uint32 (ver_raw, verfmt); + g_debug ("setting microcode version to %s", ver_str); + fu_device_set_version (device_cpu, ver_str); + fu_device_set_version_raw (device_cpu, ver_raw); + } + } + + /* success */ + return TRUE; +} + +void +fu_plugin_device_registered (FuPlugin *plugin, FuDevice *dev) +{ + if (g_strcmp0 (fu_device_get_plugin (dev), "cpu") == 0) { + fu_plugin_cache_add (plugin, "cpu", dev); + return; + } +} + +static void +fu_plugin_add_security_attr_dci_enabled (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* this MSR is only valid for a subset of Intel CPUs */ + if (!fu_common_is_cpu_intel ()) + return; + if (!priv->ia32_debug_supported) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_INTEL_DCI_ENABLED); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fu_security_attrs_append (attrs, attr); + + /* check fields */ + if (priv->ia32_debug.fields.enabled) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); +} + +static void +fu_plugin_add_security_attr_dci_locked (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* this MSR is only valid for a subset of Intel CPUs */ + if (!fu_common_is_cpu_intel ()) + return; + if (!priv->ia32_debug_supported) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_INTEL_DCI_LOCKED); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + fu_security_attrs_append (attrs, attr); + + /* check fields */ + if (!priv->ia32_debug.fields.locked) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + fu_plugin_add_security_attr_dci_enabled (plugin, attrs); + fu_plugin_add_security_attr_dci_locked (plugin, attrs); +} diff --git a/plugins/msr/fwupd-msr.conf b/plugins/msr/fwupd-msr.conf new file mode 100644 index 000000000..3e5ee7fa1 --- /dev/null +++ b/plugins/msr/fwupd-msr.conf @@ -0,0 +1 @@ +msr diff --git a/plugins/msr/meson.build b/plugins/msr/meson.build new file mode 100644 index 000000000..d829e1530 --- /dev/null +++ b/plugins/msr/meson.build @@ -0,0 +1,31 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginMsr"'] + +install_data(['msr.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +install_data(['fwupd-msr.conf'], + install_dir: join_paths(sysconfdir, 'modules-load.d') +) + +shared_module('fu_plugin_msr', + fu_hash, + sources : [ + 'fu-plugin-msr.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/msr/msr.quirk b/plugins/msr/msr.quirk new file mode 100644 index 000000000..893a02c43 --- /dev/null +++ b/plugins/msr/msr.quirk @@ -0,0 +1,3 @@ +# match all devices with this udev subsystem +[DeviceInstanceId=MSR] +Plugin = msr diff --git a/plugins/nitrokey/README.md b/plugins/nitrokey/README.md index 852e88a43..e50b472e3 100644 --- a/plugins/nitrokey/README.md +++ b/plugins/nitrokey/README.md @@ -25,3 +25,7 @@ Vendor ID Security The vendor ID is set from the USB vendor, in this instance set to `USB:0x20A0` in runtime mode and `USB:0x03EB` in bootloader mode. + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/nitrokey/meson.build b/plugins/nitrokey/meson.build index 6b9991fa8..1152cdd65 100644 --- a/plugins/nitrokey/meson.build +++ b/plugins/nitrokey/meson.build @@ -47,6 +47,8 @@ if get_option('tests') link_with : [ fwupdplugin, ], + install : true, + install_dir : installed_test_bindir, ) - test('nitrokey-self-test', e) + test('nitrokey-self-test', e) # added to installed-tests endif diff --git a/plugins/nvme/README.md b/plugins/nvme/README.md index da5c86253..3005948e9 100644 --- a/plugins/nvme/README.md +++ b/plugins/nvme/README.md @@ -54,3 +54,7 @@ Vendor ID Security ------------------ The vendor ID is set from the udev vendor, for example set to `NVME:0x1179` + +External interface access +------------------------- +This plugin requires ioctl `NVME_IOCTL_ADMIN_CMD` access. diff --git a/plugins/nvme/fu-nvme-device.c b/plugins/nvme/fu-nvme-device.c index 4e92a217b..3eacdcd13 100644 --- a/plugins/nvme/fu-nvme-device.c +++ b/plugins/nvme/fu-nvme-device.c @@ -390,6 +390,7 @@ fu_nvme_device_init (FuNvmeDevice *self) { fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_REQUIRE_AC); fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NO_GUID_MATCHING); fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_PLAIN); fu_device_set_summary (FU_DEVICE (self), "NVM Express Solid State Drive"); fu_device_add_icon (FU_DEVICE (self), "drive-harddisk"); diff --git a/plugins/nvme/fu-self-test.c b/plugins/nvme/fu-self-test.c index 9b872bb0b..b1b9ae560 100644 --- a/plugins/nvme/fu-self-test.c +++ b/plugins/nvme/fu-self-test.c @@ -16,12 +16,18 @@ fu_nvme_cns_func (void) { gboolean ret; gsize sz; + const gchar *ci = g_getenv ("CI_NETWORK"); g_autofree gchar *data = NULL; g_autofree gchar *path = NULL; g_autoptr(FuNvmeDevice) dev = NULL; g_autoptr(GError) error = NULL; - path = g_build_filename (TESTDATADIR, "TOSHIBA_THNSN5512GPU7.bin", NULL); + path = g_test_build_filename (G_TEST_DIST, "tests", "TOSHIBA_THNSN5512GPU7.bin", NULL); + + if (!g_file_test (path, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing TOSHIBA_THNSN5512GPU7.bin"); + return; + } ret = g_file_get_contents (path, &data, &sz, &error); g_assert_no_error (error); g_assert (ret); @@ -43,7 +49,7 @@ fu_nvme_cns_all_func (void) g_autoptr(GDir) dir = NULL; /* may or may not exist */ - path = g_build_filename (TESTDATADIR, "blobs", NULL); + path = g_test_build_filename (G_TEST_DIST, "tests", "blobs", NULL); if (!g_file_test (path, G_FILE_TEST_EXISTS)) return; dir = g_dir_open (path, 0, NULL); diff --git a/plugins/nvme/meson.build b/plugins/nvme/meson.build index d0db9f434..a2f005ac6 100644 --- a/plugins/nvme/meson.build +++ b/plugins/nvme/meson.build @@ -34,8 +34,9 @@ shared_module('fu_plugin_nvme', ) if get_option('tests') - testdatadir = join_paths(meson.current_source_dir(), 'tests') - cargs += '-DTESTDATADIR="' + testdatadir + '"' + testdatadirs = environment() + testdatadirs.set('G_TEST_SRCDIR', meson.current_source_dir()) + testdatadirs.set('G_TEST_BUILDDIR', meson.current_build_dir()) e = executable( 'nvme-self-test', fu_hash, @@ -56,7 +57,8 @@ if get_option('tests') fwupd, fwupdplugin, ], - c_args : cargs + install : true, + install_dir : installed_test_bindir, ) - test('nvme-self-test', e) + test('nvme-self-test', e, env : testdatadirs) # added to installed-tests endif diff --git a/plugins/nvme/tests/.gitignore b/plugins/nvme/tests/.gitignore deleted file mode 100644 index 76234851f..000000000 --- a/plugins/nvme/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -blobs diff --git a/plugins/nvme/tests/TOSHIBA_THNSN5512GPU7.bin b/plugins/nvme/tests/TOSHIBA_THNSN5512GPU7.bin deleted file mode 100644 index 8048a0df8..000000000 Binary files a/plugins/nvme/tests/TOSHIBA_THNSN5512GPU7.bin and /dev/null differ diff --git a/plugins/optionrom/README.md b/plugins/optionrom/README.md index f07405c2b..d028e3ca3 100644 --- a/plugins/optionrom/README.md +++ b/plugins/optionrom/README.md @@ -24,3 +24,7 @@ Vendor ID Security ------------------ The device is not upgradable and thus requires no vendor ID set. + +External interface access +------------------------- +This plugin requires read access to the rom file of PCI devices (`/sys/class/pci_bus/*/device/rom`) diff --git a/plugins/optionrom/fu-optionrom-device.c b/plugins/optionrom/fu-optionrom-device.c index 248d7a546..8f84b8300 100644 --- a/plugins/optionrom/fu-optionrom-device.c +++ b/plugins/optionrom/fu-optionrom-device.c @@ -18,15 +18,15 @@ G_DEFINE_TYPE (FuOptionromDevice, fu_optionrom_device, FU_TYPE_UDEV_DEVICE) static gboolean fu_optionrom_device_probe (FuUdevDevice *device, GError **error) { - GUdevDevice *udev_device = fu_udev_device_get_dev (device); - const gchar *guid = NULL; + g_autofree gchar *fn = NULL; - guid = g_udev_device_get_property (udev_device, "FWUPD_GUID"); - if (guid == NULL) { + /* does the device even have ROM? */ + fn = g_build_filename (fu_udev_device_get_sysfs_path (device), "rom", NULL); + if (!g_file_test (fn, G_FILE_TEST_EXISTS)) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "no FWUPD_GUID property"); + "Unable to read firmware from device"); return FALSE; } @@ -37,15 +37,12 @@ fu_optionrom_device_probe (FuUdevDevice *device, GError **error) return TRUE; } -static FuFirmware * -fu_optionrom_device_read_firmware (FuDevice *device, GError **error) +static GBytes * +fu_optionrom_device_dump_firmware (FuDevice *device, GError **error) { FuUdevDevice *udev_device = FU_UDEV_DEVICE (device); - g_autofree gchar *guid = NULL; - g_autofree gchar *rom_fn = NULL; - g_autoptr(FuRom) rom = NULL; - g_autoptr(GBytes) fw = NULL; g_autoptr(GFile) file = NULL; + g_autofree gchar *rom_fn = NULL; /* open the file */ rom_fn = g_build_filename (fu_udev_device_get_sysfs_path (udev_device), "rom", NULL); @@ -57,8 +54,23 @@ fu_optionrom_device_read_firmware (FuDevice *device, GError **error) return NULL; } file = g_file_new_for_path (rom_fn); + return fu_rom_dump_firmware (file, NULL, error); +} + +static FuFirmware * +fu_optionrom_device_read_firmware (FuDevice *device, GError **error) +{ + g_autofree gchar *guid = NULL; + g_autoptr(FuRom) rom = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) fw = NULL; + + /* open the file */ + blob = fu_optionrom_device_dump_firmware (device, error); + if (blob == NULL) + return NULL; rom = fu_rom_new (); - if (!fu_rom_load_file (rom, file, FU_ROM_LOAD_FLAG_BLANK_PPID, NULL, error)) + if (!fu_rom_load_data (rom, blob, FU_ROM_LOAD_FLAG_BLANK_PPID, NULL, error)) return NULL; /* update version */ @@ -91,6 +103,7 @@ fu_optionrom_device_init (FuOptionromDevice *self) fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_icon (FU_DEVICE (self), "audio-card"); fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_set_logical_id (FU_DEVICE (self), "rom"); fu_udev_device_set_flags (FU_UDEV_DEVICE (self), FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT); @@ -110,5 +123,6 @@ fu_optionrom_device_class_init (FuOptionromDeviceClass *klass) FuUdevDeviceClass *klass_udev_device = FU_UDEV_DEVICE_CLASS (klass); object_class->finalize = fu_optionrom_device_finalize; klass_device->read_firmware = fu_optionrom_device_read_firmware; + klass_device->dump_firmware = fu_optionrom_device_dump_firmware; klass_udev_device->probe = fu_optionrom_device_probe; } diff --git a/plugins/optionrom/fu-rom.c b/plugins/optionrom/fu-rom.c index 9a4ae3c3e..e35464ab5 100644 --- a/plugins/optionrom/fu-rom.c +++ b/plugins/optionrom/fu-rom.c @@ -112,7 +112,7 @@ fu_rom_blank_serial_numbers (guint8 *buffer, guint buffer_sz) } static gchar * -fu_rom_get_hex_dump (guint8 *buffer, guint32 sz) +fu_rom_get_hex_dump (const guint8 *buffer, guint32 sz) { GString *str = g_string_new (""); for (guint32 i = 0; i < sz; i++) @@ -135,7 +135,7 @@ typedef struct { } FooRomPciCertificateHdr; static void -fu_rom_pci_print_certificate_data (guint8 *buffer, gssize sz) +fu_rom_pci_print_certificate_data (const guint8 *buffer, gssize sz) { guint16 off = 0; g_autofree gchar *hdr_str = NULL; @@ -155,7 +155,7 @@ fu_rom_pci_print_certificate_data (guint8 *buffer, gssize sz) g_debug (" ISBN segment @%02x: %s", off, segment_str); h.segment_kind = buffer[off+1]; h.next_offset = (guint16) (((guint16) buffer[off+14] << 8) + buffer[off+13]); - h.data = &buffer[off+29]; + h.data = (guint8 *) &buffer[off+29]; /* calculate last block length automatically */ if (h.next_offset == 0) @@ -396,7 +396,7 @@ fu_rom_pci_parse_data (FuRomPciHeader *hdr) } static FuRomPciHeader * -fu_rom_pci_get_header (guint8 *buffer, guint32 sz) +fu_rom_pci_get_header (const guint8 *buffer, guint32 sz) { FuRomPciHeader *hdr; @@ -536,15 +536,17 @@ fu_rom_find_version (FuRomKind kind, FuRomPciHeader *hdr) gboolean fu_rom_load_data (FuRom *self, - guint8 *buffer, gsize buffer_sz, + GBytes *blob, FuRomLoadFlags flags, GCancellable *cancellable, GError **error) { FuRomPciHeader *hdr = NULL; - guint32 sz = buffer_sz; guint32 jump = 0; guint32 hdr_sz = 0; + gsize buffer_sz = 0; + const guint8 *buffer = g_bytes_get_data (blob, &buffer_sz); + guint32 sz = buffer_sz; g_return_val_if_fail (FU_IS_ROM (self), FALSE); @@ -682,19 +684,16 @@ fu_rom_load_data (FuRom *self, return TRUE; } -gboolean -fu_rom_load_file (FuRom *self, GFile *file, FuRomLoadFlags flags, - GCancellable *cancellable, GError **error) +GBytes * +fu_rom_dump_firmware (GFile *file, GCancellable *cancellable, GError **error) { - const gssize buffer_sz = 0x400000; - gssize sz; guint number_reads = 0; - g_autoptr(GError) error_local = NULL; g_autofree gchar *fn = NULL; - g_autofree guint8 *buffer = NULL; + g_autoptr(GByteArray) buf = g_byte_array_new (); + g_autoptr(GError) error_local = NULL; g_autoptr(GInputStream) stream = NULL; - g_return_val_if_fail (FU_IS_ROM (self), FALSE); + g_return_val_if_fail (G_IS_FILE (file), NULL); /* open file */ stream = G_INPUT_STREAM (g_file_read (file, cancellable, &error_local)); @@ -703,7 +702,7 @@ fu_rom_load_file (FuRom *self, GFile *file, FuRomLoadFlags flags, FWUPD_ERROR, FWUPD_ERROR_AUTH_FAILED, error_local->message); - return FALSE; + return NULL; } /* we have to enable the read for devices */ @@ -713,54 +712,55 @@ fu_rom_load_file (FuRom *self, GFile *file, FuRomLoadFlags flags, output_stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, cancellable, error); if (output_stream == NULL) - return FALSE; + return NULL; if (g_output_stream_write (G_OUTPUT_STREAM (output_stream), "1", 1, cancellable, error) < 0) - return FALSE; - } - - /* read out the header */ - buffer = g_malloc ((gsize) buffer_sz); - sz = g_input_stream_read (stream, buffer, buffer_sz, - cancellable, error); - if (sz < 0) - return FALSE; - if (sz < 512) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Firmware too small: %" G_GSSIZE_FORMAT " bytes", sz); - return FALSE; + return NULL; } /* ensure we got enough data to fill the buffer */ - while (sz < buffer_sz) { - gssize sz_chunk; - sz_chunk = g_input_stream_read (stream, - buffer + sz, - buffer_sz - sz, - cancellable, - error); - if (sz_chunk == 0) + while (TRUE) { + gssize sz; + guint8 tmp[32 * 1024] = { 0x0 }; + sz = g_input_stream_read (stream, tmp, sizeof(tmp), cancellable, error); + if (sz == 0) break; - g_debug ("ROM returned 0x%04x bytes, adding 0x%04x...", - (guint) sz, (guint) sz_chunk); - if (sz_chunk < 0) - return FALSE; - sz += sz_chunk; + g_debug ("ROM returned 0x%04x bytes", (guint) sz); + if (sz < 0) + return NULL; + g_byte_array_append (buf, tmp, sz); /* check the firmware isn't serving us small chunks */ - if (number_reads++ > 16) { + if (number_reads++ > 1024) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "firmware not fulfilling requests"); - return FALSE; + return NULL; } } - g_debug ("ROM buffer filled %" G_GSSIZE_FORMAT "kb/%" G_GSSIZE_FORMAT "kb", - sz / 0x400, buffer_sz / 0x400); - return fu_rom_load_data (self, buffer, sz, flags, cancellable, error); + if (buf->len < 512) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware too small: %u bytes", buf->len); + return NULL; + } + return g_byte_array_free_to_bytes (g_steal_pointer (&buf)); +} + +gboolean +fu_rom_load_file (FuRom *self, GFile *file, FuRomLoadFlags flags, + GCancellable *cancellable, GError **error) +{ + g_autoptr(GBytes) blob = NULL; + + g_return_val_if_fail (FU_IS_ROM (self), FALSE); + + blob = fu_rom_dump_firmware (file, cancellable, error); + if (blob == NULL) + return FALSE; + return fu_rom_load_data (self, blob, flags, cancellable, error); } FuRomKind diff --git a/plugins/optionrom/fu-rom.h b/plugins/optionrom/fu-rom.h index 45088fe21..532b9ead6 100644 --- a/plugins/optionrom/fu-rom.h +++ b/plugins/optionrom/fu-rom.h @@ -34,8 +34,7 @@ gboolean fu_rom_load_file (FuRom *self, GCancellable *cancellable, GError **error); gboolean fu_rom_load_data (FuRom *self, - guint8 *buffer, - gsize buffer_sz, + GBytes *blob, FuRomLoadFlags flags, GCancellable *cancellable, GError **error); @@ -48,3 +47,6 @@ GBytes *fu_rom_get_data (FuRom *self); guint16 fu_rom_get_vendor (FuRom *self); guint16 fu_rom_get_model (FuRom *self); const gchar *fu_rom_kind_to_string (FuRomKind kind); +GBytes *fu_rom_dump_firmware (GFile *file, + GCancellable *cancellable, + GError **error); diff --git a/plugins/optionrom/fu-self-test.c b/plugins/optionrom/fu-self-test.c index d3aa79326..59f050123 100644 --- a/plugins/optionrom/fu-self-test.c +++ b/plugins/optionrom/fu-self-test.c @@ -61,7 +61,7 @@ fu_rom_func (void) g_assert (rom != NULL); /* load file */ - filename = g_build_filename (TESTDATADIR, data[i].fn, NULL); + filename = g_test_build_filename (G_TEST_DIST, "tests", data[i].fn, NULL); if (!g_file_test (filename, G_FILE_TEST_EXISTS)) continue; g_print ("\nparsing %s...", filename); @@ -83,7 +83,7 @@ fu_rom_all_func (void) g_autofree gchar *path = NULL; /* may or may not exist */ - path = g_build_filename (TESTDATADIR, "roms", NULL); + path = g_test_build_filename (G_TEST_DIST, "tests", "roms", NULL); if (!g_file_test (path, G_FILE_TEST_EXISTS)) return; g_print ("\n"); diff --git a/plugins/optionrom/fuzzing.md b/plugins/optionrom/fuzzing.md deleted file mode 100644 index bd1334cb3..000000000 --- a/plugins/optionrom/fuzzing.md +++ /dev/null @@ -1,3 +0,0 @@ -CC=afl-gcc ./configure --disable-shared -AFL_HARDEN=1 make -afl-fuzz -m 300 -i fuzzing -o findings ./fu-rom-tool rom @@ diff --git a/plugins/optionrom/fuzzing/meson.build b/plugins/optionrom/fuzzing/meson.build new file mode 100644 index 000000000..06c1cf426 --- /dev/null +++ b/plugins/optionrom/fuzzing/meson.build @@ -0,0 +1,9 @@ +run_target('fuzz-optionrom', + command: [ + join_paths(meson.source_root(), 'contrib/afl-fuzz.py'), + '--command', 'rom', + '-i', meson.current_source_dir(), + '-o', join_paths(meson.current_build_dir(), '..', 'findings'), + optionrom_tool, + ], +) diff --git a/plugins/optionrom/meson.build b/plugins/optionrom/meson.build index 10901ff5e..8d0674062 100644 --- a/plugins/optionrom/meson.build +++ b/plugins/optionrom/meson.build @@ -28,7 +28,7 @@ shared_module('fu_plugin_optionrom', ], ) -executable( +optionrom_tool = executable( 'fu-rom-tool', fu_hash, sources : [ @@ -52,9 +52,9 @@ executable( ) if get_option('tests') - cargs += '-DPLUGINBUILDDIR="' + meson.current_build_dir() + '"' - testdatadir = join_paths(meson.current_source_dir(), 'tests') - cargs += '-DTESTDATADIR="' + testdatadir + '"' + testdatadirs = environment() + testdatadirs.set('G_TEST_SRCDIR', meson.current_source_dir()) + testdatadirs.set('G_TEST_BUILDDIR', meson.current_build_dir()) e = executable( 'optionrom-self-test', fu_hash, @@ -74,7 +74,10 @@ if get_option('tests') fwupd, fwupdplugin, ], - c_args : cargs + c_args : cargs, + install : true, + install_dir : installed_test_bindir, ) - test('optionrom-self-test', e) + test('optionrom-self-test', e, env : testdatadirs) # added to installed-tests + subdir('fuzzing') endif diff --git a/plugins/pci-bcr/README.md b/plugins/pci-bcr/README.md new file mode 100644 index 000000000..dae5c5974 --- /dev/null +++ b/plugins/pci-bcr/README.md @@ -0,0 +1,12 @@ +PCI BIOS Control Register +========================= + +Introduction +------------ + +This plugin checks if the system SPI chip is locked. The result will be stored +in an security attribute for HSI. + +External interface access +------------------------- +This plugin requires read access to the config space of PCI devices (`/sys/class/pci_bus/*/device/config`) diff --git a/plugins/pci-bcr/config b/plugins/pci-bcr/config new file mode 100644 index 000000000..7d79f10fb Binary files /dev/null and b/plugins/pci-bcr/config differ diff --git a/plugins/pci-bcr/fu-plugin-pci-bcr.c b/plugins/pci-bcr/fu-plugin-pci-bcr.c new file mode 100644 index 000000000..01661f7ca --- /dev/null +++ b/plugins/pci-bcr/fu-plugin-pci-bcr.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" + +struct FuPluginData { + gboolean has_device; + guint8 bcr_addr; + guint8 bcr; +}; + +#define BCR_WPD (1 << 0) +#define BCR_BLE (1 << 1) +#define BCR_SMM_BWP (1 << 5) + +void +fu_plugin_init (FuPlugin *plugin) +{ + FuPluginData *priv = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_udev_subsystem (plugin, "pci"); + + /* this is true except for some Atoms */ + priv->bcr_addr = 0xdc; +} + +void +fu_plugin_device_registered (FuPlugin *plugin, FuDevice *dev) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + if (g_strcmp0 (fu_device_get_plugin (dev), "cpu") == 0) { + guint tmp = fu_device_get_metadata_integer (dev, "BcrAddr"); + if (tmp != G_MAXUINT && priv->bcr_addr != tmp) { + g_debug ("overriding BCR addr from 0x%02x to 0x%02x", + priv->bcr_addr, tmp); + priv->bcr_addr = tmp; + } + } +} + +static void +fu_plugin_add_security_attr_bioswe (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fu_security_attrs_append (attrs, attr); + + /* no device */ + if (!priv->has_device) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* load file */ + if ((priv->bcr & BCR_WPD) == 1) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); +} + +static void +fu_plugin_add_security_attr_ble (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* no device */ + if (!priv->has_device) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_SPI_BLE); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fu_security_attrs_append (attrs, attr); + + /* load file */ + if ((priv->bcr & BCR_BLE) == 0) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_plugin_add_security_attr_smm_bwp (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* no device */ + if (!priv->has_device) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fu_security_attrs_append (attrs, attr); + + /* load file */ + if ((priv->bcr & BCR_SMM_BWP) == 0) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); +} + +gboolean +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* not supported */ + if (priv->bcr_addr == 0x0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "BCR not supported on this platform"); + return FALSE; + } + + /* interesting device? */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "pci") != 0) + return TRUE; + + /* open the config */ + fu_udev_device_set_flags (device, FU_UDEV_DEVICE_FLAG_USE_CONFIG); + if (!fu_udev_device_set_physical_id (device, "pci", error)) + return FALSE; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + + /* grab BIOS Control Register */ + if (!fu_udev_device_pread (device, priv->bcr_addr, &priv->bcr, error)) { + g_prefix_error (error, "could not read BCR"); + return FALSE; + } + priv->has_device = TRUE; + return TRUE; +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + /* only Intel */ + if (!fu_common_is_cpu_intel ()) + return; + + /* add attrs */ + fu_plugin_add_security_attr_bioswe (plugin, attrs); + fu_plugin_add_security_attr_ble (plugin, attrs); + fu_plugin_add_security_attr_smm_bwp (plugin, attrs); +} diff --git a/plugins/pci-bcr/meson.build b/plugins/pci-bcr/meson.build new file mode 100644 index 000000000..f5345935b --- /dev/null +++ b/plugins/pci-bcr/meson.build @@ -0,0 +1,27 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginPciBcr"'] + +install_data(['pci-bcr.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_pci_bcr', + fu_hash, + sources : [ + 'fu-plugin-pci-bcr.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/pci-bcr/pci-bcr.quirk b/plugins/pci-bcr/pci-bcr.quirk new file mode 100644 index 000000000..4ddb5007d --- /dev/null +++ b/plugins/pci-bcr/pci-bcr.quirk @@ -0,0 +1,9 @@ +# ISA bridge i.e. 00:1F.0 +# -> drivers/mfd/lpc_ich.c +[DeviceInstanceId=PCI\VEN_8086&CLASS_060100] +Plugin = pci_bcr + +# PCI devices, i.e. 00:1F.5 +# -> drivers/mtd/spi-nor/controllers/intel-spi-pci.c +[DeviceInstanceId=PCI\VEN_8086&CLASS_0C8000] +Plugin = pci_bcr diff --git a/plugins/pci-mei/README.md b/plugins/pci-mei/README.md new file mode 100644 index 000000000..d52e8f005 --- /dev/null +++ b/plugins/pci-mei/README.md @@ -0,0 +1,12 @@ +PCI MEI +======= + +Introduction +------------ + +This plugin checks if the ME is in Manufacturing Mode. The result will be stored +in an security attribute for HSI. + +External interface access +------------------------- +This plugin requires read access to the config space of PCI devices (`/sys/class/pci_bus/*/device/config`) diff --git a/plugins/pci-mei/fu-mei-common.c b/plugins/pci-mei/fu-mei-common.c new file mode 100644 index 000000000..1230e1460 --- /dev/null +++ b/plugins/pci-mei/fu-mei-common.c @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-mei-common.h" + +const gchar * +fu_mei_common_family_to_string (FuMeiFamily family) +{ + if (family == FU_MEI_FAMILY_SPS) + return "SPS"; + if (family == FU_MEI_FAMILY_TXE) + return "TXE"; + if (family == FU_MEI_FAMILY_ME) + return "ME"; + if (family == FU_MEI_FAMILY_CSME) + return "CSME"; + return "AMT"; +} + +static gint +fu_mei_common_cmp_version (FuMeiVersion *vers1, FuMeiVersion *vers2) +{ + guint16 vers1buf[] = { + vers1->major, + vers1->minor, + vers1->hotfix, + vers1->buildno, + }; + guint16 vers2buf[] = { + vers2->major, + vers2->minor, + vers2->hotfix, + vers2->buildno, + }; + for (guint i = 0; i < 4; i++) { + if (vers1buf[i] < vers2buf[i]) + return -1; + if (vers1buf[i] > vers2buf[i]) + return 1; + } + return 0; +} + +FuMeiIssue +fu_mei_common_is_csme_vulnerable (FuMeiVersion *vers) +{ + if (vers->major == 11 && vers->minor == 8 && vers->hotfix >= 70) + return FU_MEI_ISSUE_PATCHED; + if (vers->major == 11 && vers->minor == 11 && vers->hotfix >= 70) + return FU_MEI_ISSUE_PATCHED; + if (vers->major == 11 && vers->minor == 22 && vers->hotfix >= 70) + return FU_MEI_ISSUE_PATCHED; + if (vers->major == 12 && vers->minor == 0 && (vers->hotfix == 49 || vers->hotfix >= 56)) + return FU_MEI_ISSUE_PATCHED; + if (vers->major == 13 && vers->minor == 0 && vers->hotfix >= 21) + return FU_MEI_ISSUE_PATCHED; + if (vers->major == 14 && vers->minor == 0 && vers->hotfix >= 11) + return FU_MEI_ISSUE_PATCHED; + if (vers->major == 15) + return FU_MEI_ISSUE_NOT_VULNERABLE; + return FU_MEI_ISSUE_VULNERABLE; +} + +FuMeiIssue +fu_mei_common_is_txe_vulnerable (FuMeiVersion *vers) +{ + if (vers->major == 3 && vers->minor == 1 && vers->hotfix >= 70) + return FU_MEI_ISSUE_PATCHED; + if (vers->major == 4 && vers->minor == 0 && vers->hotfix >= 20) + return FU_MEI_ISSUE_PATCHED; + if (vers->major == 5) + return FU_MEI_ISSUE_NOT_VULNERABLE; + return FU_MEI_ISSUE_VULNERABLE; +} + +FuMeiIssue +fu_mei_common_is_sps_vulnerable (FuMeiVersion *vers) +{ + if (vers->major == 3 || vers->major > 5) + return FU_MEI_ISSUE_NOT_VULNERABLE; + if (vers->major == 4) { + if (vers->hotfix < 44) + return FU_MEI_ISSUE_VULNERABLE; + if (vers->platform == 0xA) { /* Purley */ + FuMeiVersion ver2 = { + .major = 4, + .minor = 1, + .hotfix = 4, + .buildno = 339, + }; + if (fu_mei_common_cmp_version (vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } else if (vers->platform == 0xE) { /* Bakerville */ + FuMeiVersion ver2 = { + .major = 4, + .minor = 0, + .hotfix = 4, + .buildno = 112, + }; + if (fu_mei_common_cmp_version (vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } else if (vers->platform == 0xB) { /* Harrisonville */ + FuMeiVersion ver2 = { + .major = 4, + .minor = 0, + .hotfix = 4, + .buildno = 193, + }; + if (fu_mei_common_cmp_version (vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } else if (vers->platform == 0x9) { /* Greenlow */ + FuMeiVersion ver2 = { + .major = 4, + .minor = 1, + .hotfix = 4, + .buildno = 88, + }; + if (vers->minor < 1) + return FU_MEI_ISSUE_NOT_VULNERABLE; + if (fu_mei_common_cmp_version (vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } else if (vers->platform == 0xD) { /* MonteVista */ + FuMeiVersion ver2 = { + .major = 4, + .minor = 8, + .hotfix = 4, + .buildno = 51, + }; + if (fu_mei_common_cmp_version (vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } + return FU_MEI_ISSUE_NOT_VULNERABLE; + } + if (vers->major == 5) { + if (vers->platform == 0x10) { /* Mehlow */ + FuMeiVersion ver2 = { 5, 1, 3, 89 }; + if (fu_mei_common_cmp_version (vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } + return FU_MEI_ISSUE_NOT_VULNERABLE; + } + return FU_MEI_ISSUE_PATCHED; +} + +/* HFS1[3:0] Current Working State Values */ +static const char *me_cws_values[] = { + [ME_HFS_CWS_RESET] = "reset", + [ME_HFS_CWS_INIT] = "initializing", + [ME_HFS_CWS_REC] = "recovery", + [ME_HFS_CWS_TEST] = "test", + [ME_HFS_CWS_DISABLED] = "disabled", + [ME_HFS_CWS_NORMAL] = "normal", + [ME_HFS_CWS_WAIT] = "wait", + [ME_HFS_CWS_TRANS] = "transition", + [ME_HFS_CWS_INVALID] = "invalid", +}; + +/* HFS1[8:6] Current Operation State Values */ +static const char *me_opstate_values[] = { + [ME_HFS_STATE_PREBOOT] = "preboot", + [ME_HFS_STATE_M0_UMA] = "m0-with-uma", + [ME_HFS_STATE_M3] = "m3-without-uma", + [ME_HFS_STATE_M0] = "m0-without-uma", + [ME_HFS_STATE_BRINGUP] = "bring-up", + [ME_HFS_STATE_ERROR] = "error", +}; + +/* HFS[19:16] Current Operation Mode Values */ +static const char *me_opmode_values[] = { + [ME_HFS_MODE_NORMAL] = "normal", + [ME_HFS_MODE_DEBUG] = "debug", + [ME_HFS_MODE_DIS] = "disable", + [ME_HFS_MODE_OVER_JMPR] = "override-jumper", + [ME_HFS_MODE_OVER_MEI] = "override-mei", + [ME_HFS_MODE_UNKNOWN_6] = "unknown-6", + [ME_HFS_MODE_MAYBE_SPS] = "maybe-sps", +}; + +/* HFS[15:12] Error Code Values */ +static const char *me_error_values[] = { + [ME_HFS_ERROR_NONE] = "no-error", + [ME_HFS_ERROR_UNCAT] = "uncategorized-failure", + [ME_HFS_ERROR_DISABLED] = "disabled", + [ME_HFS_ERROR_IMAGE] = "image-failure", + [ME_HFS_ERROR_DEBUG] = "debug-failure", +}; + +void +fu_mei_hfsts1_to_string (FuMeiHfsts1 hfsts1, guint idt, GString *str) +{ + fu_common_string_append_kv (str, idt, "WorkingState", + me_cws_values[hfsts1.fields.working_state]); + fu_common_string_append_kb (str, idt, "MfgMode", + hfsts1.fields.mfg_mode); + fu_common_string_append_kb (str, idt, "FptBad", + hfsts1.fields.fpt_bad); + fu_common_string_append_kv (str, idt, "OperationState", + me_opstate_values[hfsts1.fields.operation_state]); + fu_common_string_append_kb (str, idt, "FwInitComplete", + hfsts1.fields.fw_init_complete); + fu_common_string_append_kb (str, idt, "FtBupLdFlr", + hfsts1.fields.ft_bup_ld_flr); + fu_common_string_append_kb (str, idt, "UpdateInProgress", + hfsts1.fields.update_in_progress); + fu_common_string_append_kv (str, idt, "ErrorCode", + me_error_values[hfsts1.fields.error_code]); + fu_common_string_append_kv (str, idt, "OperationMode", + me_opmode_values[hfsts1.fields.operation_mode]); + fu_common_string_append_kx (str, idt, "ResetCount", + hfsts1.fields.reset_count); + fu_common_string_append_kb (str, idt, "BootOptions_present", + hfsts1.fields.boot_options_present); + fu_common_string_append_kb (str, idt, "BistFinished", + hfsts1.fields.bist_finished); + fu_common_string_append_kb (str, idt, "BistTestState", + hfsts1.fields.bist_test_state); + fu_common_string_append_kb (str, idt, "BistResetRequest", + hfsts1.fields.bist_reset_request); + fu_common_string_append_kx (str, idt, "CurrentPowerSource", + hfsts1.fields.current_power_source); + fu_common_string_append_kb (str, idt, "D3SupportValid", + hfsts1.fields.d3_support_valid); + fu_common_string_append_kb (str, idt, "D0i3SupportValid", + hfsts1.fields.d0i3_support_valid); +} + +void +fu_mei_hfsts2_to_string (FuMeiHfsts2 hfsts2, guint idt, GString *str) +{ + fu_common_string_append_kb (str, idt, "NftpLoadFailure", + hfsts2.fields.nftp_load_failure); + fu_common_string_append_kx (str, idt, "IccProgStatus", + hfsts2.fields.icc_prog_status); + fu_common_string_append_kb (str, idt, "InvokeMebx", + hfsts2.fields.invoke_mebx); + fu_common_string_append_kb (str, idt, "CpuReplaced", + hfsts2.fields.cpu_replaced); + fu_common_string_append_kb (str, idt, "Rsvd0", + hfsts2.fields.rsvd0); + fu_common_string_append_kb (str, idt, "MfsFailure", + hfsts2.fields.mfs_failure); + fu_common_string_append_kb (str, idt, "WarmResetRqst", + hfsts2.fields.warm_reset_rqst); + fu_common_string_append_kb (str, idt, "CpuReplacedValid", + hfsts2.fields.cpu_replaced_valid); + fu_common_string_append_kb (str, idt, "LowPowerState", + hfsts2.fields.low_power_state); + fu_common_string_append_kb (str, idt, "MePowerGate", + hfsts2.fields.me_power_gate); + fu_common_string_append_kb (str, idt, "IpuNeeded", + hfsts2.fields.ipu_needed); + fu_common_string_append_kb (str, idt, "ForcedSafeBoot", + hfsts2.fields.forced_safe_boot); + fu_common_string_append_kx (str, idt, "Rsvd1", + hfsts2.fields.rsvd1); + fu_common_string_append_kb (str, idt, "ListenerChange", + hfsts2.fields.listener_change); + fu_common_string_append_kx (str, idt, "StatusData", + hfsts2.fields.status_data); + fu_common_string_append_kx (str, idt, "CurrentPmevent", + hfsts2.fields.current_pmevent); + fu_common_string_append_kx (str, idt, "Phase", + hfsts2.fields.phase); +} + +void +fu_mei_hfsts3_to_string (FuMeiHfsts3 hfsts3, guint idt, GString *str) +{ + fu_common_string_append_kx (str, idt, "Chunk0", + hfsts3.fields.chunk0); + fu_common_string_append_kx (str, idt, "Chunk1", + hfsts3.fields.chunk1); + fu_common_string_append_kx (str, idt, "Chunk2", + hfsts3.fields.chunk2); + fu_common_string_append_kx (str, idt, "Chunk3", + hfsts3.fields.chunk3); + fu_common_string_append_kx (str, idt, "FwSku", + hfsts3.fields.fw_sku); + fu_common_string_append_kb (str, idt, "EncryptKeyCheck", + hfsts3.fields.encrypt_key_check); + fu_common_string_append_kb (str, idt, "PchConfigChange", + hfsts3.fields.pch_config_change); + fu_common_string_append_kb (str, idt, "IbbVerificationResult", + hfsts3.fields.ibb_verification_result); + fu_common_string_append_kb (str, idt, "IbbVerificationDone", + hfsts3.fields.ibb_verification_done); + fu_common_string_append_kx (str, idt, "Reserved11", + hfsts3.fields.reserved_11); + fu_common_string_append_kx (str, idt, "ActualIbbSize", + hfsts3.fields.actual_ibb_size * 1024); + fu_common_string_append_ku (str, idt, "NumberOfChunks", + hfsts3.fields.number_of_chunks); + fu_common_string_append_kb (str, idt, "EncryptKeyOverride", + hfsts3.fields.encrypt_key_override); + fu_common_string_append_kb (str, idt, "PowerDownMitigation", + hfsts3.fields.power_down_mitigation); +} + +void +fu_mei_hfsts4_to_string (FuMeiHfsts4 hfsts4, guint idt, GString *str) +{ + fu_common_string_append_kx (str, idt, "Rsvd0", + hfsts4.fields.rsvd0); + fu_common_string_append_kb (str, idt, "EnforcementFlow", + hfsts4.fields.enforcement_flow); + fu_common_string_append_kb (str, idt, "SxResumeType", + hfsts4.fields.sx_resume_type); + fu_common_string_append_kb (str, idt, "Rsvd1", + hfsts4.fields.rsvd1); + fu_common_string_append_kb (str, idt, "TpmsDisconnected", + hfsts4.fields.tpms_disconnected); + fu_common_string_append_kb (str, idt, "Rvsd2", + hfsts4.fields.rvsd2); + fu_common_string_append_kb (str, idt, "FwstsValid", + hfsts4.fields.fwsts_valid); + fu_common_string_append_kb (str, idt, "BootGuardSelfTest", + hfsts4.fields.boot_guard_self_test); + fu_common_string_append_kx (str, idt, "Rsvd3", + hfsts4.fields.rsvd3); +} + +void +fu_mei_hfsts5_to_string (FuMeiHfsts5 hfsts5, guint idt, GString *str) +{ + fu_common_string_append_kb (str, idt, "AcmActive", + hfsts5.fields.acm_active); + fu_common_string_append_kb (str, idt, "Valid", + hfsts5.fields.valid); + fu_common_string_append_kb (str, idt, "ResultCodeSource", + hfsts5.fields.result_code_source); + fu_common_string_append_kx (str, idt, "ErrorStatusCode", + hfsts5.fields.error_status_code); + fu_common_string_append_kx (str, idt, "AcmDoneSts", + hfsts5.fields.acm_done_sts); + fu_common_string_append_kx (str, idt, "TimeoutCount", + hfsts5.fields.timeout_count); + fu_common_string_append_kb (str, idt, "ScrtmIndicator", + hfsts5.fields.scrtm_indicator); + fu_common_string_append_kx (str, idt, "IncBootGuardAcm", + hfsts5.fields.inc_boot_guard_acm); + fu_common_string_append_kx (str, idt, "IncKeyManifest", + hfsts5.fields.inc_key_manifest); + fu_common_string_append_kx (str, idt, "IncBootPolicy", + hfsts5.fields.inc_boot_policy); + fu_common_string_append_kx (str, idt, "Rsvd0", + hfsts5.fields.rsvd0); + fu_common_string_append_kb (str, idt, "StartEnforcement", + hfsts5.fields.start_enforcement); +} + +void +fu_mei_hfsts6_to_string (FuMeiHfsts6 hfsts6, guint idt, GString *str) +{ + fu_common_string_append_kb (str, idt, "ForceBootGuardAcm", + hfsts6.fields.force_boot_guard_acm); + fu_common_string_append_kb (str, idt, "CpuDebugDisable", + hfsts6.fields.cpu_debug_disable); + fu_common_string_append_kb (str, idt, "BspInitDisable", + hfsts6.fields.bsp_init_disable); + fu_common_string_append_kb (str, idt, "ProtectBiosEnv", + hfsts6.fields.protect_bios_env); + fu_common_string_append_kx (str, idt, "Rsvd0", + hfsts6.fields.rsvd0); + fu_common_string_append_kx (str, idt, "ErrorEnforcePolicy", + hfsts6.fields.error_enforce_policy); + fu_common_string_append_kb (str, idt, "MeasuredBoot", + hfsts6.fields.measured_boot); + fu_common_string_append_kb (str, idt, "VerifiedBoot", + hfsts6.fields.verified_boot); + fu_common_string_append_kx (str, idt, "BootGuardAcmsvn", + hfsts6.fields.boot_guard_acmsvn); + fu_common_string_append_kx (str, idt, "Kmsvn", + hfsts6.fields.kmsvn); + fu_common_string_append_kx (str, idt, "Bpmsvn", + hfsts6.fields.bpmsvn); + fu_common_string_append_kx (str, idt, "KeyManifestId", + hfsts6.fields.key_manifest_id); + fu_common_string_append_kb (str, idt, "BootPolicyStatus", + hfsts6.fields.boot_policy_status); + fu_common_string_append_kb (str, idt, "Error", + hfsts6.fields.error); + fu_common_string_append_kb (str, idt, "BootGuardDisable", + hfsts6.fields.boot_guard_disable); + fu_common_string_append_kb (str, idt, "FpfDisable", + hfsts6.fields.fpf_disable); + fu_common_string_append_kb (str, idt, "FpfSocLock", + hfsts6.fields.fpf_soc_lock); + fu_common_string_append_kb (str, idt, "TxtSupport", + hfsts6.fields.txt_support); +} diff --git a/plugins/pci-mei/fu-mei-common.h b/plugins/pci-mei/fu-mei-common.h new file mode 100644 index 000000000..440d8949c --- /dev/null +++ b/plugins/pci-mei/fu-mei-common.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +typedef enum { + FU_MEI_FAMILY_UNKNOWN, + FU_MEI_FAMILY_SPS, + FU_MEI_FAMILY_TXE, + FU_MEI_FAMILY_ME, + FU_MEI_FAMILY_CSME, +} FuMeiFamily; + +typedef enum { + FU_MEI_ISSUE_UNKNOWN, + FU_MEI_ISSUE_NOT_VULNERABLE, + FU_MEI_ISSUE_VULNERABLE, + FU_MEI_ISSUE_PATCHED, +} FuMeiIssue; + +typedef struct { + guint8 platform; + guint8 major; + guint8 minor; + guint8 hotfix; + guint16 buildno; +} FuMeiVersion; + +/* Host Firmware Status register 1 */ +typedef union { + guint32 data; + struct { + guint32 working_state : 4; + guint32 mfg_mode : 1; + guint32 fpt_bad : 1; + guint32 operation_state : 3; + guint32 fw_init_complete : 1; + guint32 ft_bup_ld_flr : 1; + guint32 update_in_progress : 1; + guint32 error_code : 4; + guint32 operation_mode : 4; + guint32 reset_count : 4; + guint32 boot_options_present : 1; + guint32 bist_finished : 1; + guint32 bist_test_state : 1; + guint32 bist_reset_request : 1; + guint32 current_power_source : 2; + guint32 d3_support_valid : 1; + guint32 d0i3_support_valid : 1; + } __attribute__((packed)) fields; +} FuMeiHfsts1; + +/* Host Firmware Status Register 2 */ +typedef union { + guint32 data; + struct { + guint32 nftp_load_failure : 1; + guint32 icc_prog_status : 2; + guint32 invoke_mebx : 1; + guint32 cpu_replaced : 1; + guint32 rsvd0 : 1; + guint32 mfs_failure : 1; + guint32 warm_reset_rqst : 1; + guint32 cpu_replaced_valid : 1; + guint32 low_power_state : 1; + guint32 me_power_gate : 1; + guint32 ipu_needed : 1; + guint32 forced_safe_boot : 1; + guint32 rsvd1 : 2; + guint32 listener_change : 1; + guint32 status_data : 8; + guint32 current_pmevent : 4; + guint32 phase : 4; + } __attribute__((packed)) fields; +} FuMeiHfsts2; + +/* Host Firmware Status Register 3 */ +typedef union { + guint32 data; + struct { + guint32 chunk0 : 1; + guint32 chunk1 : 1; + guint32 chunk2 : 1; + guint32 chunk3 : 1; + guint32 fw_sku : 3; + guint32 encrypt_key_check : 1; + guint32 pch_config_change : 1; + guint32 ibb_verification_result : 1; + guint32 ibb_verification_done : 1; + guint32 reserved_11 : 3; + guint32 actual_ibb_size : 14; + guint32 number_of_chunks : 2; + guint32 encrypt_key_override : 1; + guint32 power_down_mitigation : 1; + } __attribute__((packed)) fields; +} FuMeiHfsts3; + +/* Host Firmware Status Register 4 */ +typedef union { + guint32 data; + struct { + guint32 rsvd0 : 9; + guint32 enforcement_flow : 1; + guint32 sx_resume_type : 1; + guint32 rsvd1 : 1; + guint32 tpms_disconnected : 1; + guint32 rvsd2 : 1; + guint32 fwsts_valid : 1; + guint32 boot_guard_self_test : 1; + guint32 rsvd3 : 16; + } __attribute__((packed)) fields; +} FuMeiHfsts4; + +/* Host Firmware Status Register 5 */ +typedef union { + guint32 data; + struct { + guint32 acm_active : 1; + guint32 valid : 1; + guint32 result_code_source : 1; + guint32 error_status_code : 5; + guint32 acm_done_sts : 1; + guint32 timeout_count : 7; + guint32 scrtm_indicator : 1; + guint32 inc_boot_guard_acm : 4; + guint32 inc_key_manifest : 4; + guint32 inc_boot_policy : 4; + guint32 rsvd0 : 2; + guint32 start_enforcement : 1; + } __attribute__((packed)) fields; +} FuMeiHfsts5; + +/* Host Firmware Status Register 6 */ +typedef union { + guint32 data; + struct { + guint32 force_boot_guard_acm : 1; + guint32 cpu_debug_disable : 1; + guint32 bsp_init_disable : 1; + guint32 protect_bios_env : 1; + guint32 rsvd0 : 2; + guint32 error_enforce_policy : 2; + guint32 measured_boot : 1; + guint32 verified_boot : 1; + guint32 boot_guard_acmsvn : 4; + guint32 kmsvn : 4; + guint32 bpmsvn : 4; + guint32 key_manifest_id : 4; + guint32 boot_policy_status : 1; + guint32 error : 1; + guint32 boot_guard_disable : 1; + guint32 fpf_disable : 1; + guint32 fpf_soc_lock : 1; + guint32 txt_support : 1; + } __attribute__((packed)) fields; +} FuMeiHfsts6; + +#define ME_HFS_CWS_RESET 0 +#define ME_HFS_CWS_INIT 1 +#define ME_HFS_CWS_REC 2 +#define ME_HFS_CWS_TEST 3 +#define ME_HFS_CWS_DISABLED 4 +#define ME_HFS_CWS_NORMAL 5 +#define ME_HFS_CWS_WAIT 6 +#define ME_HFS_CWS_TRANS 7 +#define ME_HFS_CWS_INVALID 8 + +#define ME_HFS_STATE_PREBOOT 0 +#define ME_HFS_STATE_M0_UMA 1 +#define ME_HFS_STATE_M3 4 +#define ME_HFS_STATE_M0 5 +#define ME_HFS_STATE_BRINGUP 6 +#define ME_HFS_STATE_ERROR 7 + +#define ME_HFS_ERROR_NONE 0 +#define ME_HFS_ERROR_UNCAT 1 +#define ME_HFS_ERROR_DISABLED 2 +#define ME_HFS_ERROR_IMAGE 3 +#define ME_HFS_ERROR_DEBUG 4 + +#define ME_HFS_MODE_NORMAL 0 +#define ME_HFS_MODE_DEBUG 2 +#define ME_HFS_MODE_DIS 3 +#define ME_HFS_MODE_OVER_JMPR 4 +#define ME_HFS_MODE_OVER_MEI 5 +#define ME_HFS_MODE_UNKNOWN_6 6 +#define ME_HFS_MODE_MAYBE_SPS 7 + +#define ME_HFS_ENFORCEMENT_POLICY_NOTHING 0b00 +#define ME_HFS_ENFORCEMENT_POLICY_SHUTDOWN_TO 0b01 +#define ME_HFS_ENFORCEMENT_POLICY_SHUTDOWN_NOW 0b11 + +const gchar *fu_mei_common_family_to_string (FuMeiFamily family); +FuMeiIssue fu_mei_common_is_csme_vulnerable (FuMeiVersion *vers); +FuMeiIssue fu_mei_common_is_txe_vulnerable (FuMeiVersion *vers); +FuMeiIssue fu_mei_common_is_sps_vulnerable (FuMeiVersion *vers); + +void fu_mei_hfsts1_to_string (FuMeiHfsts1 hfsts1, + guint idt, + GString *str); +void fu_mei_hfsts2_to_string (FuMeiHfsts2 hfsts2, + guint idt, + GString *str); +void fu_mei_hfsts3_to_string (FuMeiHfsts3 hfsts3, + guint idt, + GString *str); +void fu_mei_hfsts4_to_string (FuMeiHfsts4 hfsts4, + guint idt, + GString *str); +void fu_mei_hfsts5_to_string (FuMeiHfsts5 hfsts5, + guint idt, + GString *str); +void fu_mei_hfsts6_to_string (FuMeiHfsts6 hfsts6, + guint idt, + GString *str); diff --git a/plugins/pci-mei/fu-plugin-pci-mei.c b/plugins/pci-mei/fu-plugin-pci-mei.c new file mode 100644 index 000000000..6e5517d02 --- /dev/null +++ b/plugins/pci-mei/fu-plugin-pci-mei.c @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" + +#include "fu-mei-common.h" + +struct FuPluginData { + gboolean has_device; + FuMeiHfsts1 hfsts1; + FuMeiHfsts2 hfsts2; + FuMeiHfsts3 hfsts3; + FuMeiHfsts4 hfsts4; + FuMeiHfsts5 hfsts5; + FuMeiHfsts6 hfsts6; + FuMeiFamily family; + FuMeiVersion vers; + FuMeiIssue issue; +}; + +#define PCI_CFG_HFS_1 0x40 +#define PCI_CFG_HFS_2 0x48 +#define PCI_CFG_HFS_3 0x60 +#define PCI_CFG_HFS_4 0x64 +#define PCI_CFG_HFS_5 0x68 +#define PCI_CFG_HFS_6 0x6c + +static void +fu_mei_hfsts_to_string (FuPlugin *plugin, guint idt, GString *str) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + fu_common_string_append_kv (str, idt, "HFSTS1", NULL); + fu_mei_hfsts1_to_string (priv->hfsts1, idt + 1, str); + fu_common_string_append_kv (str, idt, "HFSTS2", NULL); + fu_mei_hfsts2_to_string (priv->hfsts2, idt + 1, str); + fu_common_string_append_kv (str, idt, "HFSTS3", NULL); + fu_mei_hfsts3_to_string (priv->hfsts3, idt + 1, str); + fu_common_string_append_kv (str, idt, "HFSTS4", NULL); + fu_mei_hfsts4_to_string (priv->hfsts4, idt + 1, str); + fu_common_string_append_kv (str, idt, "HFSTS5", NULL); + fu_mei_hfsts5_to_string (priv->hfsts5, idt + 1, str); + fu_common_string_append_kv (str, idt, "HFSTS6", NULL); + fu_mei_hfsts6_to_string (priv->hfsts6, idt + 1, str); +} + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_udev_subsystem (plugin, "pci"); +} + +static FuMeiFamily +fu_mei_detect_family (FuPlugin *plugin) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + guint8 ver = priv->vers.major; + + if (ver == 1 || ver == 2) { + if (priv->hfsts1.fields.operation_mode == 0xf) + return FU_MEI_FAMILY_SPS; + return FU_MEI_FAMILY_TXE; + } + if (ver == 3 || ver == 4 || ver == 5) + return FU_MEI_FAMILY_TXE; + if (ver == 6 || ver == 7 || ver == 8 || ver == 9 || ver == 10) + return FU_MEI_FAMILY_ME; + if (ver == 11 || ver == 12 || ver == 13 || ver == 14 || ver == 15) + return FU_MEI_FAMILY_CSME; + return FU_MEI_FAMILY_UNKNOWN; +} + +static gboolean +fu_mei_parse_fwvers (FuPlugin *plugin, const gchar *fwvers, GError **error) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_auto(GStrv) lines = NULL; + g_auto(GStrv) sections = NULL; + g_auto(GStrv) split = NULL; + + /* we only care about the first version */ + lines = g_strsplit (fwvers, "\n", -1); + if (g_strv_length (lines) < 1) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "expected data, got %s", fwvers); + return FALSE; + } + + /* split platform : version */ + sections = g_strsplit (lines[0], ":", -1); + if (g_strv_length (sections) != 2) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "expected platform:major.minor.micro.build, got %s", + lines[0]); + return FALSE; + } + + /* parse platform and versions */ + priv->vers.platform = fu_common_strtoull (sections[0]); + split = g_strsplit (sections[1], ".", -1); + if (g_strv_length (split) != 4) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "expected major.minor.micro.build, got %s", + sections[1]); + return FALSE; + } + priv->vers.major = fu_common_strtoull (split[0]); + priv->vers.minor = fu_common_strtoull (split[1]); + priv->vers.hotfix = fu_common_strtoull (split[2]); + priv->vers.buildno = fu_common_strtoull (split[3]); + + /* check the AMT version for issues using the data from: + * https://downloadcenter.intel.com/download/28632 */ + priv->family = fu_mei_detect_family (plugin); + if (priv->family == FU_MEI_FAMILY_CSME) + priv->issue = fu_mei_common_is_csme_vulnerable (&priv->vers); + else if (priv->family == FU_MEI_FAMILY_TXE) + priv->issue = fu_mei_common_is_txe_vulnerable (&priv->vers); + else if (priv->family == FU_MEI_FAMILY_SPS) + priv->issue = fu_mei_common_is_sps_vulnerable (&priv->vers); + if (g_getenv ("FWUPD_MEI_VERBOSE") != NULL) { + g_debug ("%s version parsed as %u.%u.%u", + fu_mei_common_family_to_string (priv->family), + priv->vers.major, priv->vers.minor, priv->vers.hotfix); + } + return TRUE; +} + +gboolean +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + const gchar *fwvers; + guint8 buf[4] = { 0x0 }; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* interesting device? */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "pci") != 0) + return TRUE; + + /* open the config */ + fu_udev_device_set_flags (device, FU_UDEV_DEVICE_FLAG_USE_CONFIG); + if (!fu_udev_device_set_physical_id (device, "pci", error)) + return FALSE; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + + /* grab MEI config registers */ + if (!fu_udev_device_pread_full (device, PCI_CFG_HFS_1, buf, sizeof(buf), error)) { + g_prefix_error (error, "could not read HFS1: "); + return FALSE; + } + priv->hfsts1.data = fu_common_read_uint32 (buf, G_LITTLE_ENDIAN); + if (!fu_udev_device_pread_full (device, PCI_CFG_HFS_2, buf, sizeof(buf), error)) { + g_prefix_error (error, "could not read HFS2: "); + return FALSE; + } + priv->hfsts2.data = fu_common_read_uint32 (buf, G_LITTLE_ENDIAN); + if (!fu_udev_device_pread_full (device, PCI_CFG_HFS_3, buf, sizeof(buf), error)) { + g_prefix_error (error, "could not read HFS3: "); + return FALSE; + } + priv->hfsts3.data = fu_common_read_uint32 (buf, G_LITTLE_ENDIAN); + if (!fu_udev_device_pread_full (device, PCI_CFG_HFS_4, buf, sizeof(buf), error)) { + g_prefix_error (error, "could not read HFS4: "); + return FALSE; + } + priv->hfsts4.data = fu_common_read_uint32 (buf, G_LITTLE_ENDIAN); + if (!fu_udev_device_pread_full (device, PCI_CFG_HFS_5, buf, sizeof(buf), error)) { + g_prefix_error (error, "could not read HFS5: "); + return FALSE; + } + priv->hfsts5.data = fu_common_read_uint32 (buf, G_LITTLE_ENDIAN); + if (!fu_udev_device_pread_full (device, PCI_CFG_HFS_6, buf, sizeof(buf), error)) { + g_prefix_error (error, "could not read HFS6: "); + return FALSE; + } + priv->hfsts6.data = fu_common_read_uint32 (buf, G_LITTLE_ENDIAN); + priv->has_device = TRUE; + + /* dump to console */ + if (g_getenv ("FWUPD_PCI_MEI_VERBOSE") != NULL) { + g_autoptr(GString) str = g_string_new (NULL); + fu_mei_hfsts_to_string (plugin, 0, str); + g_debug ("\n%s", str->str); + } + + /* check firmware version */ + fwvers = fu_udev_device_get_sysfs_attr (device, "mei/mei0/fw_ver", NULL); + if (fwvers != NULL) { + if (!fu_mei_parse_fwvers (plugin, fwvers, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_plugin_add_security_attrs_manufacturing_mode (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fwupd_security_attr_add_metadata (attr, "kind", fu_mei_common_family_to_string (priv->family)); + fu_security_attrs_append (attrs, attr); + + /* Manufacturing Mode */ + if (priv->hfsts1.fields.mfg_mode) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); +} + +static void +fu_plugin_add_security_attrs_override_strap (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fwupd_security_attr_add_metadata (attr, "kind", fu_mei_common_family_to_string (priv->family)); + fu_security_attrs_append (attrs, attr); + + /* Flash Descriptor Security Override Strap */ + if (priv->hfsts1.fields.operation_mode == ME_HFS_MODE_OVER_JMPR) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); +} + +static void +fu_plugin_add_security_attrs_bootguard_enabled (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* not supported */ + if (priv->family == FU_MEI_FAMILY_TXE) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + fwupd_security_attr_set_url (attr, "#org.fwupd.hsi.Kernel.IntelBootguard"); + fu_security_attrs_append (attrs, attr); + + /* disabled at runtime? */ + if (priv->hfsts6.fields.boot_guard_disable) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_plugin_add_security_attrs_bootguard_verified (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* not supported */ + if (priv->family == FU_MEI_FAMILY_TXE) + return; + + /* disabled */ + if (priv->hfsts6.fields.boot_guard_disable) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + fwupd_security_attr_set_url (attr, "#org.fwupd.hsi.Kernel.IntelBootguard"); + fu_security_attrs_append (attrs, attr); + + /* measured boot is not sufficient, verified is required */ + if (!priv->hfsts6.fields.verified_boot) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_plugin_add_security_attrs_bootguard_acm (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* not supported */ + if (priv->family == FU_MEI_FAMILY_TXE) + return; + + /* disabled */ + if (priv->hfsts6.fields.boot_guard_disable) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + fu_security_attrs_append (attrs, attr); + + /* ACM protection required */ + if (!priv->hfsts6.fields.force_boot_guard_acm) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_plugin_add_security_attrs_bootguard_policy (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* not supported */ + if (priv->family == FU_MEI_FAMILY_TXE) + return; + + /* disabled */ + if (priv->hfsts6.fields.boot_guard_disable) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL); + fwupd_security_attr_set_url (attr, "#org.fwupd.hsi.Kernel.IntelBootguard"); + fu_security_attrs_append (attrs, attr); + + /* policy must be to immediately shutdown */ + if (priv->hfsts6.fields.error_enforce_policy != ME_HFS_ENFORCEMENT_POLICY_SHUTDOWN_NOW) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_plugin_add_security_attrs_bootguard_otp (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* not supported */ + if (priv->family == FU_MEI_FAMILY_TXE) + return; + + /* disabled */ + if (priv->hfsts6.fields.boot_guard_disable) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + fwupd_security_attr_set_url (attr, "#org.fwupd.hsi.Kernel.IntelBootguard"); + fu_security_attrs_append (attrs, attr); + + /* ensure vendor set the FPF OTP fuse */ + if (!priv->hfsts6.fields.fpf_soc_lock) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_plugin_add_security_attrs_bootguard (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + fu_plugin_add_security_attrs_bootguard_enabled (plugin, attrs); + fu_plugin_add_security_attrs_bootguard_verified (plugin, attrs); + fu_plugin_add_security_attrs_bootguard_acm (plugin, attrs); + fu_plugin_add_security_attrs_bootguard_policy (plugin, attrs); + fu_plugin_add_security_attrs_bootguard_otp (plugin, attrs); +} + +static void +fu_plugin_add_security_attrs_mei_version (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autofree gchar *version = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* format version as string */ + version = g_strdup_printf ("%u:%u.%u.%u.%u", + priv->vers.platform, + priv->vers.major, + priv->vers.minor, + priv->vers.hotfix, + priv->vers.buildno); + if (priv->issue == FU_MEI_ISSUE_UNKNOWN) { + g_warning ("ME family not supported for %s", version); + return; + } + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_MEI_VERSION); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fwupd_security_attr_add_metadata (attr, "kind", fu_mei_common_family_to_string (priv->family)); + fwupd_security_attr_add_metadata (attr, "version", version); + fu_security_attrs_append (attrs, attr); + + /* Flash Descriptor Security Override Strap */ + if (priv->issue == FU_MEI_ISSUE_VULNERABLE) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* success */ + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_VALID); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + + /* only Intel */ + if (!fu_common_is_cpu_intel ()) + return; + if (!priv->has_device) + return; + + fu_plugin_add_security_attrs_manufacturing_mode (plugin, attrs); + fu_plugin_add_security_attrs_override_strap (plugin, attrs); + fu_plugin_add_security_attrs_bootguard (plugin, attrs); + fu_plugin_add_security_attrs_mei_version (plugin, attrs); +} diff --git a/plugins/pci-mei/meson.build b/plugins/pci-mei/meson.build new file mode 100644 index 000000000..4b8c4f01c --- /dev/null +++ b/plugins/pci-mei/meson.build @@ -0,0 +1,28 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginPciMei"'] + +install_data(['pci-mei.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_pci_mei', + fu_hash, + sources : [ + 'fu-plugin-pci-mei.c', + 'fu-mei-common.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/pci-mei/pci-mei.quirk b/plugins/pci-mei/pci-mei.quirk new file mode 100644 index 000000000..4b9112cb4 --- /dev/null +++ b/plugins/pci-mei/pci-mei.quirk @@ -0,0 +1,2 @@ +[DeviceInstanceId=PCI\DRIVER_mei_me] +Plugin = pci_mei diff --git a/plugins/platform-integrity/README.md b/plugins/platform-integrity/README.md new file mode 100644 index 000000000..170e541a9 --- /dev/null +++ b/plugins/platform-integrity/README.md @@ -0,0 +1,12 @@ +Platform Integrity +================== + +Introduction +------------ + +This plugin checks if the system SPI chip is locked. The result will be stored +in an security attribute for HSI. + +External interface access +------------------------- +This plugin requires read access to `/sys/class/platform-integrity` diff --git a/plugins/platform-integrity/fu-plugin-platform-integrity.c b/plugins/platform-integrity/fu-plugin-platform-integrity.c new file mode 100644 index 000000000..876f1cab4 --- /dev/null +++ b/plugins/platform-integrity/fu-plugin-platform-integrity.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + + +#include "fu-plugin-vfuncs.h" +#include "fu-hash.h" + +struct FuPluginData { + gchar *sysfs_path; +}; + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_udev_subsystem (plugin, "platform-integrity"); +} + +void +fu_plugin_destroy (FuPlugin *plugin) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + g_free (priv->sysfs_path); +} + +gboolean +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + + /* interesting device? */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "platform-integrity") != 0) + return TRUE; + + /* we only care about the first instance */ + if (priv->sysfs_path != NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "only one platform-integrity device supported; already using %s", + priv->sysfs_path); + return FALSE; + } + + /* success */ + priv->sysfs_path = g_strdup (fu_udev_device_get_sysfs_path (device)); + return TRUE; +} + +static void +fu_plugin_add_security_attr_bioswe (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autofree gchar *fn = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fwupd_security_attr_add_obsolete (attr, "pci_bcr"); + fu_security_attrs_append (attrs, attr); + + /* load file */ + fn = g_build_filename (priv->sysfs_path, "bioswe", NULL); + if (!g_file_get_contents (fn, &buf, &bufsz, &error_local)) { + g_warning ("could not open %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (g_strcmp0 (buf, "0\n") != 0) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); +} + +static void +fu_plugin_add_security_attr_ble (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autofree gchar *fn = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_SPI_BLE); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fwupd_security_attr_add_obsolete (attr, "pci_bcr"); + fu_security_attrs_append (attrs, attr); + + /* load file */ + fn = g_build_filename (priv->sysfs_path, "biosle", NULL); + if (!g_file_get_contents (fn, &buf, &bufsz, &error_local)) { + g_warning ("could not open %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (g_strcmp0 (buf, "1\n") != 0) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_plugin_add_security_attr_smm_bwp (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autofree gchar *fn = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fwupd_security_attr_add_obsolete (attr, "pci_bcr"); + fu_security_attrs_append (attrs, attr); + + /* load file */ + fn = g_build_filename (priv->sysfs_path, "smm_bioswp", NULL); + if (!g_file_get_contents (fn, &buf, &bufsz, &error_local)) { + g_warning ("could not open %s: %s", fn, error_local->message); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (g_strcmp0 (buf, "1\n") != 0) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + + /* only when the kernel module is available */ + if (priv->sysfs_path == NULL) + return; + + /* look for the three files in sysfs */ + fu_plugin_add_security_attr_bioswe (plugin, attrs); + fu_plugin_add_security_attr_ble (plugin, attrs); + fu_plugin_add_security_attr_smm_bwp (plugin, attrs); +} diff --git a/plugins/platform-integrity/fwupd-platform-integrity.conf b/plugins/platform-integrity/fwupd-platform-integrity.conf new file mode 100644 index 000000000..2fd69d6ea --- /dev/null +++ b/plugins/platform-integrity/fwupd-platform-integrity.conf @@ -0,0 +1 @@ +platform-integrity diff --git a/plugins/platform-integrity/meson.build b/plugins/platform-integrity/meson.build new file mode 100644 index 000000000..6f1c4bc51 --- /dev/null +++ b/plugins/platform-integrity/meson.build @@ -0,0 +1,33 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginPlatformIntegrity"'] + +install_data([ + 'platform-integrity.quirk', + ], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +install_data(['fwupd-platform-integrity.conf'], + install_dir: join_paths(sysconfdir, 'modules-load.d') +) + +shared_module('fu_plugin_platform_integrity', + fu_hash, + sources : [ + 'fu-plugin-platform-integrity.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/platform-integrity/platform-integrity.quirk b/plugins/platform-integrity/platform-integrity.quirk new file mode 100644 index 000000000..c24cf0c0e --- /dev/null +++ b/plugins/platform-integrity/platform-integrity.quirk @@ -0,0 +1,3 @@ +# match all devices with this udev subsystem +[DeviceInstanceId=PLATFORM-INTEGRITY] +Plugin = platform_integrity diff --git a/plugins/redfish/README.md b/plugins/redfish/README.md index 55b3cca0b..b46dd525a 100644 --- a/plugins/redfish/README.md +++ b/plugins/redfish/README.md @@ -73,3 +73,7 @@ and verify the uri with or $ curl -k https://192.168.0.133:443/redfish/v1/ + +External interface access +------------------------- +This requires HTTP access to a given URL. diff --git a/plugins/redfish/fu-redfish-client.c b/plugins/redfish/fu-redfish-client.c index a570d5459..9b708f975 100644 --- a/plugins/redfish/fu-redfish-client.c +++ b/plugins/redfish/fu-redfish-client.c @@ -14,6 +14,7 @@ #include "fwupd-enums.h" #include "fu-device.h" +#include "fu-efivar.h" #include "fu-redfish-client.h" #include "fu-redfish-common.h" @@ -361,9 +362,9 @@ fu_redfish_client_set_uefi_credentials (FuRedfishClient *self, GError **error) g_autoptr(GBytes) userpass = NULL; /* get the uint32 specifying if there are EFI variables set */ - indications = fu_redfish_common_get_evivar_raw (REDFISH_EFI_INFORMATION_GUID, - REDFISH_EFI_INFORMATION_INDICATIONS, - error); + indications = fu_efivar_get_data_bytes (REDFISH_EFI_INFORMATION_GUID, + REDFISH_EFI_INFORMATION_INDICATIONS, + NULL, error); if (indications == NULL) return FALSE; if (g_bytes_get_size (indications) != 4) { @@ -385,9 +386,9 @@ fu_redfish_client_set_uefi_credentials (FuRedfishClient *self, GError **error) } /* read the correct EFI var for runtime */ - userpass = fu_redfish_common_get_evivar_raw (REDFISH_EFI_INFORMATION_GUID, - REDFISH_EFI_INFORMATION_OS_CREDENTIALS, - error); + userpass = fu_efivar_get_data_bytes (REDFISH_EFI_INFORMATION_GUID, + REDFISH_EFI_INFORMATION_OS_CREDENTIALS, + NULL, error); if (userpass == NULL) return FALSE; diff --git a/plugins/redfish/fu-redfish-common.c b/plugins/redfish/fu-redfish-common.c index 812f3e7de..98311e62e 100644 --- a/plugins/redfish/fu-redfish-common.c +++ b/plugins/redfish/fu-redfish-common.c @@ -10,26 +10,6 @@ #include "fu-redfish-common.h" -GBytes * -fu_redfish_common_get_evivar_raw (efi_guid_t guid, const gchar *name, GError **error) -{ - gsize sz = 0; - guint32 attribs = 0; - guint8 *data = NULL; - - if (efi_get_variable (guid, name, &data, &sz, &attribs) < 0) { - g_autofree gchar *guid_str = NULL; - efi_guid_to_str (&guid, &guid_str); - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "failed to get efivar for %s %s", - guid_str, name); - return NULL; - } - return g_bytes_new_take (data, sz); -} - gchar * fu_redfish_common_buffer_to_ipv4 (const guint8 *buffer) { diff --git a/plugins/redfish/fu-redfish-common.h b/plugins/redfish/fu-redfish-common.h index baaee9f08..a7b40d467 100644 --- a/plugins/redfish/fu-redfish-common.h +++ b/plugins/redfish/fu-redfish-common.h @@ -7,7 +7,6 @@ #pragma once #include -#include /* SMBIOS */ #define REDFISH_SMBIOS_TABLE_TYPE 0x42 @@ -29,7 +28,7 @@ #define REDFISH_IP_ADDRESS_FORMAT_V6 0x02 /* EFI */ -#define REDFISH_EFI_INFORMATION_GUID EFI_GUID(0x16faa37e,0x4b6a,0x4891,0x9028,0x24,0x2d,0xe6,0x5a,0x3b,0x70) +#define REDFISH_EFI_INFORMATION_GUID "16faa37e-4b6a-4891-9028-242de65a3b70" #define REDFISH_EFI_INFORMATION_INDICATIONS "RedfishIndications" #define REDFISH_EFI_INFORMATION_FW_CREDENTIALS "RedfishFWCredentials" @@ -39,8 +38,5 @@ #define REDFISH_EFI_INDICATIONS_OS_CREDENTIALS 0x00000002 /* shared */ -GBytes *fu_redfish_common_get_evivar_raw (efi_guid_t guid, - const gchar *name, - GError **error); gchar *fu_redfish_common_buffer_to_ipv4 (const guint8 *buffer); gchar *fu_redfish_common_buffer_to_ipv6 (const guint8 *buffer); diff --git a/plugins/redfish/meson.build b/plugins/redfish/meson.build index 25fc5c7d3..927627918 100644 --- a/plugins/redfish/meson.build +++ b/plugins/redfish/meson.build @@ -21,7 +21,6 @@ shared_module('fu_plugin_redfish', c_args : cargs, dependencies : [ plugin_deps, - efivar, libjsonglib, ], ) @@ -46,14 +45,15 @@ if get_option('tests') ], dependencies : [ plugin_deps, - efivar, libjsonglib, ], link_with : [ fwupd, fwupdplugin, ], - c_args : cargs + c_args : cargs, + install : true, + install_dir : installed_test_bindir, ) - test('redfish-self-test', e) + test('redfish-self-test', e) # added to installed-tests endif diff --git a/plugins/rts54hid/README.md b/plugins/rts54hid/README.md index 75148df39..9cb52a604 100644 --- a/plugins/rts54hid/README.md +++ b/plugins/rts54hid/README.md @@ -44,6 +44,10 @@ This plugin uses the following plugin-specific quirks: | Quirk | Description | Minimum fwupd version | |------------------------|---------------------------------------------|-----------------------| -| `Rts54SlaveAddr` | The slave address of a child module. | 1.1.3 | +| `Rts54TargetAddr` | The target address of a child module. | 1.1.3 | | `Rts54I2cSpeed` | The I2C speed to operate at (0, 1, 2). | 1.1.3 | | `Rts54RegisterAddrLen` | The I2C register address length of commands | 1.1.3 | + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/rts54hid/fu-rts54hid-common.h b/plugins/rts54hid/fu-rts54hid-common.h index d62e7d26a..7e8e1c2da 100644 --- a/plugins/rts54hid/fu-rts54hid-common.h +++ b/plugins/rts54hid/fu-rts54hid-common.h @@ -15,7 +15,7 @@ #define FU_RTS54HID_CMD_BUFFER_OFFSET_DATA 0x40 typedef struct __attribute__ ((packed)) { - guint8 slave_addr; + guint8 target_addr; guint8 data_sz; guint8 speed; } FuRts54HidI2cParameters; diff --git a/plugins/rts54hid/fu-rts54hid-device.c b/plugins/rts54hid/fu-rts54hid-device.c index 0592dd425..1a3c9c0bc 100644 --- a/plugins/rts54hid/fu-rts54hid-device.c +++ b/plugins/rts54hid/fu-rts54hid-device.c @@ -132,7 +132,7 @@ fu_rts54hid_device_verify_update_fw (FuRts54HidDevice *self, GError **error) FU_HID_DEVICE_FLAG_NONE, error)) return FALSE; - g_usleep (4 * G_USEC_PER_SEC); + fu_device_sleep_with_progress (FU_DEVICE (self), 4); /* seconds */ if (!fu_hid_device_get_report (FU_HID_DEVICE (self), 0x0, buf, sizeof(buf), FU_RTS54HID_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, diff --git a/plugins/rts54hid/fu-rts54hid-module.c b/plugins/rts54hid/fu-rts54hid-module.c index 902a6d576..cb817a20c 100644 --- a/plugins/rts54hid/fu-rts54hid-module.c +++ b/plugins/rts54hid/fu-rts54hid-module.c @@ -17,7 +17,7 @@ struct _FuRts54HidModule { FuDevice parent_instance; - guint8 slave_addr; + guint8 target_addr; guint8 i2c_speed; guint8 register_addr_len; }; @@ -28,7 +28,7 @@ static void fu_rts54hid_module_to_string (FuDevice *module, guint idt, GString *str) { FuRts54HidModule *self = FU_RTS54HID_MODULE (module); - fu_common_string_append_kx (str, idt, "SlaveAddr", self->slave_addr); + fu_common_string_append_kx (str, idt, "TargetAddr", self->target_addr); fu_common_string_append_kx (str, idt, "I2cSpeed", self->i2c_speed); fu_common_string_append_kx (str, idt, "RegisterAddrLen", self->register_addr_len); } @@ -59,7 +59,7 @@ fu_rts54hid_module_i2c_write (FuRts54HidModule *self, .ext = FU_RTS54HID_EXT_I2C_WRITE, .dwregaddr = 0, .bufferlen = GUINT16_TO_LE (data_sz), - .parameters_i2c = {.slave_addr = self->slave_addr, + .parameters_i2c = {.target_addr = self->target_addr, .data_sz = self->register_addr_len, .speed = self->i2c_speed | 0x80}, }; @@ -83,7 +83,7 @@ fu_rts54hid_module_i2c_write (FuRts54HidModule *self, FU_RTS54HID_DEVICE_TIMEOUT * 2, FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error (error, "failed to write i2c @%04x: ", self->slave_addr); + g_prefix_error (error, "failed to write i2c @%04x: ", self->target_addr); return FALSE; } return TRUE; @@ -102,7 +102,7 @@ fu_rts54hid_module_i2c_read (FuRts54HidModule *self, .ext = FU_RTS54HID_EXT_I2C_READ, .dwregaddr = GUINT32_TO_LE (cmd), .bufferlen = GUINT16_TO_LE (data_sz), - .parameters_i2c = {.slave_addr = self->slave_addr, + .parameters_i2c = {.target_addr = self->target_addr, .data_sz = self->register_addr_len, .speed = self->i2c_speed | 0x80}, }; @@ -123,7 +123,7 @@ fu_rts54hid_module_i2c_read (FuRts54HidModule *self, FU_RTS54HID_DEVICE_TIMEOUT * 2, FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error (error, "failed to write i2c @%04x: ", self->slave_addr); + g_prefix_error (error, "failed to write i2c @%04x: ", self->target_addr); return FALSE; } if (!fu_hid_device_get_report (FU_HID_DEVICE (parent), 0x0, buf, sizeof(buf), @@ -144,17 +144,17 @@ fu_rts54hid_module_set_quirk_kv (FuDevice *device, { FuRts54HidModule *self = FU_RTS54HID_MODULE (device); - /* load slave address from quirks */ - if (g_strcmp0 (key, "Rts54SlaveAddr") == 0) { + /* load target address from quirks */ + if (g_strcmp0 (key, "Rts54TargetAddr") == 0) { guint64 tmp = fu_common_strtoull (value); if (tmp <= 0xff) { - self->slave_addr = tmp; + self->target_addr = tmp; return TRUE; } g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, - "invalid slave address"); + "invalid target address"); return FALSE; } diff --git a/plugins/rts54hid/rts54hid.quirk b/plugins/rts54hid/rts54hid.quirk index 28d502c39..a740c5593 100644 --- a/plugins/rts54hid/rts54hid.quirk +++ b/plugins/rts54hid/rts54hid.quirk @@ -11,6 +11,6 @@ Plugin = rts54hid Name = HDMI Converter Flags = updatable FirmwareSize = 0x20000 -Rts54SlaveAddr = 0x00 +Rts54TargetAddr = 0x00 Rts54I2cSpeed = 0x00 Rts54RegisterAddrLen = 0x04 diff --git a/plugins/rts54hub/README.md b/plugins/rts54hub/README.md index be4818da4..c0c231d51 100644 --- a/plugins/rts54hub/README.md +++ b/plugins/rts54hub/README.md @@ -33,3 +33,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, in this instance set to `USB:0x0BDA` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/solokey/README.md b/plugins/solokey/README.md index 5f72e8d76..b32a0a48f 100644 --- a/plugins/solokey/README.md +++ b/plugins/solokey/README.md @@ -31,3 +31,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, in this instance set to `USB:0x0483` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/solokey/fu-solokey-device.c b/plugins/solokey/fu-solokey-device.c index 13d85a1e8..ef329030d 100644 --- a/plugins/solokey/fu-solokey-device.c +++ b/plugins/solokey/fu-solokey-device.c @@ -407,7 +407,6 @@ fu_solokey_device_prepare_firmware (FuDevice *device, GError **error) { g_autoptr(FuFirmware) firmware = fu_solokey_firmware_new (); - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; return g_steal_pointer (&firmware); diff --git a/plugins/steelseries/README.md b/plugins/steelseries/README.md index dd3c745f5..9ea70cf40 100644 --- a/plugins/steelseries/README.md +++ b/plugins/steelseries/README.md @@ -21,3 +21,7 @@ Vendor ID Security ------------------ The device is not upgradable and thus requires no vendor ID set. + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/superio/README.md b/plugins/superio/README.md index 79c97efd8..8ef8f91c9 100644 --- a/plugins/superio/README.md +++ b/plugins/superio/README.md @@ -27,3 +27,7 @@ Vendor ID Security ------------------ The vendor ID is set from the baseboard vendor, for example `DMI:Star Labs` + +External interface access +------------------------- +This plugin requires access to raw system memory via `inb`/`outb`. diff --git a/plugins/superio/fu-superio-it89-device.c b/plugins/superio/fu-superio-it89-device.c index f038780c2..03e8c3297 100644 --- a/plugins/superio/fu-superio-it89-device.c +++ b/plugins/superio/fu-superio-it89-device.c @@ -417,18 +417,35 @@ fu_plugin_superio_fix_signature (FuSuperioDevice *self, GBytes *fw, GError **err return g_bytes_new_take (g_steal_pointer (&buf2), sz); } +static GBytes * +fu_superio_it89_device_dump_firmware (FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); + guint64 fwsize = fu_device_get_firmware_size_min (device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* require detach -> attach */ + locker = fu_device_locker_new_full (device, + (FuDeviceLockerFunc) fu_device_detach, + (FuDeviceLockerFunc) fu_device_attach, + error); + if (locker == NULL) + return NULL; + + fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ); + return fu_superio_it89_device_read_addr (self, 0x0, fwsize, + fu_superio_it89_device_progress_cb, + error); +} + static FuFirmware * fu_superio_it89_device_read_firmware (FuDevice *device, GError **error) { FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); - guint64 fwsize = fu_device_get_firmware_size_min (device); g_autoptr(GBytes) blob = NULL; g_autoptr(GBytes) fw = NULL; - fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ); - blob = fu_superio_it89_device_read_addr (self, 0x0, fwsize, - fu_superio_it89_device_progress_cb, - error); + blob = fu_superio_it89_device_dump_firmware (device, error); fw = fu_plugin_superio_fix_signature (self, blob, error); return fu_firmware_new_from_bytes (fw); } @@ -681,6 +698,7 @@ fu_superio_it89_device_class_init (FuSuperioIt89DeviceClass *klass) klass_device->attach = fu_superio_it89_device_attach; klass_device->detach = fu_superio_it89_device_detach; klass_device->read_firmware = fu_superio_it89_device_read_firmware; + klass_device->dump_firmware = fu_superio_it89_device_dump_firmware; klass_device->write_firmware = fu_superio_it89_device_write_firmware; klass_superio_device->setup = fu_superio_it89_device_setup; } diff --git a/plugins/superio/superio.quirk b/plugins/superio/superio.quirk index 13194972c..5ff98f138 100644 --- a/plugins/superio/superio.quirk +++ b/plugins/superio/superio.quirk @@ -6,6 +6,11 @@ SuperioChipsets=IT8587 [HwId=f00d8c4e-dce2-51c3-89d6-6cbc5fc5cdbb] SuperioChipsets=IT8587 +# Star Lite Mk III +[HwId=23fe120a-f9e1-590d-9e6b-4a2a9274b293] +SuperioChipsets=IT8987 +InstallDuration=20 + # Star LabTop Mk IV [HwId=6ed215ec-75d5-54e9-9c60-7708b325b904] SuperioChipsets=IT8987 diff --git a/plugins/synaptics-cxaudio/README.md b/plugins/synaptics-cxaudio/README.md index ac32c8886..1bd7d2c15 100644 --- a/plugins/synaptics-cxaudio/README.md +++ b/plugins/synaptics-cxaudio/README.md @@ -47,3 +47,7 @@ This plugin uses the following plugin-specific quirks: | `IsSoftwareResetSupported` | If the chip supports self-reset | 1.3.2 | | `EepromPatchValidAddr` | Address of patch location #1 | 1.3.2 | | `EepromPatch2ValidAddr` | Address of patch location #2 | 1.3.2 | + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c index 76a8f09b7..ebe6268d7 100644 --- a/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c +++ b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c @@ -67,7 +67,7 @@ fu_synaptics_cxaudio_device_output_report (FuSynapticsCxaudioDevice *self, return fu_hid_device_set_report (FU_HID_DEVICE (self), buf[0], buf, bufsz, FU_SYNAPTICS_CXAUDIO_USB_TIMEOUT, - FU_HID_DEVICE_FLAG_NONE, + FU_HID_DEVICE_FLAG_RETRY_FAILURE, error); } @@ -550,8 +550,6 @@ fu_synaptics_cxaudio_device_prepare_firmware (FuDevice *device, FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE (device); guint32 chip_id_base; g_autoptr(FuFirmware) firmware = fu_synaptics_cxaudio_firmware_new (); - - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; chip_id_base = fu_synaptics_cxaudio_firmware_get_devtype (FU_SYNAPTICS_CXAUDIO_FIRMWARE (firmware)); @@ -762,6 +760,7 @@ fu_synaptics_cxaudio_device_init (FuSynapticsCxaudioDevice *self) fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_PLAIN); fu_device_set_install_duration (FU_DEVICE (self), 3); /* seconds */ fu_device_set_protocol (FU_DEVICE (self), "com.synaptics.cxaudio"); + fu_device_retry_set_delay (FU_DEVICE (self), 100); /* ms */ fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); } diff --git a/plugins/synaptics-cxaudio/synaptics-cxaudio.quirk b/plugins/synaptics-cxaudio/synaptics-cxaudio.quirk index 3d6fef156..9aec0dbc1 100644 --- a/plugins/synaptics-cxaudio/synaptics-cxaudio.quirk +++ b/plugins/synaptics-cxaudio/synaptics-cxaudio.quirk @@ -1,7 +1,7 @@ # ThinkPad TBT3-TR Gen 2 dock [DeviceInstanceId=USB\VID_17EF&PID_3083] Guid = SYNAPTICS_CXAUDIO\CX2098X -ParentGuid = TBT-01081720 +ParentGuid = USB\VID_17EF&PID_307F&HUB_0006 # ThinkPad TBT3-MS Gen 2 dock [DeviceInstanceId=USB\VID_17EF&PID_3092] diff --git a/plugins/synaptics-mst/README.md b/plugins/synaptics-mst/README.md index a821455f3..1a9ecfa32 100644 --- a/plugins/synaptics-mst/README.md +++ b/plugins/synaptics-mst/README.md @@ -84,3 +84,7 @@ Here is a sample list of systems known to support them however: * Latitude Rugged 5414 * Latitude Rugged 7214 * Latitude Rugged 7414 + +External interface access +------------------------- +This plugin requires read/write access to `/dev/drm_dp_aux*`. diff --git a/plugins/synaptics-mst/fu-self-test.c b/plugins/synaptics-mst/fu-self-test.c index 100ab6474..12e55a9a0 100644 --- a/plugins/synaptics-mst/fu-self-test.c +++ b/plugins/synaptics-mst/fu-self-test.c @@ -53,15 +53,17 @@ static void fu_plugin_synaptics_mst_none_func (void) { gboolean ret; + const gchar *ci = g_getenv ("CI_NETWORK"); g_autoptr(FuPlugin) plugin = fu_plugin_new (); g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); g_autofree gchar *pluginfn = NULL; + g_autofree gchar *filename = NULL; g_signal_connect (plugin, "device-added", G_CALLBACK (_plugin_device_added_cb), &devices); - pluginfn = g_build_filename (PLUGINBUILDDIR, + pluginfn = g_test_build_filename (G_TEST_BUILT, "libfu_plugin_synaptics_mst." G_MODULE_SUFFIX, NULL); ret = fu_plugin_open (plugin, pluginfn, &error); @@ -75,7 +77,12 @@ fu_plugin_synaptics_mst_none_func (void) g_assert_no_error (error); g_assert (ret); - _test_add_fake_devices_from_dir (plugin, SOURCEDIR "/tests/no_devices"); + filename = g_test_build_filename (G_TEST_DIST, "tests", "no_devices", NULL); + if (!g_file_test (filename, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing no_devices"); + return; + } + _test_add_fake_devices_from_dir (plugin, filename); g_assert_cmpint (devices->len, ==, 0); } @@ -84,15 +91,17 @@ static void fu_plugin_synaptics_mst_tb16_func (void) { gboolean ret; + const gchar *ci = g_getenv ("CI_NETWORK"); g_autoptr(FuPlugin) plugin = fu_plugin_new (); g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); g_autofree gchar *pluginfn = NULL; + g_autofree gchar *filename = NULL; g_signal_connect (plugin, "device-added", G_CALLBACK (_plugin_device_added_cb), &devices); - pluginfn = g_build_filename (PLUGINBUILDDIR, + pluginfn = g_test_build_filename (G_TEST_BUILT, "libfu_plugin_synaptics_mst." G_MODULE_SUFFIX, NULL); ret = fu_plugin_open (plugin, pluginfn, &error); @@ -106,7 +115,12 @@ fu_plugin_synaptics_mst_tb16_func (void) g_assert_no_error (error); g_assert (ret); - _test_add_fake_devices_from_dir (plugin, SOURCEDIR "/tests/tb16_dock"); + filename = g_test_build_filename (G_TEST_DIST, "tests", "tb16_dock", NULL); + if (!g_file_test (filename, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing tb16_dock"); + return; + } + _test_add_fake_devices_from_dir (plugin, filename); for (guint i = 0; i < devices->len; i++) { FuDevice *device = g_ptr_array_index (devices, i); g_autofree gchar *tmp = fu_device_to_string (device); diff --git a/plugins/synaptics-mst/fu-synaptics-mst-device.c b/plugins/synaptics-mst/fu-synaptics-mst-device.c index c36eedb67..23e3eb59e 100644 --- a/plugins/synaptics-mst/fu-synaptics-mst-device.c +++ b/plugins/synaptics-mst/fu-synaptics-mst-device.c @@ -776,7 +776,7 @@ fu_synaptics_mst_device_prepare_firmware (FuDevice *device, FuSynapticsMstDevice *self = FU_SYNAPTICS_MST_DEVICE (device); /* check firmware and board ID match */ - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0 && !fu_device_has_custom_flag (device, "ignore-board-id")) { const guint8 *buf; gsize len; diff --git a/plugins/synaptics-mst/meson.build b/plugins/synaptics-mst/meson.build index 120f405fc..faeb070c1 100644 --- a/plugins/synaptics-mst/meson.build +++ b/plugins/synaptics-mst/meson.build @@ -32,8 +32,10 @@ shared_module('fu_plugin_synaptics_mst', ) if get_option('tests') - cargs += '-DPLUGINBUILDDIR="' + meson.current_build_dir() + '"' - cargs += '-DSOURCEDIR="' + meson.current_source_dir() + '"' + testdatadirs = environment() + testdatadirs.set('G_TEST_SRCDIR', meson.current_source_dir()) + testdatadirs.set('G_TEST_BUILDDIR', meson.current_build_dir()) + testdatadirs.set('FWUPD_LOCALSTATEDIR', '/tmp/fwupd-self-test/var') e = executable( 'synaptics-mst-self-test', fu_hash, @@ -60,7 +62,8 @@ if get_option('tests') c_args : [ cargs, ], + install : true, + install_dir : installed_test_bindir, ) - test('synaptics-mst-self-test', e, - env: ['FWUPD_LOCALSTATEDIR=/tmp/fwupd-self-test/var']) + test('synaptics-mst-self-test', e, env: testdatadirs) endif diff --git a/plugins/synaptics-mst/tests/no_devices/drm_dp_aux0 b/plugins/synaptics-mst/tests/no_devices/drm_dp_aux0 deleted file mode 100644 index ffc2b1e80..000000000 Binary files a/plugins/synaptics-mst/tests/no_devices/drm_dp_aux0 and /dev/null differ diff --git a/plugins/synaptics-mst/tests/no_devices/drm_dp_aux1 b/plugins/synaptics-mst/tests/no_devices/drm_dp_aux1 deleted file mode 100644 index e69de29bb..000000000 diff --git a/plugins/synaptics-mst/tests/no_devices/drm_dp_aux2 b/plugins/synaptics-mst/tests/no_devices/drm_dp_aux2 deleted file mode 100644 index e69de29bb..000000000 diff --git a/plugins/synaptics-mst/tests/tb16_dock/drm_dp_aux0 b/plugins/synaptics-mst/tests/tb16_dock/drm_dp_aux0 deleted file mode 100644 index ffc2b1e80..000000000 Binary files a/plugins/synaptics-mst/tests/tb16_dock/drm_dp_aux0 and /dev/null differ diff --git a/plugins/synaptics-mst/tests/tb16_dock/drm_dp_aux1 b/plugins/synaptics-mst/tests/tb16_dock/drm_dp_aux1 deleted file mode 100644 index e132d259e..000000000 Binary files a/plugins/synaptics-mst/tests/tb16_dock/drm_dp_aux1 and /dev/null differ diff --git a/plugins/synaptics-mst/tests/tb16_dock/drm_dp_aux2 b/plugins/synaptics-mst/tests/tb16_dock/drm_dp_aux2 deleted file mode 100644 index 1c60857e3..000000000 Binary files a/plugins/synaptics-mst/tests/tb16_dock/drm_dp_aux2 and /dev/null differ diff --git a/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux0 b/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux0 deleted file mode 100644 index 13b878fe4..000000000 Binary files a/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux0 and /dev/null differ diff --git a/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux1 b/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux1 deleted file mode 100644 index ea7ff51db..000000000 Binary files a/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux1 and /dev/null differ diff --git a/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux1_eeprom b/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux1_eeprom deleted file mode 100644 index 45d72a52c..000000000 --- a/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux1_eeprom +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux2 b/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux2 deleted file mode 100644 index 87e4ad5d7..000000000 Binary files a/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux2 and /dev/null differ diff --git a/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux2_eeprom b/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux2_eeprom deleted file mode 100644 index 45d72a52c..000000000 --- a/plugins/synaptics-mst/tests/tb16_dock/remote/drm_dp_aux2_eeprom +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/plugins/synaptics-prometheus/README.md b/plugins/synaptics-prometheus/README.md index c448680eb..993ff031c 100644 --- a/plugins/synaptics-prometheus/README.md +++ b/plugins/synaptics-prometheus/README.md @@ -31,3 +31,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, in this instance set to `USB:0x06CB` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/synaptics-prometheus/data/test.pkg b/plugins/synaptics-prometheus/data/test.pkg deleted file mode 100644 index c0997f23a..000000000 Binary files a/plugins/synaptics-prometheus/data/test.pkg and /dev/null differ diff --git a/plugins/synaptics-prometheus/fu-self-test.c b/plugins/synaptics-prometheus/fu-self-test.c index a9b4df72d..67fe99bf1 100644 --- a/plugins/synaptics-prometheus/fu-self-test.c +++ b/plugins/synaptics-prometheus/fu-self-test.c @@ -16,6 +16,7 @@ static void fu_test_synaprom_firmware_func (void) { + const gchar *ci = g_getenv ("CI_NETWORK"); const guint8 *buf; gboolean ret; gsize sz = 0; @@ -28,7 +29,11 @@ fu_test_synaprom_firmware_func (void) g_autoptr(FuFirmware) firmware2 = NULL; g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new (); - filename = g_build_filename (TESTDATADIR, "test.pkg", NULL); + filename = g_test_build_filename (G_TEST_DIST, "tests", "test.pkg", NULL); + if (!g_file_test (filename, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing test.pkg"); + return; + } fw = fu_common_get_contents_bytes (filename, &error); g_assert_no_error (error); g_assert_nonnull (fw); diff --git a/plugins/synaptics-prometheus/fu-synaprom-common.c b/plugins/synaptics-prometheus/fu-synaprom-common.c index 80ac679a3..3d9036877 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-common.c +++ b/plugins/synaptics-prometheus/fu-synaprom-common.c @@ -41,7 +41,7 @@ GByteArray * fu_synaprom_reply_new (gsize cmdlen) { GByteArray *blob = g_byte_array_new (); - g_byte_array_set_size (blob, cmdlen); + fu_byte_array_set_size (blob, cmdlen); return blob; } diff --git a/plugins/synaptics-prometheus/fu-synaprom-config.c b/plugins/synaptics-prometheus/fu-synaprom-config.c index 371d8d809..74f9a5fca 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-config.c +++ b/plugins/synaptics-prometheus/fu-synaprom-config.c @@ -133,7 +133,6 @@ fu_synaprom_config_prepare_firmware (FuDevice *device, guint32 id2; /* parse the firmware */ - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; @@ -151,7 +150,7 @@ fu_synaprom_config_prepare_firmware (FuDevice *device, memcpy (&hdr, g_bytes_get_data (blob, NULL), sizeof(hdr)); product = GUINT32_FROM_LE(hdr.product); if (product != FU_SYNAPROM_PRODUCT_PROMETHEUS) { - if (flags & FWUPD_INSTALL_FLAG_FORCE) { + if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) { g_warning ("CFG metadata not compatible, " "got 0x%02x expected 0x%02x", product, (guint) FU_SYNAPROM_PRODUCT_PROMETHEUS); @@ -168,7 +167,7 @@ fu_synaprom_config_prepare_firmware (FuDevice *device, id1 = GUINT32_FROM_LE(hdr.id1); id2 = GUINT32_FROM_LE(hdr.id2); if (id1 != self->configid1 || id2 != self->configid2) { - if (flags & FWUPD_INSTALL_FLAG_FORCE) { + if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) { g_warning ("CFG version not compatible, " "got %u:%u expected %u:%u", id1, id2, self->configid1, self->configid2); diff --git a/plugins/synaptics-prometheus/fu-synaprom-device.c b/plugins/synaptics-prometheus/fu-synaprom-device.c index 299ebde27..26ba80ead 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-device.c +++ b/plugins/synaptics-prometheus/fu-synaprom-device.c @@ -252,7 +252,6 @@ fu_synaprom_device_prepare_fw (FuDevice *device, g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new (); /* parse the firmware */ - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; @@ -270,7 +269,7 @@ fu_synaprom_device_prepare_fw (FuDevice *device, memcpy (&hdr, g_bytes_get_data (blob, NULL), sizeof(hdr)); product = GUINT32_FROM_LE(hdr.product); if (product != FU_SYNAPROM_PRODUCT_PROMETHEUS) { - if (flags & FWUPD_INSTALL_FLAG_FORCE) { + if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) { g_warning ("MFW metadata not compatible, " "got 0x%02x expected 0x%02x", product, (guint) FU_SYNAPROM_PRODUCT_PROMETHEUS); diff --git a/plugins/synaptics-prometheus/meson.build b/plugins/synaptics-prometheus/meson.build index 7e2f72fad..fed45214c 100644 --- a/plugins/synaptics-prometheus/meson.build +++ b/plugins/synaptics-prometheus/meson.build @@ -31,8 +31,9 @@ shared_module('fu_plugin_synaptics_prometheus', ) if get_option('tests') - testdatadir = join_paths(meson.current_source_dir(), 'data') - cargs += '-DTESTDATADIR="' + testdatadir + '"' + testdatadirs = environment() + testdatadirs.set('G_TEST_SRCDIR', meson.current_source_dir()) + testdatadirs.set('G_TEST_BUILDDIR', meson.current_build_dir()) e = executable( 'synaptics-prometheus-self-test', fu_hash, @@ -55,9 +56,11 @@ if get_option('tests') fwupd, fwupdplugin, ], - c_args : cargs + c_args : cargs, + install : true, + install_dir : installed_test_bindir, ) - test('synaptics-prometheus-self-test', e) + test('synaptics-prometheus-self-test', e, env : testdatadirs) # added to installed-tests # for fuzzing executable( @@ -73,6 +76,7 @@ if get_option('tests') ], dependencies : [ gio, + libxmlb, ], link_with : [ fwupd, diff --git a/plugins/synaptics-prometheus/synaptics-prometheus.quirk b/plugins/synaptics-prometheus/synaptics-prometheus.quirk index 95170f9ee..3b23bce20 100644 --- a/plugins/synaptics-prometheus/synaptics-prometheus.quirk +++ b/plugins/synaptics-prometheus/synaptics-prometheus.quirk @@ -13,3 +13,16 @@ InstallDuration = 2 [DeviceInstanceId=USB\VID_06CB&PID_00E9] Plugin = synaptics_prometheus InstallDuration = 2 + +[DeviceInstanceId=USB\VID_06CB&PID_00C2] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[DeviceInstanceId=USB\VID_06CB&PID_00F9] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[DeviceInstanceId=USB\VID_06CB&PID_00FC] +Plugin = synaptics_prometheus +InstallDuration = 2 + diff --git a/plugins/synaptics-rmi/README.md b/plugins/synaptics-rmi/README.md index e2c8fdddc..d3e6ea919 100644 --- a/plugins/synaptics-rmi/README.md +++ b/plugins/synaptics-rmi/README.md @@ -31,3 +31,7 @@ a proprietary (but docucumented) file format. This plugin supports the following protocol ID: * com.synaptics.rmi + +External interface access +------------------------- +This plugin requires ioctl access to `HIDIOCSFEATURE` and `HIDIOCGFEATURE`. diff --git a/plugins/synaptics-rmi/fu-dump.c b/plugins/synaptics-rmi/fu-dump.c index 2a0d1170d..ffc5a5adf 100644 --- a/plugins/synaptics-rmi/fu-dump.c +++ b/plugins/synaptics-rmi/fu-dump.c @@ -19,7 +19,7 @@ fu_dump_parse (const gchar *filename, GError **error) if (!g_file_get_contents (filename, &data, &len, error)) return FALSE; blob = g_bytes_new_take (data, len); - if (!fu_firmware_parse (firmware, blob, FWUPD_INSTALL_FLAG_FORCE, error)) + if (!fu_firmware_parse (firmware, blob, FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM, error)) return FALSE; str = fu_firmware_to_string (firmware); g_print ("%s", str); diff --git a/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c b/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c index 23b14dc01..dba3cad69 100644 --- a/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c +++ b/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c @@ -349,7 +349,7 @@ fu_synaptics_rmi_firmware_parse (FuFirmware *firmware, /* verify checksum */ self->checksum = fu_common_read_uint32 (data + RMI_IMG_CHECKSUM_OFFSET, G_LITTLE_ENDIAN); checksum_calculated = fu_synaptics_rmi_generate_checksum (data + 4, sz - 4); - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { if (self->checksum != checksum_calculated) { g_set_error (error, FWUPD_ERROR, @@ -403,7 +403,7 @@ fu_synaptics_rmi_firmware_generate_v0x (void) GByteArray *buf = g_byte_array_new (); /* create empty block */ - g_byte_array_set_size (buf, RMI_IMG_FW_OFFSET + 0x4 + 0x4); + fu_byte_array_set_size (buf, RMI_IMG_FW_OFFSET + 0x4 + 0x4); buf->data[RMI_IMG_IO_OFFSET] = 0x0; /* no build_id or package_id */ buf->data[RMI_IMG_BOOTLOADER_VERSION_OFFSET] = 0x2; /* not hierarchical */ memcpy (buf->data + RMI_IMG_PRODUCT_ID_OFFSET, "Example", 7); @@ -436,7 +436,7 @@ fu_synaptics_rmi_firmware_generate_v10 (void) }; /* create empty block */ - g_byte_array_set_size (buf, RMI_IMG_FW_OFFSET + 0x48); + fu_byte_array_set_size (buf, RMI_IMG_FW_OFFSET + 0x48); buf->data[RMI_IMG_IO_OFFSET] = 0x1; buf->data[RMI_IMG_BOOTLOADER_VERSION_OFFSET] = 16; /* hierarchical */ memcpy (buf->data + RMI_IMG_PRODUCT_ID_OFFSET, "Example", 7); diff --git a/plugins/synaptics-rmi/meson.build b/plugins/synaptics-rmi/meson.build index 3e71f911c..3272fad87 100644 --- a/plugins/synaptics-rmi/meson.build +++ b/plugins/synaptics-rmi/meson.build @@ -48,6 +48,7 @@ if get_option('tests') ], dependencies : [ gio, + libxmlb, ], link_with : [ fwupd, diff --git a/plugins/test/README.md b/plugins/test/README.md index 3ab0229c8..4691cce0a 100644 --- a/plugins/test/README.md +++ b/plugins/test/README.md @@ -16,3 +16,7 @@ Vendor ID Security ------------------ The fake device is only for local testing and thus requires no vendor ID set. + +External interface access +------------------------- +This plugin requires no extra access. diff --git a/plugins/test/fu-plugin-test.c b/plugins/test/fu-plugin-test.c index a2d90d030..2380ecb56 100644 --- a/plugins/test/fu-plugin-test.c +++ b/plugins/test/fu-plugin-test.c @@ -38,7 +38,7 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) fu_device_set_name (device, "Integrated_Webcam(TM)"); fu_device_add_icon (device, "preferences-desktop-keyboard"); fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag (device, FWUPD_DEVICE_FLAG_CAN_VERIFY); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); fu_device_set_protocol (device, "com.acme.test"); fu_device_set_summary (device, "A fake webcam"); fu_device_set_vendor (device, "ACME Corp."); diff --git a/plugins/thelio-io/README.md b/plugins/thelio-io/README.md index f85e8735e..812f62571 100644 --- a/plugins/thelio-io/README.md +++ b/plugins/thelio-io/README.md @@ -20,3 +20,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, in this instance set to `USB:0x1209` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/thelio-io/fu-thelio-io-device.c b/plugins/thelio-io/fu-thelio-io-device.c index 04e49f2c1..78c8b7e99 100644 --- a/plugins/thelio-io/fu-thelio-io-device.c +++ b/plugins/thelio-io/fu-thelio-io-device.c @@ -98,6 +98,7 @@ fu_thelio_io_device_init (FuThelioIoDevice *self) fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_protocol (FU_DEVICE (self), "org.usb.dfu"); } static void diff --git a/plugins/thunderbolt/README.md b/plugins/thunderbolt/README.md index 914257cb3..4823a2ac9 100644 --- a/plugins/thunderbolt/README.md +++ b/plugins/thunderbolt/README.md @@ -37,6 +37,13 @@ used for systems with multiple host controllers to disambiguiate between control * `TBT-$(vid)$(pid)-native-controller$(num)` +For retimers the only GUID created is as follows: +* `TBT-$(vid)$(pid)-retimer$index` + +The retimer index is oriented around the physical connection within +the machine. It is important as multiple controllers may otherwise +identify identically. + Vendor ID Security ------------------ @@ -85,3 +92,7 @@ DROM and exposed in the relevant sysfs attributes. If the controller is in native enumeration mode, the string "-native" is added at the end so the format is "TBT-vvvvdddd-native". + +External interface access +------------------------- +This plugin requires read/write access to `/sys/bus/thunderbolt`. diff --git a/plugins/thunderbolt/fu-self-test.c b/plugins/thunderbolt/fu-self-test.c index 90d2105f8..def08a110 100644 --- a/plugins/thunderbolt/fu-self-test.c +++ b/plugins/thunderbolt/fu-self-test.c @@ -26,6 +26,7 @@ #include "fu-plugin-private.h" #include "fu-thunderbolt-firmware.h" #include "fu-thunderbolt-firmware-update.h" +#include "fu-udev-device-private.h" static gchar * udev_mock_add_domain (UMockdevTestbed *bed, int id) @@ -395,7 +396,7 @@ mock_tree_attach_device (gpointer user_data) "device", dev->id, "vendor", "042", "vendor_name", "GNOME.org", - "authorized", "0", + "authorized", "1", "nvm_authenticate", authenticate, "nvm_version", tree->nvm_version, "unique_id", tree->uuid, @@ -891,8 +892,7 @@ fu_thunderbolt_gudev_uevent_cb (GUdevClient *gudev_client, const gchar *uuid = g_udev_device_get_sysfs_attr (udev_device, "unique_id"); MockTree *target = (MockTree *) mock_tree_find_uuid (tt->tree, uuid); g_assert_nonnull (target); - fu_plugin_runner_udev_device_changed (tt->plugin, FU_UDEV_DEVICE (target->fu_device), - &error_local); + fu_udev_device_emit_changed (FU_UDEV_DEVICE (target->fu_device)); return; } } diff --git a/plugins/thunderbolt/fu-thunderbolt-device.c b/plugins/thunderbolt/fu-thunderbolt-device.c index 67a0cfe11..5fa22bdb2 100644 --- a/plugins/thunderbolt/fu-thunderbolt-device.c +++ b/plugins/thunderbolt/fu-thunderbolt-device.c @@ -20,10 +20,17 @@ #include "fu-thunderbolt-device.h" #include "fu-thunderbolt-firmware.h" #include "fu-thunderbolt-firmware-update.h" +#include "fu-udev-device-private.h" + +typedef enum { + FU_THUNDERBOLT_DEVICE_TYPE_DEVICE_CONTROLLER, + FU_THUNDERBOLT_DEVICE_TYPE_HOST_CONTROLLER, + FU_THUNDERBOLT_DEVICE_TYPE_RETIMER +} FuThunderboltDeviceType; struct _FuThunderboltDevice { FuUdevDevice parent_instance; - gboolean host; + FuThunderboltDeviceType device_type; gboolean safe_mode; gboolean is_native; guint16 gen; @@ -103,6 +110,42 @@ fu_thunderbolt_device_read_status_block (FuThunderboltDevice *self, GError **err return TRUE; } +static gboolean +fu_thunderbolt_device_check_authorized (FuThunderboltDevice *self, GError **error) +{ + guint64 status; + g_autofree gchar *attribute = NULL; + const gchar *update_error = NULL; + /* read directly from file to prevent udev caching */ + g_autofree gchar *safe_path = g_build_path ("/", self->devpath, "authorized", NULL); + + if (!g_file_test (safe_path, G_FILE_TEST_EXISTS)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing authorized attribute"); + return FALSE; + } + + if (!g_file_get_contents (safe_path, &attribute, NULL, error)) + return FALSE; + status = g_ascii_strtoull (attribute, NULL, 16); + if (status == G_MAXUINT64 && errno == ERANGE) { + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errno), + "failed to read 'authorized: %s", + g_strerror (errno)); + return FALSE; + } + if (status == 1 || status == 2) + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + else + update_error = "Not authorized"; + fu_device_set_update_error (FU_DEVICE (self), update_error); + + return TRUE; +} + static gboolean fu_thunderbolt_device_can_update (FuThunderboltDevice *self) { @@ -120,7 +163,7 @@ fu_thunderbolt_device_can_update (FuThunderboltDevice *self) } static gboolean -fu_thunderbolt_device_get_version (FuThunderboltDevice *self) +fu_thunderbolt_device_get_version (FuThunderboltDevice *self, GError **error) { g_auto(GStrv) split = NULL; g_autofree gchar *version_raw = NULL; @@ -128,6 +171,14 @@ fu_thunderbolt_device_get_version (FuThunderboltDevice *self) /* read directly from file to prevent udev caching */ g_autofree gchar *safe_path = g_build_path ("/", self->devpath, "nvm_version", NULL); + if (!g_file_test (safe_path, G_FILE_TEST_EXISTS)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing nvm_version attribute"); + return FALSE; + } + for (guint i = 0; i < 50; i++) { g_autoptr(GError) error_local = NULL; /* glib can't return a properly mapped -ENODATA but the @@ -141,18 +192,26 @@ fu_thunderbolt_device_get_version (FuThunderboltDevice *self) break; } - if (version_raw == NULL) + if (version_raw == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to read NVM"); return FALSE; + } split = g_strsplit (version_raw, ".", -1); - if (g_strv_length (split) != 2) + if (g_strv_length (split) != 2) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid nvm_version format: %s", version_raw); return FALSE; + } version = g_strdup_printf ("%02x.%02x", (guint) g_ascii_strtoull (split[0], NULL, 16), (guint) g_ascii_strtoull (split[1], NULL, 16)); fu_device_set_version (FU_DEVICE (self), version); - g_debug ("setting version to %s", version); - g_debug ("path is %s", self->devpath); return TRUE; } @@ -160,7 +219,7 @@ static void fu_thunderbolt_device_check_safe_mode (FuThunderboltDevice *self) { /* failed to read, for host check for safe mode */ - if (!self->host || self->gen >= 4) + if (self->device_type != FU_THUNDERBOLT_DEVICE_TYPE_DEVICE_CONTROLLER) return; g_warning ("%s is in safe mode -- VID/DID will " "need to be set by another plugin", @@ -171,11 +230,31 @@ fu_thunderbolt_device_check_safe_mode (FuThunderboltDevice *self) fu_device_set_metadata_boolean (FU_DEVICE (self), FU_DEVICE_METADATA_TBT_IS_SAFE_MODE, TRUE); } +static const gchar* +fu_thunderbolt_device_type_to_string (FuThunderboltDevice *self) +{ + if (self->device_type == FU_THUNDERBOLT_DEVICE_TYPE_HOST_CONTROLLER) { + if (self->gen >= 4) + return "USB4 host controller"; + else + return "Thunderbolt host controller"; + } + if (self->device_type == FU_THUNDERBOLT_DEVICE_TYPE_DEVICE_CONTROLLER) { + if (self->gen >= 4) + return "USB4 device controller"; + else + return "Thunderbolt device controller"; + } + if (self->device_type == FU_THUNDERBOLT_DEVICE_TYPE_RETIMER) + return "USB4 Retimer"; + return "Unknown"; +} + static void fu_thunderbolt_device_to_string (FuUdevDevice *device, guint idt, GString *str) { FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE (device); - fu_common_string_append_kb (str, idt, "Host Controller", self->host); + fu_common_string_append_kv (str, idt, "Device Type", fu_thunderbolt_device_type_to_string (self)); fu_common_string_append_kb (str, idt, "Safe Mode", self->safe_mode); fu_common_string_append_kb (str, idt, "Native mode", self->is_native); fu_common_string_append_ku (str, idt, "Generation", self->gen); @@ -185,16 +264,28 @@ fu_thunderbolt_device_to_string (FuUdevDevice *device, guint idt, GString *str) static gboolean fu_thunderbolt_device_probe (FuUdevDevice *device, GError **error) { - const gchar *tmp = fu_udev_device_get_sysfs_attr (device, "unique_id", NULL); - /* most likely the domain itself, ignore */ - if (tmp == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "thunderbolt domain not used"); + FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE (device); + const gchar *tmp = fu_udev_device_get_devtype (device); + + /* device */ + if (g_strcmp0 (tmp, "thunderbolt_device") == 0) { + tmp = fu_udev_device_get_sysfs_attr (device, "unique_id", NULL); + if (tmp != NULL) + fu_device_set_physical_id (FU_DEVICE (device), tmp); + /* retimer */ + } else if (g_strcmp0 (tmp, "thunderbolt_retimer") == 0) { + self->device_type = FU_THUNDERBOLT_DEVICE_TYPE_RETIMER; + tmp = g_path_get_basename (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device))); + if (tmp != NULL) + fu_device_set_physical_id (FU_DEVICE (device), tmp); + /* domain or unsupported */ + } else { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s not used", tmp); return FALSE; } - fu_device_set_physical_id (FU_DEVICE (device), tmp); return TRUE; } @@ -231,7 +322,7 @@ fu_thunderbolt_device_get_attr_uint16 (FuThunderboltDevice *self, } static gboolean -fu_thunderbolt_device_setup (FuDevice *device, GError **error) +fu_thunderbolt_device_setup_controller (FuDevice *device, GError **error) { FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE (device); const gchar *tmp = NULL; @@ -240,9 +331,6 @@ fu_thunderbolt_device_setup (FuDevice *device, GError **error) g_autoptr(GError) error_gen = NULL; g_autofree gchar *parent_name = fu_udev_device_get_parent_name (FU_UDEV_DEVICE (self)); - self->devpath = g_strdup (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device))); - fu_device_set_metadata (device, "sysfs-path", self->devpath); - /* these may be missing on ICL or later */ vid = fu_udev_device_get_vendor (FU_UDEV_DEVICE (self)); if (vid == 0x0) @@ -263,7 +351,7 @@ fu_thunderbolt_device_setup (FuDevice *device, GError **error) /* determine if host controller or not */ if (parent_name != NULL && g_str_has_prefix (parent_name, "domain")) { - self->host = TRUE; + self->device_type = FU_THUNDERBOLT_DEVICE_TYPE_HOST_CONTROLLER; fu_device_add_flag (device, FWUPD_DEVICE_FLAG_INTERNAL); fu_device_set_summary (device, "Unmatched performance for high-speed I/O"); } else { @@ -271,12 +359,8 @@ fu_thunderbolt_device_setup (FuDevice *device, GError **error) } /* set the controller name */ - if (tmp == NULL) { - if (self->gen == 4) - tmp = "USB4 Controller"; - else - tmp = "Thunderbolt Controller"; - } + if (tmp == NULL) + tmp = fu_thunderbolt_device_type_to_string (self); fu_device_set_name (device, tmp); /* set vendor string */ @@ -285,8 +369,7 @@ fu_thunderbolt_device_setup (FuDevice *device, GError **error) return FALSE; fu_device_set_vendor (device, tmp); - /* try to read the version */ - if (!fu_thunderbolt_device_get_version (self)) + if (fu_device_get_version (device) == NULL) fu_thunderbolt_device_check_safe_mode (self); if (self->safe_mode) { @@ -299,7 +382,8 @@ fu_thunderbolt_device_setup (FuDevice *device, GError **error) g_autofree gchar *domain = g_path_get_basename (self->devpath); /* USB4 controllers don't have a concept of legacy vs native * so don't try to read a native attribute from their NVM */ - if (self->host && self->gen < 4) { + if (self->device_type == FU_THUNDERBOLT_DEVICE_TYPE_HOST_CONTROLLER + && self->gen < 4) { domain_id = g_strdup_printf ("TBT-%04x%04x%s-controller%s", (guint) vid, (guint) did, @@ -312,8 +396,12 @@ fu_thunderbolt_device_setup (FuDevice *device, GError **error) (guint) vid, (guint) did, self->is_native ? "-native" : ""); - fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag (device, FWUPD_DEVICE_FLAG_DUAL_IMAGE); + + /* check if device is authorized */ + if (!fu_thunderbolt_device_check_authorized (self, error)) + return FALSE; + } else { device_id = g_strdup ("TBT-fixed"); } @@ -331,14 +419,97 @@ fu_thunderbolt_device_setup (FuDevice *device, GError **error) fu_device_add_flag (device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); /* forces the device to write to authenticate on disconnect attribute */ fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_SKIPS_RESTART); - } else { - self->auth_method = "nvm_authenticate"; + /* control the order of activation (less relevant; install too though) */ + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST); } - /* success */ return TRUE; } +static gboolean +fu_thunderbolt_device_setup_retimer (FuDevice *device, GError **error) +{ + FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE (device); + guint16 did; + guint16 vid; + g_autofree gchar *instance = NULL; + + /* as defined in PCIe 4.0 spec */ + fu_device_set_summary (device, "A physical layer protocol-aware, software-transparent extension device " + "that forms two separate electrical link segments"); + fu_device_set_name (device, fu_thunderbolt_device_type_to_string (self)); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_INTERNAL); + vid = fu_udev_device_get_vendor (FU_UDEV_DEVICE (self)); + if (vid == 0x0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing vendor id"); + return FALSE; + } + + did = fu_udev_device_get_model (FU_UDEV_DEVICE (self)); + if (did == 0x0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing device id"); + return FALSE; + + } + + instance = g_strdup_printf ("TBT-%04x%04x-retimer%s", + (guint) vid, + (guint) did, + fu_device_get_physical_id (device)); + fu_device_add_instance_id (device, instance); + + /* hardcoded for now: + * 1. unsure if ID_VENDOR_FROM_DATABASE works in this instance + * 2. we don't recognize anyone else yet + */ + if (fu_device_get_vendor (device) == NULL) + fu_device_set_vendor (device, "Intel"); + + return TRUE; +} + +static gboolean +fu_thunderbolt_device_setup (FuDevice *device, GError **error) +{ + FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE (device); + g_autoptr(GError) error_version = NULL; + + self->devpath = g_strdup (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device))); + + /* try to read the version */ + if (!fu_thunderbolt_device_get_version (self, &error_version)) { + if (g_error_matches (error_version, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_propagate_error (error, g_steal_pointer (&error_version)); + return FALSE; + } + g_debug ("%s", error_version->message); + } + + /* default behavior */ + self->auth_method = "nvm_authenticate"; + + /* configure differences between retimer and controller */ + if (self->device_type == FU_THUNDERBOLT_DEVICE_TYPE_RETIMER) + return fu_thunderbolt_device_setup_retimer (device, error); + return fu_thunderbolt_device_setup_controller (device, error); +} + +static gboolean +fu_thunderbolt_device_activate (FuDevice *device, GError **error) +{ + FuUdevDevice *udev = FU_UDEV_DEVICE (device); + + return fu_udev_device_write_sysfs (udev, "nvm_authenticate", "1", error); +} + static gboolean fu_thunderbolt_device_authenticate (FuDevice *device, GError **error) { @@ -394,8 +565,13 @@ static gboolean fu_thunderbolt_device_rescan (FuDevice *device, GError **error) { FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE (device); + + /* refresh updatability */ + if (!fu_thunderbolt_device_check_authorized (self, error)) + return FALSE; + /* refresh the version */ - return fu_thunderbolt_device_get_version (self); + return fu_thunderbolt_device_get_version (self, error); } static gboolean @@ -508,7 +684,7 @@ fu_thunderbolt_device_prepare_firmware (FuDevice *device, fu_thunderbolt_firmware_get_device_id (firmware_old)); return NULL; } - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { if (fu_thunderbolt_firmware_get_model_id (FU_THUNDERBOLT_FIRMWARE (firmware)) != fu_thunderbolt_firmware_get_model_id (firmware_old)) { g_set_error (error, @@ -618,7 +794,7 @@ fu_thunderbolt_device_class_init (FuThunderboltDeviceClass *klass) FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuUdevDeviceClass *klass_udev_device = FU_UDEV_DEVICE_CLASS (klass); object_class->finalize = fu_thunderbolt_device_finalize; - klass_device->activate = fu_thunderbolt_device_authenticate; + klass_device->activate = fu_thunderbolt_device_activate; klass_udev_device->to_string = fu_thunderbolt_device_to_string; klass_device->setup = fu_thunderbolt_device_setup; klass_device->prepare_firmware = fu_thunderbolt_device_prepare_firmware; diff --git a/plugins/tpm-eventlog/README.md b/plugins/tpm-eventlog/README.md index 603b21995..af62cb55a 100644 --- a/plugins/tpm-eventlog/README.md +++ b/plugins/tpm-eventlog/README.md @@ -15,3 +15,7 @@ Vendor ID Security ------------------ The device is not upgradable and thus requires no vendor ID set. + +External interface access +------------------------- +This plugin requires read only access to `/sys/kernel/security/tpm0/binary_bios_measurements`. diff --git a/plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c b/plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c index 110b90592..c03713611 100644 --- a/plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c +++ b/plugins/tpm-eventlog/fu-plugin-tpm-eventlog.c @@ -10,11 +10,12 @@ #include "fu-plugin-vfuncs.h" #include "fu-tpm-eventlog-device.h" -#include "fu-efivar.h" struct FuPluginData { GPtrArray *pcr0s; - gboolean secure_boot_problem; + gboolean has_tpm_device; + gboolean has_uefi_device; + gboolean reconstructed; }; void @@ -22,6 +23,7 @@ fu_plugin_init (FuPlugin *plugin) { fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_BEFORE, "uefi"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "tpm"); fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); } @@ -42,16 +44,9 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) g_autofree gchar *str = NULL; g_autofree guint8 *buf = NULL; g_autoptr(FuTpmEventlogDevice) dev = NULL; - g_autoptr(GError) error_local = NULL; - if (!g_file_get_contents (fn, (gchar **) &buf, &bufsz, &error_local)) { - if (fu_efivar_supported (NULL) && !fu_efivar_secure_boot_enabled ()) { - data->secure_boot_problem = TRUE; - return TRUE; - } - g_propagate_error (error, g_steal_pointer (&error_local)); + if (!g_file_get_contents (fn, (gchar **) &buf, &bufsz, error)) return FALSE; - } if (bufsz == 0) { g_set_error (error, FWUPD_ERROR, @@ -76,45 +71,92 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) /* add optional report metadata */ str = fu_tpm_eventlog_device_report_metadata (dev); - g_debug ("using TPM event log report data of:\n%s", str); fu_plugin_add_report_metadata (plugin, "TpmEventLog", str); fu_plugin_device_add (plugin, FU_DEVICE (dev)); return TRUE; } -void -fu_plugin_device_registered (FuPlugin *plugin, FuDevice *device) +static void +fu_plugin_device_registered_tpm (FuPlugin *plugin, FuDevice *device) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + data->has_tpm_device = TRUE; +} + +static void +fu_plugin_device_registered_uefi (FuPlugin *plugin, FuDevice *device) { FuPluginData *data = fu_plugin_get_data (plugin); GPtrArray *checksums; - /* only care about UEFI devices from ESRT */ - if (g_strcmp0 (fu_device_get_plugin (device), "uefi") != 0) - return; - /* only the system-firmware device gets checksums */ checksums = fu_device_get_checksums (device); if (checksums->len == 0) return; - - if (data->secure_boot_problem) { - g_warning ("Platform firmware measurement unavailable. Secure boot is disabled in " - "BIOS setup, enabling it may fix this issue"); - return; - } + data->has_uefi_device = TRUE; for (guint i = 0; i < checksums->len; i++) { const gchar *checksum = g_ptr_array_index (checksums, i); + data->reconstructed = FALSE; for (guint j = 0; j < data->pcr0s->len; j++) { const gchar *checksum_tmp = g_ptr_array_index (data->pcr0s, j); + /* skip unless same algorithm */ + if (strlen (checksum) != strlen (checksum_tmp)) + continue; if (g_strcmp0 (checksum, checksum_tmp) == 0) { - g_debug ("TPM reconstructed event log matched PCR0 reading"); - return; + data->reconstructed = TRUE; + break; } } + /* check at least one reconstruction for this algorithm */ + if (!data->reconstructed) + return; + } +} + +void +fu_plugin_device_registered (FuPlugin *plugin, FuDevice *device) +{ + /* only care about UEFI devices from ESRT */ + if (g_strcmp0 (fu_device_get_plugin (device), "uefi") == 0) { + fu_plugin_device_registered_uefi (plugin, device); + return; } - /* urgh, this is unexpected */ - g_warning ("TPM PCR0 differs from reconstruction, " - "please see https://github.com/fwupd/fwupd/wiki/TPM-PCR0-differs-from-reconstruction"); + /* detect the system TPM device */ + if (g_strcmp0 (fu_device_get_plugin (device), "tpm") == 0) { + fu_plugin_device_registered_tpm (plugin, device); + return; + } +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* no TPM device */ + if (!data->has_tpm_device) + return; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + fu_security_attrs_append (attrs, attr); + + /* check reconstructed to PCR0 */ + if (fu_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED) || !data->has_uefi_device) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + if (!data->reconstructed) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_VALID); } diff --git a/plugins/tpm-eventlog/fu-self-test.c b/plugins/tpm-eventlog/fu-self-test.c index 0e238f63f..f0d9ed25f 100644 --- a/plugins/tpm-eventlog/fu-self-test.c +++ b/plugins/tpm-eventlog/fu-self-test.c @@ -14,6 +14,7 @@ static void fu_test_tpm_eventlog_parse_v1_func (void) { + const gchar *ci = g_getenv ("CI_NETWORK"); const gchar *tmp; gboolean ret; gsize bufsz = 0; @@ -24,7 +25,11 @@ fu_test_tpm_eventlog_parse_v1_func (void) g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) pcr0s = NULL; - fn = g_build_filename (TESTDATADIR, "binary_bios_measurements-v1", NULL); + fn = g_test_build_filename (G_TEST_DIST, "tests", "binary_bios_measurements-v1", NULL); + if (!g_file_test (fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing binary_bios_measurements-v1"); + return; + } ret = g_file_get_contents (fn, (gchar **) &buf, &bufsz, &error); g_assert_no_error (error); g_assert_true (ret); @@ -48,6 +53,7 @@ fu_test_tpm_eventlog_parse_v1_func (void) static void fu_test_tpm_eventlog_parse_v2_func (void) { + const gchar *ci = g_getenv ("CI_NETWORK"); const gchar *tmp; gboolean ret; gsize bufsz = 0; @@ -58,7 +64,11 @@ fu_test_tpm_eventlog_parse_v2_func (void) g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) pcr0s = NULL; - fn = g_build_filename (TESTDATADIR, "binary_bios_measurements-v2", NULL); + fn = g_test_build_filename (G_TEST_DIST, "tests", "binary_bios_measurements-v2", NULL); + if (!g_file_test (fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip ("Missing binary_bios_measurements-v2"); + return; + } ret = g_file_get_contents (fn, (gchar **) &buf, &bufsz, &error); g_assert_no_error (error); g_assert_true (ret); @@ -75,9 +85,11 @@ fu_test_tpm_eventlog_parse_v2_func (void) pcr0s = fu_tpm_eventlog_device_get_checksums (dev, 0, &error); g_assert_no_error (error); g_assert_nonnull (pcr0s); - g_assert_cmpint (pcr0s->len, ==, 1); + g_assert_cmpint (pcr0s->len, ==, 2); tmp = g_ptr_array_index (pcr0s, 0); g_assert_cmpstr (tmp, ==, "ebead4b31c7c49e193c440cd6ee90bc1b61a3ca6"); + tmp = g_ptr_array_index (pcr0s, 1); + g_assert_cmpstr (tmp, ==, "6d9fed68092cfb91c9552bcb7879e75e1df36efd407af67690dc3389a5722fab"); } int diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-parser.c b/plugins/tpm-eventlog/fu-tpm-eventlog-parser.c index 1f133b7c0..5b6e48eee 100644 --- a/plugins/tpm-eventlog/fu-tpm-eventlog-parser.c +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-parser.c @@ -110,6 +110,7 @@ fu_tpm_eventlog_parser_parse_blob_v2 (const guint8 *buf, gsize bufsz, for (guint i = 0; i < digestcnt; i++) { guint16 alg_type = 0; guint32 alg_size = 0; + g_autofree guint8 *digest = NULL; /* get checksum type */ if (!fu_common_read_uint16_safe (buf, bufsz, idx, @@ -127,22 +128,19 @@ fu_tpm_eventlog_parser_parse_blob_v2 (const guint8 *buf, gsize bufsz, /* build checksum */ idx += sizeof(alg_type); - if (alg_type == TPM2_ALG_SHA1 || - flags & FU_TPM_EVENTLOG_PARSER_FLAG_ALL_ALGS) { - g_autofree guint8 *digest = g_malloc0 (alg_size); - /* copy hash */ - if (!fu_memcpy_safe (digest, alg_size, 0x0, /* dst */ - buf, bufsz, idx, /* src */ - alg_size, error)) - return NULL; + /* copy hash */ + digest = g_malloc0 (alg_size); + if (!fu_memcpy_safe (digest, alg_size, 0x0, /* dst */ + buf, bufsz, idx, /* src */ + alg_size, error)) + return NULL; - /* save this for analysis */ - if (alg_type == TPM2_ALG_SHA1) - checksum_sha1 = g_bytes_new_take (g_steal_pointer (&digest), alg_size); - else if (alg_type == TPM2_ALG_SHA256) - checksum_sha256 = g_bytes_new_take (g_steal_pointer (&digest), alg_size); - } + /* save this for analysis */ + if (alg_type == TPM2_ALG_SHA1) + checksum_sha1 = g_bytes_new_take (g_steal_pointer (&digest), alg_size); + else if (alg_type == TPM2_ALG_SHA256) + checksum_sha256 = g_bytes_new_take (g_steal_pointer (&digest), alg_size); /* next block */ idx += alg_size; diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog-parser.h b/plugins/tpm-eventlog/fu-tpm-eventlog-parser.h index 74c3ccd12..61a1f4d65 100644 --- a/plugins/tpm-eventlog/fu-tpm-eventlog-parser.h +++ b/plugins/tpm-eventlog/fu-tpm-eventlog-parser.h @@ -13,7 +13,6 @@ typedef enum { FU_TPM_EVENTLOG_PARSER_FLAG_NONE = 0, FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS = 1 << 0, - FU_TPM_EVENTLOG_PARSER_FLAG_ALL_ALGS = 1 << 1, FU_TPM_EVENTLOG_PARSER_FLAG_LAST } FuTpmEventlogParserFlags; diff --git a/plugins/tpm-eventlog/fu-tpm-eventlog.c b/plugins/tpm-eventlog/fu-tpm-eventlog.c index ac66f0869..95f88679b 100644 --- a/plugins/tpm-eventlog/fu-tpm-eventlog.c +++ b/plugins/tpm-eventlog/fu-tpm-eventlog.c @@ -15,6 +15,7 @@ #include #include +#include "fwupd-common-private.h" #include "fu-tpm-eventlog-parser.h" static gint @@ -36,12 +37,12 @@ fu_tmp_eventlog_process (const gchar *fn, gint pcr, GError **error) g_autofree guint8 *buf = NULL; g_autoptr(GPtrArray) items = NULL; g_autoptr(GString) str = g_string_new (NULL); + gint max_pcr = 0; /* parse this */ if (!g_file_get_contents (fn, (gchar **) &buf, &bufsz, error)) return FALSE; items = fu_tpm_eventlog_parser_new (buf, bufsz, - FU_TPM_EVENTLOG_PARSER_FLAG_ALL_ALGS | FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS, error); if (items == NULL) @@ -50,20 +51,32 @@ fu_tmp_eventlog_process (const gchar *fn, gint pcr, GError **error) for (guint i = 0; i < items->len; i++) { FuTpmEventlogItem *item = g_ptr_array_index (items, i); + if (item->pcr > max_pcr) + max_pcr = item->pcr; if (pcr >= 0 && item->pcr != pcr) continue; fu_tpm_eventlog_item_to_string (item, 0, str); g_string_append (str, "\n"); } - fu_common_string_append_kv (str, 0, "PCRs", NULL); - for (guint8 i = 0; i < 10; i++) { + if (pcr > max_pcr) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "invalid PCR specified: %d", pcr); + return FALSE; + } + fu_common_string_append_kv (str, 0, "Reconstructed PCRs", NULL); + for (guint8 i = 0; i <= max_pcr; i++) { g_autoptr(GPtrArray) pcrs = fu_tpm_eventlog_calc_checksums (items, i, NULL); if (pcrs == NULL) continue; for (guint j = 0; j < pcrs->len; j++) { const gchar *csum = g_ptr_array_index (pcrs, j); - g_autofree gchar *title = g_strdup_printf ("%x", i); - fu_common_string_append_kv (str, 1, title, csum); + g_autofree gchar *title = NULL; + g_autofree gchar *pretty = NULL; + if (pcr >= 0 && i != (guint) pcr) + continue; + title = g_strdup_printf ("PCR %x", i); + pretty = fwupd_checksum_format_for_display (csum); + fu_common_string_append_kv (str, 1, title, pretty); } } @@ -108,7 +121,7 @@ main (int argc, char *argv[]) g_option_context_add_main_entries (context, options, NULL); g_option_context_set_description (context, "This tool will read and parse the TPM event log " - "from the system firwmare."); + "from the system firmware."); if (!g_option_context_parse (context, &argc, &argv, &error)) { /* TRANSLATORS: the user didn't read the man page */ g_print ("%s: %s\n", _("Failed to parse arguments"), diff --git a/plugins/tpm-eventlog/meson.build b/plugins/tpm-eventlog/meson.build index 6f155a463..3f7e7aa76 100644 --- a/plugins/tpm-eventlog/meson.build +++ b/plugins/tpm-eventlog/meson.build @@ -27,8 +27,9 @@ shared_module('fu_plugin_tpm_eventlog', ) if get_option('tests') - testdatadir = join_paths(meson.current_source_dir(), 'tests') - cargs += '-DTESTDATADIR="' + testdatadir + '"' + testdatadirs = environment() + testdatadirs.set('G_TEST_SRCDIR', meson.current_source_dir()) + testdatadirs.set('G_TEST_BUILDDIR', meson.current_build_dir()) e = executable( 'tpm-eventlog-self-test', fu_hash, @@ -53,7 +54,7 @@ if get_option('tests') ], c_args : cargs ) - test('tpm-eventlog-self-test', e) + test('tpm-eventlog-self-test', e, env : testdatadirs) # added to installed-tests endif fwupdtpmevlog = executable( diff --git a/plugins/tpm-eventlog/tests/binary_bios_measurements-v1 b/plugins/tpm-eventlog/tests/binary_bios_measurements-v1 deleted file mode 100644 index 0618456e9..000000000 Binary files a/plugins/tpm-eventlog/tests/binary_bios_measurements-v1 and /dev/null differ diff --git a/plugins/tpm-eventlog/tests/binary_bios_measurements-v2 b/plugins/tpm-eventlog/tests/binary_bios_measurements-v2 deleted file mode 100644 index 5aacd4197..000000000 Binary files a/plugins/tpm-eventlog/tests/binary_bios_measurements-v2 and /dev/null differ diff --git a/plugins/tpm/README.md b/plugins/tpm/README.md index f37561bd7..32fceeee3 100644 --- a/plugins/tpm/README.md +++ b/plugins/tpm/README.md @@ -30,3 +30,7 @@ Vendor ID Security ------------------ The device is not upgradable and thus requires no vendor ID set. + +External interface access +------------------------- +This plugin uses the tpm2-tss library to access the TPM. It requires access to `/sys/class/tpm`. diff --git a/plugins/tpm/fu-plugin-tpm.c b/plugins/tpm/fu-plugin-tpm.c index fec3842eb..f3227b53d 100644 --- a/plugins/tpm/fu-plugin-tpm.c +++ b/plugins/tpm/fu-plugin-tpm.c @@ -11,10 +11,55 @@ #include "fu-tpm-device.h" +struct FuPluginData { + gboolean has_tpm; + gboolean has_tpm_v20; +}; + void fu_plugin_init (FuPlugin *plugin) { + fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_add_udev_subsystem (plugin, "tpm"); fu_plugin_set_device_gtype (plugin, FU_TYPE_TPM_DEVICE); } + +void +fu_plugin_device_added (FuPlugin *plugin, FuDevice *dev) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + const gchar *family = fu_tpm_device_get_family (FU_TPM_DEVICE (dev)); + + data->has_tpm = TRUE; + if (g_strcmp0 (family, "2.0") == 0) + data->has_tpm_v20 = TRUE; + fu_plugin_add_report_metadata (plugin, "TpmFamily", family); +} + +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPluginData *data = fu_plugin_get_data (plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fu_security_attrs_append (attrs, attr); + + /* check exists, and in v2.0 mode */ + if (!data->has_tpm) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + if (!data->has_tpm_v20) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_FOUND); +} diff --git a/plugins/tpm/fu-tpm-device.c b/plugins/tpm/fu-tpm-device.c index fd3a95abd..90dde789e 100644 --- a/plugins/tpm/fu-tpm-device.c +++ b/plugins/tpm/fu-tpm-device.c @@ -12,6 +12,7 @@ struct _FuTpmDevice { FuUdevDevice parent_instance; + gchar *family; }; G_DEFINE_TYPE (FuTpmDevice, fu_tpm_device, FU_TYPE_UDEV_DEVICE) @@ -22,6 +23,12 @@ static void Esys_Finalize_autoptr_cleanup (ESYS_CONTEXT *esys_context) } G_DEFINE_AUTOPTR_CLEANUP_FUNC (ESYS_CONTEXT, Esys_Finalize_autoptr_cleanup) +const gchar * +fu_tpm_device_get_family (FuTpmDevice *self) +{ + return self->family; +} + static gboolean fu_tpm_device_probe (FuUdevDevice *device, GError **error) { @@ -134,6 +141,7 @@ fu_tpm_device_convert_manufacturer (const gchar *manufacturer) static gboolean fu_tpm_device_setup (FuDevice *device, GError **error) { + FuTpmDevice *self = FU_TPM_DEVICE (device); FwupdVersionFormat verfmt; TSS2_RC rc; const gchar *tmp; @@ -141,7 +149,6 @@ fu_tpm_device_setup (FuDevice *device, GError **error) guint32 version1 = 0; guint32 version2 = 0; guint64 version_raw; - g_autofree gchar *family = NULL; g_autofree gchar *id1 = NULL; g_autofree gchar *id2 = NULL; g_autofree gchar *id3 = NULL; @@ -171,8 +178,8 @@ fu_tpm_device_setup (FuDevice *device, GError **error) } /* lookup guaranteed details from TPM */ - family = fu_tpm_device_get_string (ctx, TPM2_PT_FAMILY_INDICATOR, error); - if (family == NULL) { + self->family = fu_tpm_device_get_string (ctx, TPM2_PT_FAMILY_INDICATOR, error); + if (self->family == NULL) { g_prefix_error (error, "failed to read TPM family"); return FALSE; } @@ -202,9 +209,9 @@ fu_tpm_device_setup (FuDevice *device, GError **error) fu_device_add_instance_id (device, id1); id2 = g_strdup_printf ("TPM\\VEN_%s&MOD_%s", manufacturer, model); fu_device_add_instance_id (device, id2); - id3 = g_strdup_printf ("TPM\\VEN_%s&DEV_%04X&VER_%s", manufacturer, tpm_type, family); + id3 = g_strdup_printf ("TPM\\VEN_%s&DEV_%04X&VER_%s", manufacturer, tpm_type, self->family); fu_device_add_instance_id (device, id3); - id4 = g_strdup_printf ("TPM\\VEN_%s&MOD_%s&VER_%s", manufacturer, model, family); + id4 = g_strdup_printf ("TPM\\VEN_%s&MOD_%s&VER_%s", manufacturer, model, self->family); fu_device_add_instance_id (device, id4); /* enforce vendors can only ship updates for their own hardware */ @@ -245,6 +252,8 @@ fu_tpm_device_init (FuTpmDevice *self) static void fu_tpm_device_finalize (GObject *object) { + FuTpmDevice *self = FU_TPM_DEVICE (object); + g_free (self->family); G_OBJECT_CLASS (fu_tpm_device_parent_class)->finalize (object); } diff --git a/plugins/tpm/fu-tpm-device.h b/plugins/tpm/fu-tpm-device.h index 55c1ef312..0584f9d26 100644 --- a/plugins/tpm/fu-tpm-device.h +++ b/plugins/tpm/fu-tpm-device.h @@ -10,3 +10,5 @@ #define FU_TYPE_TPM_DEVICE (fu_tpm_device_get_type ()) G_DECLARE_FINAL_TYPE (FuTpmDevice, fu_tpm_device, FU, TPM_DEVICE, FuUdevDevice) + +const gchar *fu_tpm_device_get_family (FuTpmDevice *self); diff --git a/plugins/uefi-dbx/README.md b/plugins/uefi-dbx/README.md index 303bc8359..69ac99507 100644 --- a/plugins/uefi-dbx/README.md +++ b/plugins/uefi-dbx/README.md @@ -8,6 +8,9 @@ Updating the UEFI revocation database prevents starting EFI binaries with known security issues, and is typically no longer done from a firmware update due to the risk of the machine being "bricked" if the bootloader is not updated first. +This plugin also checks if the UEFI dbx contains all the most recent revoked +checksums. The result will be stored in an security attribute for HSI. + Firmware Format --------------- @@ -36,3 +39,9 @@ Vendor ID Security ------------------ The vendor ID is hardcoded to `UEFI:Microsoft` for all devices. + + +External interface access +------------------------- +This plugin requires: +* read/write access to `/sys/firmware/efi/efivars` diff --git a/plugins/uefi-dbx/create-fuzzing-targets.py b/plugins/uefi-dbx/create-fuzzing-targets.py new file mode 100755 index 000000000..9c2bba681 --- /dev/null +++ b/plugins/uefi-dbx/create-fuzzing-targets.py @@ -0,0 +1,28 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1+ + +import sys +import struct + +if __name__ == '__main__': + + # SignatureType + buf = b'0' * 16 + + # SignatureListSize + buf += struct.pack('bgrt = fu_uefi_bgrt_new (); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "upower"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "tpm"); fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "tpm_eventlog"); - fu_plugin_add_compile_version (plugin, "com.redhat.efivar", EFIVAR_LIBRARY_VERSION); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "dell"); fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); } @@ -47,6 +48,8 @@ void fu_plugin_destroy (FuPlugin *plugin) { FuPluginData *data = fu_plugin_get_data (plugin); + if (data->esp != NULL) + g_object_unref (data->esp); g_object_unref (data->bgrt); } @@ -92,6 +95,35 @@ fu_plugin_get_results (FuPlugin *plugin, FuDevice *device, GError **error) return TRUE; } +void +fu_plugin_add_security_attrs (FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error = NULL; + + /* create attr */ + attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT); + fwupd_security_attr_set_plugin (attr, fu_plugin_get_name (plugin)); + fu_security_attrs_append (attrs, attr); + + /* SB not available or disabled */ + if (!fu_efivar_secure_boot_enabled_full (&error)) { + if (g_error_matches (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED)) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + static GBytes * fu_plugin_uefi_get_splash_data (guint width, guint height, GError **error) { @@ -187,7 +219,7 @@ fu_plugin_uefi_write_splash_data (FuPlugin *plugin, efi_ux_capsule_header_t header = { 0 }; efi_capsule_header_t capsule_header = { .flags = EFI_CAPSULE_HEADER_FLAGS_PERSIST_ACROSS_RESET, - .guid = efi_guid_ux_capsule, + .guid = { 0x0 }, .header_size = sizeof(efi_capsule_header_t), .capsule_image_size = 0 }; @@ -220,6 +252,11 @@ fu_plugin_uefi_write_splash_data (FuPlugin *plugin, if (ostream == NULL) return FALSE; + if (!fwupd_guid_from_string (FU_EFIVAR_GUID_UX_CAPSULE, + &capsule_header.guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, + error)) + return FALSE; capsule_header.capsule_image_size = g_bytes_get_size (blob) + sizeof(efi_capsule_header_t) + @@ -254,13 +291,10 @@ fu_plugin_uefi_write_splash_data (FuPlugin *plugin, return FALSE; /* write display capsule location as UPDATE_INFO */ - if (!fu_uefi_device_write_update_info (FU_UEFI_DEVICE (device), fn, - "fwupd-ux-capsule", - &efi_guid_ux_capsule, error)) - return FALSE; - - /* success */ - return TRUE; + return fu_uefi_device_write_update_info (FU_UEFI_DEVICE (device), fn, + "fwupd-ux-capsule", + FU_EFIVAR_GUID_UX_CAPSULE, + error); } static gboolean @@ -378,25 +412,13 @@ fu_plugin_update (FuPlugin *plugin, g_assert (str != NULL); /* perform the update */ - g_debug ("Performing UEFI capsule update"); fu_device_set_status (device, FWUPD_STATUS_SCHEDULING); if (!fu_plugin_uefi_update_splash (plugin, device, &error_splash)) { g_debug ("failed to upload UEFI UX capsule text: %s", error_splash->message); } - if (!fu_device_write_firmware (device, blob_fw, flags, error)) - return FALSE; - - /* record if we had an invalid header during update */ - str = fu_uefi_missing_capsule_header (device) ? "True" : "False"; - fu_plugin_add_report_metadata (plugin, "MissingCapsuleHeader", str); - - /* where the ESP was mounted during installation */ - str = fu_device_get_metadata (device, "EspPath"); - fu_plugin_add_report_metadata (plugin, "ESPMountPoint", str); - - return TRUE; + return fu_device_write_firmware (device, blob_fw, flags, error); } static void @@ -424,10 +446,18 @@ fu_plugin_uefi_register_proxy_device (FuPlugin *plugin, FuDevice *device) { FuPluginData *data = fu_plugin_get_data (plugin); g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_dev (device); + g_autoptr(GError) error_local = NULL; /* load all configuration variables */ fu_plugin_uefi_load_config (plugin, FU_DEVICE (dev)); - fu_uefi_device_set_esp (dev, data->esp); + if (data->esp == NULL) + data->esp = fu_common_get_esp_default (&error_local); + if (data->esp == NULL) { + fu_device_set_update_error (FU_DEVICE (dev), error_local->message); + fu_device_remove_flag (FU_DEVICE (dev), FWUPD_DEVICE_FLAG_UPDATABLE); + } else { + fu_uefi_device_set_esp (dev, data->esp); + } fu_plugin_device_add (plugin, FU_DEVICE (dev)); } @@ -480,6 +510,8 @@ fu_plugin_uefi_coldplug_device (FuPlugin *plugin, FuUefiDevice *dev, GError **er /* probe to get add GUIDs (and hence any quirk fixups) */ if (!fu_device_probe (FU_DEVICE (dev), error)) return FALSE; + if (!fu_device_setup (FU_DEVICE (dev), error)) + return FALSE; /* if not already set by quirks */ if (fu_device_get_custom_flags (FU_DEVICE (dev)) == NULL) { @@ -528,7 +560,6 @@ fu_plugin_uefi_test_secure_boot (FuPlugin *plugin) const gchar *result_str = "Disabled"; if (fu_efivar_secure_boot_enabled ()) result_str = "Enabled"; - g_debug ("SecureBoot is: %s", result_str); fu_plugin_add_report_metadata (plugin, "SecureBoot", result_str); } @@ -577,7 +608,9 @@ gboolean fu_plugin_startup (FuPlugin *plugin, GError **error) { FuPluginData *data = fu_plugin_get_data (plugin); + guint64 nvram_total; g_autofree gchar *esp_path = NULL; + g_autofree gchar *nvram_total_str = NULL; g_autoptr(GError) error_local = NULL; /* some platforms have broken SMBIOS data */ @@ -601,6 +634,11 @@ fu_plugin_startup (FuPlugin *plugin, GError **error) /* are the EFI dirs set up so we can update each device */ if (!fu_efivar_supported (error)) return FALSE; + nvram_total = fu_efivar_space_used (error); + if (nvram_total == G_MAXUINT64) + return FALSE; + nvram_total_str = g_format_size_full (nvram_total, G_FORMAT_SIZE_LONG_FORMAT); + fu_plugin_add_report_metadata (plugin, "EfivarNvramUsed", nvram_total_str); /* override the default ESP path */ esp_path = fu_plugin_get_config_value (plugin, "OverrideESPMountPoint"); @@ -611,12 +649,6 @@ fu_plugin_startup (FuPlugin *plugin, GError **error) "specified in config: ", esp_path); return FALSE; } - } else { - data->esp = fu_common_get_esp_default (error); - if (data->esp == NULL) { - g_prefix_error (error, "cannot find default ESP: "); - return FALSE; - } } /* test for invalid ESP in coldplug, and set the update-error rather @@ -716,43 +748,6 @@ fu_plugin_unlock (FuPlugin *plugin, FuDevice *device, GError **error) return TRUE; } -static gboolean -fu_plugin_uefi_create_dummy (FuPlugin *plugin, const gchar *reason, GError **error) -{ - const gchar *key; - g_autoptr(FuDevice) dev = fu_device_new (); - - fu_device_set_version_format (dev, FWUPD_VERSION_FORMAT_PLAIN); - key = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_MANUFACTURER); - if (key != NULL) - fu_device_set_vendor (dev, key); - key = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_BIOS_VENDOR); - if (key != NULL) { - g_autofree gchar *vendor_id = g_strdup_printf ("DMI:%s", key); - fu_device_set_vendor_id (FU_DEVICE (dev), vendor_id); - } - key = fu_plugin_uefi_get_name_for_type (plugin, FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE); - fu_device_set_name (dev, key); - key = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_BIOS_VERSION); - if (key != NULL) - fu_device_set_version (dev, key); - fu_device_set_update_error (dev, reason); - - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_REQUIRE_AC); - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_MD_SET_VERFMT); - - fu_device_add_icon (dev, "computer"); - fu_device_set_id (dev, "UEFI-dummy"); - fu_device_add_instance_id (dev, "main-system-firmware"); - if (!fu_device_setup (dev, error)) - return FALSE; - fu_plugin_device_add (plugin, dev); - - return TRUE; -} - gboolean fu_plugin_coldplug (FuPlugin *plugin, GError **error) { @@ -760,30 +755,35 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) const gchar *str; g_autofree gchar *esrt_path = NULL; g_autofree gchar *sysfsfwdir = NULL; + g_autoptr(GError) error_udisks2 = NULL; g_autoptr(GError) error_efivarfs = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(GPtrArray) entries = NULL; - /* are the EFI dirs set up so we can update each device */ - if (!fu_efivar_supported (&error_local)) { - const gchar *reason = "Firmware can not be updated in legacy mode, switch to UEFI mode"; - g_warning ("%s", error_local->message); - return fu_plugin_uefi_create_dummy (plugin, reason, error); - } - /* get the directory of ESRT entries */ sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); esrt_path = g_build_filename (sysfsfwdir, "efi", "esrt", NULL); - entries = fu_uefi_get_esrt_entry_paths (esrt_path, &error_local); - if (entries == NULL) { - const gchar *reason = "UEFI Capsule updates not available or enabled"; - g_warning ("%s", error_local->message); - return fu_plugin_uefi_create_dummy (plugin, reason, error); - } + entries = fu_uefi_get_esrt_entry_paths (esrt_path, error); + if (entries == NULL) + return FALSE; /* make sure that efivarfs is rw */ - if (!fu_plugin_uefi_ensure_efivarfs_rw (&error_efivarfs)) + if (!fu_plugin_uefi_ensure_efivarfs_rw (&error_efivarfs)) { + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED); + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE); + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); g_warning ("%s", error_efivarfs->message); + } + + if (data->esp == NULL) { + data->esp = fu_common_get_esp_default (&error_udisks2); + if (data->esp == NULL) { + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND); + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE); + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); + g_warning ("cannot find default ESP: %s", error_udisks2->message); + } + } /* add each device */ for (guint i = 0; i < entries->len; i++) { @@ -795,15 +795,13 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) continue; } fu_device_set_quirks (FU_DEVICE (dev), fu_plugin_get_quirks (plugin)); - fu_uefi_device_set_esp (FU_UEFI_DEVICE (dev), data->esp); + if (data->esp != NULL) + fu_uefi_device_set_esp (FU_UEFI_DEVICE (dev), data->esp); if (!fu_plugin_uefi_coldplug_device (plugin, dev, error)) return FALSE; - if (error_efivarfs != NULL) { - fu_device_set_update_error (FU_DEVICE (dev), error_efivarfs->message); - } else { - fu_device_add_flag (FU_DEVICE (dev), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag (FU_DEVICE (dev), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); - } + fu_device_add_flag (FU_DEVICE (dev), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag (FU_DEVICE (dev), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + /* load all configuration variables */ fu_plugin_uefi_load_config (plugin, FU_DEVICE (dev)); fu_plugin_device_add (plugin, FU_DEVICE (dev)); diff --git a/plugins/uefi/fu-self-test.c b/plugins/uefi/fu-self-test.c index 19899b38d..aef078cc6 100644 --- a/plugins/uefi/fu-self-test.c +++ b/plugins/uefi/fu-self-test.c @@ -50,6 +50,11 @@ fu_uefi_pcrs_2_0_func (void) const gchar *tpm_server_running = g_getenv ("TPM_SERVER_RUNNING"); g_setenv ("FWUPD_FORCE_TPM2", "1", TRUE); +#ifndef HAVE_TSS2 + g_test_skip ("Compiled without TPM2.0 support"); + return; +#endif + #ifdef HAVE_GETUID if (tpm_server_running == NULL && (getuid () != 0 || geteuid () != 0)) { diff --git a/plugins/uefi/fu-uefi-bootmgr.c b/plugins/uefi/fu-uefi-bootmgr.c index 4d9319be3..ee7396728 100644 --- a/plugins/uefi/fu-uefi-bootmgr.c +++ b/plugins/uefi/fu-uefi-bootmgr.c @@ -7,7 +7,6 @@ #include "config.h" #include -#include #include #include @@ -25,46 +24,22 @@ static gboolean fu_uefi_bootmgr_add_to_boot_order (guint16 boot_entry, GError **error) { gsize boot_order_size = 0; - gint rc; guint i = 0; - guint32 attr = EFI_VARIABLE_NON_VOLATILE | - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS; + guint32 attr = 0; g_autofree guint16 *boot_order = NULL; g_autofree guint16 *new_boot_order = NULL; - /* get size of the BootOrder */ - rc = efi_get_variable_size (efi_guid_global, "BootOrder", &boot_order_size); - if (rc == ENOENT) { - boot_order_size = 0; - efi_error_clear (); - } else if (rc < 0) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "efi_get_variable_size() failed"); - return rc; - } - /* get the current boot order */ - if (boot_order_size != 0) { - rc = efi_get_variable (efi_guid_global, "BootOrder", - (guint8 **)&boot_order, &boot_order_size, - &attr); - if (rc < 0) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "efi_get_variable(BootOrder) failed"); - return FALSE; - } + if (!fu_efivar_get_data (FU_EFIVAR_GUID_EFI_GLOBAL, "BootOrder", + (guint8 **) &boot_order, &boot_order_size, + &attr, error)) + return FALSE; - /* already set next */ - for (i = 0; i < boot_order_size / sizeof (guint16); i++) { - guint16 val = boot_order[i]; - if (val == boot_entry) - return TRUE; - } + /* already set next */ + for (i = 0; i < boot_order_size / sizeof (guint16); i++) { + guint16 val = boot_order[i]; + if (val == boot_entry) + return TRUE; } /* add the new boot index to the end of the list */ @@ -72,44 +47,42 @@ fu_uefi_bootmgr_add_to_boot_order (guint16 boot_entry, GError **error) if (boot_order_size != 0) memcpy (new_boot_order, boot_order, boot_order_size); + attr |= FU_EFIVAR_ATTR_NON_VOLATILE | + FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS; + i = boot_order_size / sizeof (guint16); new_boot_order[i] = boot_entry; boot_order_size += sizeof (guint16); - rc = efi_set_variable(efi_guid_global, "BootOrder", - (guint8 *)new_boot_order, boot_order_size, - attr, 0644); - if (rc < 0) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "efi_set_variable(BootOrder) failed"); - return FALSE; - } - - return TRUE; + return fu_efivar_set_data (FU_EFIVAR_GUID_EFI_GLOBAL, "BootOrder", + (guint8 *)new_boot_order, boot_order_size, + attr, error); } static gboolean fu_uefi_setup_bootnext_with_dp (const guint8 *dp_buf, guint8 *opt, gssize opt_size, GError **error) { - efi_guid_t *guid = NULL; + const gchar *desc; + const gchar *name; efi_load_option *loadopt = NULL; - gchar *name = NULL; gint rc; gsize var_data_size = 0; guint32 attr; guint16 boot_next = G_MAXUINT16; g_autofree guint8 *var_data = NULL; g_autofree guint8 *set_entries = g_malloc0 (G_MAXUINT16); + g_autoptr(GPtrArray) names = NULL; - while ((rc = efi_get_next_variable_name (&guid, &name)) > 0) { - const gchar *desc; + names = fu_efivar_get_names (FU_EFIVAR_GUID_EFI_GLOBAL, error); + if (names == NULL) + return FALSE; + for (guint i = 0; i < names->len; i++) { gint scanned = 0; guint16 entry = 0; g_autofree guint8 *var_data_tmp = NULL; + g_autoptr(GError) error_local = NULL; - if (efi_guid_cmp (guid, &efi_guid_global) != 0) - continue; + name = g_ptr_array_index (names, i); rc = sscanf (name, "Boot%hX%n", &entry, &scanned); if (rc < 0) { g_set_error (error, @@ -126,53 +99,47 @@ fu_uefi_setup_bootnext_with_dp (const guint8 *dp_buf, guint8 *opt, gssize opt_si /* mark this as used */ set_entries[entry] = 1; - rc = efi_get_variable (*guid, name, &var_data_tmp, &var_data_size, &attr); - if (rc < 0) { - g_debug ("efi_get_variable(%s) failed", name); + if (!fu_efivar_get_data (FU_EFIVAR_GUID_EFI_GLOBAL, name, + &var_data_tmp, &var_data_size, + &attr, &error_local)) { + g_debug ("failed to get data for name %s: %s", + name, error_local->message); continue; } loadopt = (efi_load_option *)var_data_tmp; if (!efi_loadopt_is_valid(loadopt, var_data_size)) { - g_debug ("load option was invalid"); + g_debug ("%s -> load option was invalid", name); continue; } desc = (const gchar *) efi_loadopt_desc (loadopt, var_data_size); if (g_strcmp0 (desc, "Linux Firmware Updater") != 0 && g_strcmp0 (desc, "Linux-Firmware-Updater") != 0) { - g_debug ("description does not match"); + g_debug ("%s -> '%s' : does not match", name, desc); continue; } var_data = g_steal_pointer (&var_data_tmp); boot_next = entry; - efi_error_clear (); break; } - if (rc < 0) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "failed to find boot variable"); - return FALSE; - } /* already exists */ if (var_data != NULL) { - /* is different than before */ if (var_data_size != (gsize) opt_size || memcmp (var_data, opt, opt_size) != 0) { + g_debug ("%s -> '%s' : updating existing boot entry", name, desc); efi_loadopt_attr_set (loadopt, LOAD_OPTION_ACTIVE); - rc = efi_set_variable (*guid, name, opt, opt_size, attr, 0644); - if (rc < 0) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "could not set boot variable active"); + if (!fu_efivar_set_data (FU_EFIVAR_GUID_EFI_GLOBAL, + name, opt, opt_size, attr, error)) { + g_prefix_error (error, + "could not set boot variable active: "); return FALSE; } + } else { + g_debug ("%s -> %s : re-using existing boot entry", name, desc); } /* create a new one */ } else { @@ -192,17 +159,16 @@ fu_uefi_setup_bootnext_with_dp (const guint8 *dp_buf, guint8 *opt, gssize opt_si return FALSE; } boot_next_name = g_strdup_printf ("Boot%04X", (guint) boot_next); - rc = efi_set_variable (efi_guid_global, boot_next_name, opt, opt_size, - EFI_VARIABLE_NON_VOLATILE | - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS, - 0644); - if (rc < 0) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "could not set boot variable %s: %d", - boot_next_name, rc); + g_debug ("%s -> creating new entry", boot_next_name); + if (!fu_efivar_set_data (FU_EFIVAR_GUID_EFI_GLOBAL, + boot_next_name, opt, opt_size, + FU_EFIVAR_ATTR_NON_VOLATILE | + FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS, + error)) { + g_prefix_error (error, + "could not set boot variable %s: ", + boot_next_name); return FALSE; } } @@ -212,17 +178,15 @@ fu_uefi_setup_bootnext_with_dp (const guint8 *dp_buf, guint8 *opt, gssize opt_si return FALSE; /* set the boot next */ - rc = efi_set_variable (efi_guid_global, "BootNext", (guint8 *)&boot_next, 2, - EFI_VARIABLE_NON_VOLATILE | - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS, - 0644); - if (rc < 0) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "could not set BootNext(%" G_GUINT16_FORMAT ")", - boot_next); + if (!fu_efivar_set_data (FU_EFIVAR_GUID_EFI_GLOBAL, + "BootNext", (guint8 *)&boot_next, 2, + FU_EFIVAR_ATTR_NON_VOLATILE | + FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS, + error)) { + g_prefix_error (error, + "could not set BootNext(%" G_GUINT16_FORMAT "): ", + boot_next); return FALSE; } return TRUE; @@ -413,9 +377,5 @@ fu_uefi_bootmgr_bootnext (const gchar *esp_path, "loadopt size was unreasonable."); return FALSE; } - if (!fu_uefi_setup_bootnext_with_dp (dp_buf, opt, opt_size, error)) - return FALSE; - efi_error_clear(); - - return TRUE; + return fu_uefi_setup_bootnext_with_dp (dp_buf, opt, opt_size, error); } diff --git a/plugins/uefi/fu-uefi-common.c b/plugins/uefi/fu-uefi-common.c index 6cd556726..a5b554293 100644 --- a/plugins/uefi/fu-uefi-common.c +++ b/plugins/uefi/fu-uefi-common.c @@ -263,21 +263,3 @@ fu_uefi_read_file_as_uint64 (const gchar *path, const gchar *attr_name) return 0x0; return fu_common_strtoull (data); } - -void -fu_uefi_print_efivar_errors (void) -{ - for (gint i = 0; ; i++) { - gchar *filename = NULL; - gchar *function = NULL; - gchar *message = NULL; - gint line = 0; - gint err = 0; - if (efi_error_get (i, &filename, &function, &line, - &message, &err) <= 0) - break; - g_debug ("{efivar error #%d} %s:%d %s(): %s: %s\t", - i, filename, line, function, - message, strerror (err)); - } -} diff --git a/plugins/uefi/fu-uefi-common.h b/plugins/uefi/fu-uefi-common.h index cf7381169..42f89e956 100644 --- a/plugins/uefi/fu-uefi-common.h +++ b/plugins/uefi/fu-uefi-common.h @@ -8,7 +8,8 @@ #pragma once #include -#include + +#include "fwupd-common.h" #define EFI_CAPSULE_HEADER_FLAGS_PERSIST_ACROSS_RESET 0x00010000 #define EFI_CAPSULE_HEADER_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 @@ -29,7 +30,7 @@ typedef struct __attribute__((__packed__)) { } efi_time_t; typedef struct __attribute__((__packed__)) { - efi_guid_t guid; + fwupd_guid_t guid; guint32 header_size; guint32 flags; guint32 capsule_image_size; @@ -47,7 +48,7 @@ typedef struct __attribute__((__packed__)) { typedef struct __attribute__((__packed__)) { guint32 update_info_version; - efi_guid_t guid; + fwupd_guid_t guid; guint32 capsule_flags; guint64 hw_inst; efi_time_t time_attempted; @@ -74,4 +75,3 @@ GPtrArray *fu_uefi_get_esrt_entry_paths (const gchar *esrt_path, GError **error); guint64 fu_uefi_read_file_as_uint64 (const gchar *path, const gchar *attr_name); -void fu_uefi_print_efivar_errors (void); diff --git a/plugins/uefi/fu-uefi-device.c b/plugins/uefi/fu-uefi-device.c index 80a670dca..9fe66de28 100644 --- a/plugins/uefi/fu-uefi-device.c +++ b/plugins/uefi/fu-uefi-device.c @@ -127,6 +127,36 @@ fu_uefi_device_to_string (FuDevice *device, guint idt, GString *str) fu_device_get_metadata_boolean (device, "RequireShimForSecureBoot")); } +static void +fu_uefi_device_report_metadata_pre (FuDevice *device, GHashTable *metadata) +{ + FuUefiDevice *self = FU_UEFI_DEVICE (device); + + /* record if we had an invalid header during update */ + g_hash_table_insert (metadata, + g_strdup ("MissingCapsuleHeader"), + g_strdup (self->missing_header ? "True" : "False")); + + /* where the ESP was mounted during installation */ + g_hash_table_insert (metadata, + g_strdup ("EspPath"), + fu_volume_get_mount_point (self->esp)); +} + +static void +fu_uefi_device_report_metadata_post (FuDevice *device, GHashTable *metadata) +{ + FuUefiDevice *self = FU_UEFI_DEVICE (device); + + /* the actual last_attempt values */ + g_hash_table_insert (metadata, + g_strdup ("LastAttemptStatus"), + g_strdup_printf ("0x%x", self->last_attempt_status)); + g_hash_table_insert (metadata, + g_strdup ("LastAttemptVersion"), + g_strdup_printf ("0x%x", self->last_attempt_version)); +} + FuUefiDeviceKind fu_uefi_device_get_kind (FuUefiDevice *self) { @@ -310,26 +340,21 @@ fu_uefi_device_fixup_firmware (FuDevice *device, GBytes *fw, GError **error) { FuUefiDevice *self = FU_UEFI_DEVICE (device); gsize fw_length; - efi_guid_t esrt_guid; - efi_guid_t payload_guid; - const gchar *data = g_bytes_get_data (fw, &fw_length); + const guint8 *data = g_bytes_get_data (fw, &fw_length); + g_autofree gchar *guid_new = NULL; + self->missing_header = FALSE; - /* convert to EFI GUIDs */ - if (efi_str_to_guid (fu_uefi_device_get_guid (self), &esrt_guid) < 0) { - g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "Invalid ESRT GUID"); - return NULL; - } - if (fw_length < sizeof(efi_guid_t)) { + /* GUID is the first 16 bytes */ + if (fw_length < sizeof(fwupd_guid_t)) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "Invalid payload"); return NULL; } - memcpy (&payload_guid, data, sizeof(efi_guid_t)); + guid_new = fwupd_guid_to_string ((fwupd_guid_t *) data, FWUPD_GUID_FLAG_MIXED_ENDIAN); /* ESRT header matches payload */ - if (efi_guid_cmp (&esrt_guid, &payload_guid) == 0) { + if (g_strcmp0 (fu_uefi_device_get_guid (self), guid_new) == 0) { g_debug ("ESRT matches payload GUID"); return g_bytes_new_from_bytes (fw, 0, fw_length); /* Type that doesn't require a header */ @@ -340,6 +365,7 @@ fu_uefi_device_fixup_firmware (FuDevice *device, GBytes *fw, GError **error) guint header_size = getpagesize(); guint8 *new_data = g_malloc (fw_length + header_size); guint8 *capsule = new_data + header_size; + fwupd_guid_t esrt_guid = { 0x0 }; efi_capsule_header_t *header = (efi_capsule_header_t *) new_data; g_warning ("missing or invalid embedded capsule header"); @@ -347,25 +373,23 @@ fu_uefi_device_fixup_firmware (FuDevice *device, GBytes *fw, GError **error) header->flags = self->capsule_flags; header->header_size = header_size; header->capsule_image_size = fw_length + header_size; - memcpy (&header->guid, &esrt_guid, sizeof (efi_guid_t)); + if (!fwupd_guid_from_string (fu_uefi_device_get_guid (self), &esrt_guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) { + g_prefix_error (error, "Invalid ESRT GUID: "); + return NULL; + } + memcpy (&header->guid, &esrt_guid, sizeof (fwupd_guid_t)); memcpy (capsule, data, fw_length); return g_bytes_new_take (new_data, fw_length + header_size); } } -gboolean -fu_uefi_missing_capsule_header (FuDevice *device) -{ - FuUefiDevice *self = FU_UEFI_DEVICE (device); - return self->missing_header; -} - gboolean fu_uefi_device_write_update_info (FuUefiDevice *self, const gchar *filename, const gchar *varname, - const efi_guid_t *guid, + const gchar *guid, GError **error) { gsize datasz = 0; @@ -389,27 +413,22 @@ fu_uefi_device_write_update_info (FuUefiDevice *self, /* convert to EFI device path */ dp_buf = fu_uefi_device_build_dp_buf (filename, &dp_bufsz, error); - if (dp_buf == NULL) { - fu_uefi_print_efivar_errors (); + if (dp_buf == NULL) return FALSE; - } /* save this header and body to the hardware */ - memcpy (&info.guid, guid, sizeof(efi_guid_t)); + if (!fwupd_guid_from_string (guid, &info.guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) + return FALSE; datasz = sizeof(info) + dp_bufsz; data = g_malloc0 (datasz); memcpy (data, &info, sizeof(info)); memcpy (data + sizeof(info), dp_buf, dp_bufsz); - if (!fu_efivar_set_data (FU_EFIVAR_GUID_FWUPDATE, varname, - data, datasz, - FU_EFIVAR_ATTR_NON_VOLATILE | - FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVAR_ATTR_RUNTIME_ACCESS, - error)) { - fu_uefi_print_efivar_errors (); - return FALSE; - } - return TRUE; + return fu_efivar_set_data (FU_EFIVAR_GUID_FWUPDATE, varname, + data, datasz, + FU_EFIVAR_ATTR_NON_VOLATILE | + FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS, + error); } static gboolean @@ -518,7 +537,6 @@ fu_uefi_device_write_firmware (FuDevice *device, FuUefiDevice *self = FU_UEFI_DEVICE (device); FuUefiBootmgrFlags flags = FU_UEFI_BOOTMGR_FLAG_NONE; const gchar *bootmgr_desc = "Linux Firmware Updater"; - efi_guid_t guid; g_autofree gchar *esp_path = fu_volume_get_mount_point (self->esp); g_autoptr(GBytes) fixed_fw = NULL; g_autoptr(GBytes) fw = NULL; @@ -553,15 +571,12 @@ fu_uefi_device_write_firmware (FuDevice *device, if (!fu_common_set_contents_bytes (fn, fixed_fw, error)) return FALSE; + /* delete the logs to save space; use fwupdate to debug the EFI binary */ + fu_efivar_delete (FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_VERBOSE", NULL); + fu_efivar_delete (FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_DEBUG_LOG", NULL); + /* set the blob header shared with fwupd.efi */ - if (efi_str_to_guid (self->fw_class, &guid) < 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to get convert GUID"); - return FALSE; - } - if (!fu_uefi_device_write_update_info (self, fn, varname, &guid, error)) + if (!fu_uefi_device_write_update_info (self, fn, varname, self->fw_class, error)) return FALSE; /* update the firmware before the bootloader runs */ @@ -698,7 +713,6 @@ static void fu_uefi_device_init (FuUefiDevice *self) { fu_device_set_protocol (FU_DEVICE (self), "org.uefi.capsule"); - fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_NUMBER); } static void @@ -707,6 +721,8 @@ fu_uefi_device_finalize (GObject *object) FuUefiDevice *self = FU_UEFI_DEVICE (object); g_free (self->fw_class); + if (self->esp != NULL) + g_object_unref (self->esp); if (self->esp_locker != NULL) g_object_unref (self->esp_locker); @@ -724,6 +740,8 @@ fu_uefi_device_class_init (FuUefiDeviceClass *klass) klass_device->prepare = fu_uefi_device_prepare; klass_device->write_firmware = fu_uefi_device_write_firmware; klass_device->cleanup = fu_uefi_device_cleanup; + klass_device->report_metadata_pre = fu_uefi_device_report_metadata_pre; + klass_device->report_metadata_post = fu_uefi_device_report_metadata_post; } FuUefiDevice * @@ -738,6 +756,9 @@ fu_uefi_device_new_from_entry (const gchar *entry_path, GError **error) /* create object */ self = g_object_new (FU_TYPE_UEFI_DEVICE, NULL); + /* assume a uint64_t unless told otherwise by a quirk entry or metadata */ + fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_NUMBER); + /* read values from sysfs */ fw_class_fn = g_build_filename (entry_path, "fw_class", NULL); if (g_file_get_contents (fw_class_fn, &self->fw_class, NULL, NULL)) @@ -797,5 +818,6 @@ fu_uefi_device_new_from_guid (const gchar *guid) FuUefiDevice *self; self = g_object_new (FU_TYPE_UEFI_DEVICE, NULL); self->fw_class = g_strdup (guid); + fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_NUMBER); return self; } diff --git a/plugins/uefi/fu-uefi-device.h b/plugins/uefi/fu-uefi-device.h index 9db843946..385430c9c 100644 --- a/plugins/uefi/fu-uefi-device.h +++ b/plugins/uefi/fu-uefi-device.h @@ -56,9 +56,8 @@ const gchar *fu_uefi_device_kind_to_string (FuUefiDeviceKind kind); const gchar *fu_uefi_device_status_to_string (FuUefiDeviceStatus status); FuUefiUpdateInfo *fu_uefi_device_load_update_info (FuUefiDevice *self, GError **error); -gboolean fu_uefi_missing_capsule_header (FuDevice *device); gboolean fu_uefi_device_write_update_info (FuUefiDevice *self, const gchar *filename, const gchar *varname, - const efi_guid_t *guid, + const gchar *guid, GError **error); diff --git a/plugins/uefi/fu-uefi-pcrs.c b/plugins/uefi/fu-uefi-pcrs.c index 9396c80ad..ab3f486e4 100644 --- a/plugins/uefi/fu-uefi-pcrs.c +++ b/plugins/uefi/fu-uefi-pcrs.c @@ -163,14 +163,16 @@ fu_uefi_pcrs_setup_tpm20 (FuUefiPcrs *self, GError **error) for (guint i = 0; i < pcr_values->count; i++) { FuUefiPcrItem *item; g_autoptr(GString) str = NULL; + gboolean valid = FALSE; str = g_string_new (NULL); for (guint j = 0; j < pcr_values->digests[i].size; j++) { gint64 val = pcr_values->digests[i].buffer[j]; if (val > 0) - g_string_append_printf (str, "%02x", pcr_values->digests[i].buffer[j]); + valid = TRUE; + g_string_append_printf (str, "%02x", pcr_values->digests[i].buffer[j]); } - if (str->len > 0) { + if (valid) { item = g_new0 (FuUefiPcrItem, 1); item->idx = 0; /* constant PCR index 0, since we only read this single PCR */ item->checksum = g_string_free (g_steal_pointer (&str), FALSE); diff --git a/plugins/uefi/fu-uefi-tool.c b/plugins/uefi/fu-uefi-tool.c index 82463c57a..b5f80203c 100644 --- a/plugins/uefi/fu-uefi-tool.c +++ b/plugins/uefi/fu-uefi-tool.c @@ -257,11 +257,19 @@ main (int argc, char *argv[]) /* load any existing update info */ info = fu_uefi_device_load_update_info (dev, &error_local); + g_print ("Information for the update status entry %u:\n", i); if (info == NULL) { - g_printerr ("failed: %s\n", error_local->message); + if (g_error_matches (error_local, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND)) { + g_print (" Firmware GUID: {%s}\n", + fu_uefi_device_get_guid (dev)); + g_print (" Update Status: No update info found\n\n"); + } else { + g_printerr ("Failed: %s\n\n", error_local->message); + } continue; } - g_print ("Information for the update status entry %u:\n", i); g_print (" Information Version: %" G_GUINT32_FORMAT "\n", fu_uefi_update_info_get_version (info)); g_print (" Firmware GUID: {%s}\n", @@ -288,12 +296,12 @@ main (int argc, char *argv[]) const guint8 data = 1; g_autoptr(GError) error_local = NULL; if (!fu_efivar_set_data (FU_EFIVAR_GUID_FWUPDATE, - "FWUPDATE_VERBOSE", - &data, sizeof(data), - EFI_VARIABLE_NON_VOLATILE | - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS, - &error_local)) { + "FWUPDATE_VERBOSE", + &data, sizeof(data), + FU_EFIVAR_ATTR_NON_VOLATILE | + FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS, + &error_local)) { g_printerr ("failed: %s\n", error_local->message); return EXIT_FAILURE; } @@ -327,6 +335,7 @@ main (int argc, char *argv[]) g_printerr ("failed: %s\n", error_local->message); return EXIT_FAILURE; } + fu_uefi_device_set_esp (dev, esp); if (flags != NULL) fu_device_set_custom_flags (FU_DEVICE (dev), flags); if (!fu_device_prepare (FU_DEVICE (dev), diff --git a/plugins/uefi/fu-uefi-update-info.c b/plugins/uefi/fu-uefi-update-info.c index 9454844ce..3dc138dcc 100644 --- a/plugins/uefi/fu-uefi-update-info.c +++ b/plugins/uefi/fu-uefi-update-info.c @@ -14,6 +14,9 @@ #include "fwupd-error.h" +#define EFIDP_MEDIA_TYPE 0x04 +#define EFIDP_MEDIA_FILE 0x4 + struct _FuUefiUpdateInfo { GObject parent_instance; guint32 version; @@ -80,7 +83,7 @@ gboolean fu_uefi_update_info_parse (FuUefiUpdateInfo *self, const guint8 *buf, gsize sz, GError **error) { efi_update_info_t info; - efi_guid_t guid_tmp; + fwupd_guid_t guid_tmp; g_return_val_if_fail (FU_IS_UEFI_UPDATE_INFO (self), FALSE); @@ -96,14 +99,8 @@ fu_uefi_update_info_parse (FuUefiUpdateInfo *self, const guint8 *buf, gsize sz, self->capsule_flags = info.capsule_flags; self->hw_inst = info.hw_inst; self->status = info.status; - memcpy (&guid_tmp, &info.guid, sizeof(efi_guid_t)); - if (efi_guid_to_str (&guid_tmp, &self->guid) < 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to convert GUID"); - return FALSE; - } + memcpy (&guid_tmp, &info.guid, sizeof(fwupd_guid_t)); + self->guid = fwupd_guid_to_string (&guid_tmp, FWUPD_GUID_FLAG_MIXED_ENDIAN); if (sz > sizeof(efi_update_info_t)) { self->capsule_fn = fu_uefi_update_info_parse_dp (buf + sizeof(efi_update_info_t), sz - sizeof(efi_update_info_t), diff --git a/plugins/uefi/meson.build b/plugins/uefi/meson.build index 3f13f4c75..2d1b2d221 100644 --- a/plugins/uefi/meson.build +++ b/plugins/uefi/meson.build @@ -37,7 +37,6 @@ shared_module('fu_plugin_uefi', c_args : cargs, dependencies : [ plugin_deps, - efivar, efiboot, tpm2tss, ], @@ -68,7 +67,6 @@ fwupdate = executable( giounix, gusb, gudev, - efivar, efiboot, tpm2tss, ], @@ -126,7 +124,6 @@ if get_option('tests') ], dependencies : [ plugin_deps, - efivar, efiboot, tpm2tss, ], diff --git a/plugins/upower/README.md b/plugins/upower/README.md index 3940be144..e27863dd0 100644 --- a/plugins/upower/README.md +++ b/plugins/upower/README.md @@ -10,3 +10,7 @@ Vendor ID Security ------------------ This protocol does not create a device and thus requires no vendor ID set. + +External interface access +------------------------- +This plugin requires access to the dbus interface `org.freedesktop.UPower`. diff --git a/plugins/upower/fu-plugin-upower.c b/plugins/upower/fu-plugin-upower.c index 25546c229..2d83f9b9b 100644 --- a/plugins/upower/fu-plugin-upower.c +++ b/plugins/upower/fu-plugin-upower.c @@ -149,7 +149,7 @@ fu_plugin_update_prepare (FuPlugin *plugin, /* determine if operating on AC or battery */ if (fu_plugin_upower_check_on_battery (plugin) && - (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + (flags & FWUPD_INSTALL_FLAG_IGNORE_POWER) == 0) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_AC_POWER_REQUIRED, @@ -160,7 +160,7 @@ fu_plugin_update_prepare (FuPlugin *plugin, /* determine if battery high enough */ if (!fu_plugin_upower_check_percentage_level (plugin) && - (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + (flags & FWUPD_INSTALL_FLAG_IGNORE_POWER) == 0) { FuPluginData *data = fu_plugin_get_data (plugin); g_set_error (error, FWUPD_ERROR, diff --git a/plugins/vli/README.md b/plugins/vli/README.md index 71f459b65..14a47a70c 100644 --- a/plugins/vli/README.md +++ b/plugins/vli/README.md @@ -80,3 +80,7 @@ the other flash chip parameters. For example: [Guid=VLI_USBHUB\\SPI_37303840] SpiCmdChipErase = 0xc7 SpiCmdSectorErase = 0x20 + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/vli/fu-vli-common.c b/plugins/vli/fu-vli-common.c index 8c6904af9..c5b7fda1b 100644 --- a/plugins/vli/fu-vli-common.c +++ b/plugins/vli/fu-vli-common.c @@ -9,38 +9,6 @@ #include "fu-vli-common.h" -guint8 -fu_vli_common_crc8 (const guint8 *buf, gsize bufsz) -{ - guint32 crc = 0; - for (gsize j = bufsz; j > 0; j--) { - crc ^= (*(buf++) << 8); - for (guint32 i = 8; i; i--) { - if (crc & 0x8000) - crc ^= (0x1070 << 3); - crc <<= 1; - } - } - return (guint8) (crc >> 8); -} - -guint16 -fu_vli_common_crc16 (const guint8 *buf, gsize bufsz) -{ - guint16 crc = 0xffff; - for (gsize len = bufsz; len > 0; len--) { - crc = (guint16) (crc ^ (*buf++)); - for (guint8 i = 0; i < 8; i++) { - if (crc & 0x1) { - crc = (crc >> 1) ^ 0xa001; - } else { - crc >>= 1; - } - } - } - return ~crc; -} - const gchar * fu_vli_common_device_kind_to_string (FuVliDeviceKind device_kind) { diff --git a/plugins/vli/fu-vli-common.h b/plugins/vli/fu-vli-common.h index cc652f427..30d964835 100644 --- a/plugins/vli/fu-vli-common.h +++ b/plugins/vli/fu-vli-common.h @@ -43,8 +43,3 @@ const gchar *fu_vli_common_device_kind_to_string (FuVliDeviceKind device_kind); FuVliDeviceKind fu_vli_common_device_kind_from_string (const gchar *device_kind); guint32 fu_vli_common_device_kind_get_size (FuVliDeviceKind device_kind); guint32 fu_vli_common_device_kind_get_offset (FuVliDeviceKind device_kind); - -guint8 fu_vli_common_crc8 (const guint8 *buf, - gsize bufsz); -guint16 fu_vli_common_crc16 (const guint8 *buf, - gsize bufsz); diff --git a/plugins/vli/fu-vli-device.c b/plugins/vli/fu-vli-device.c index d83f38c6f..a2a9ff213 100644 --- a/plugins/vli/fu-vli-device.c +++ b/plugins/vli/fu-vli-device.c @@ -370,7 +370,7 @@ fu_vli_device_spi_erase_all (FuVliDevice *self, GError **error) return FALSE; if (!fu_vli_device_spi_chip_erase (self, error)) return FALSE; - g_usleep (4 * G_USEC_PER_SEC); + fu_device_sleep_with_progress (FU_DEVICE (self), 4); /* seconds */ /* verify chip was erased */ for (guint addr = 0; addr < 0x10000; addr += 0x1000) { @@ -539,7 +539,6 @@ fu_vli_device_setup (FuDevice *device, GError **error) g_autofree gchar *devid1 = NULL; g_autofree gchar *devid2 = NULL; g_autofree gchar *flash_id = fu_vli_device_get_flash_id_str (self); - g_debug ("using flash part %s", flash_id); /* load the SPI parameters from quirks */ spi_id = g_strdup_printf ("VLI_USBHUB\\SPI_%s", flash_id); @@ -631,6 +630,15 @@ fu_vli_device_set_quirk_kv (FuDevice *device, return FALSE; } +static void +fu_vli_device_report_metadata_pre (FuDevice *device, GHashTable *metadata) +{ + FuVliDevice *self = FU_VLI_DEVICE (device); + g_hash_table_insert (metadata, + g_strdup ("GType"), + g_strdup (G_OBJECT_TYPE_NAME (self))); +} + static void fu_vli_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { @@ -697,4 +705,5 @@ fu_vli_device_class_init (FuVliDeviceClass *klass) klass_device->to_string = fu_vli_device_to_string; klass_device->set_quirk_kv = fu_vli_device_set_quirk_kv; klass_device->setup = fu_vli_device_setup; + klass_device->report_metadata_pre = fu_vli_device_report_metadata_pre; } diff --git a/plugins/vli/fu-vli-pd-device.c b/plugins/vli/fu-vli-pd-device.c index 024f71596..8dc675e0c 100644 --- a/plugins/vli/fu-vli-pd-device.c +++ b/plugins/vli/fu-vli-pd-device.c @@ -7,6 +7,7 @@ #include "config.h" +#include "fu-common.h" #include "fu-firmware.h" #include "fu-vli-pd-device.h" @@ -344,7 +345,6 @@ fu_vli_pd_device_prepare_firmware (FuDevice *device, } /* check is compatible with firmware */ - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; device_kind = fu_vli_pd_firmware_get_kind (FU_VLI_PD_FIRMWARE (firmware)); @@ -363,18 +363,23 @@ fu_vli_pd_device_prepare_firmware (FuDevice *device, return g_steal_pointer (&firmware); } -static FuFirmware * -fu_vli_pd_device_read_firmware (FuDevice *device, GError **error) +static GBytes * +fu_vli_pd_device_dump_firmware (FuDevice *device, GError **error) { FuVliPdDevice *self = FU_VLI_PD_DEVICE (device); - g_autoptr(GBytes) fw = NULL; - fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_VERIFY); - fw = fu_vli_device_spi_read (FU_VLI_DEVICE (self), 0x0, - fu_device_get_firmware_size_max (device), - error); - if (fw == NULL) + g_autoptr(FuDeviceLocker) locker = NULL; + + /* require detach -> attach */ + locker = fu_device_locker_new_full (device, + (FuDeviceLockerFunc) fu_device_detach, + (FuDeviceLockerFunc) fu_device_attach, + error); + if (locker == NULL) return NULL; - return fu_firmware_new_from_bytes (fw); + fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_READ); + return fu_vli_device_spi_read (FU_VLI_DEVICE (self), 0x0, + fu_device_get_firmware_size_max (device), + error); } static gboolean @@ -420,7 +425,7 @@ fu_vli_pd_device_write_dual_firmware (FuVliPdDevice *self, GBytes *fw, GError ** g_prefix_error (error, "failed to read file CRC: "); return FALSE; } - crc_actual = fu_vli_common_crc16 (sbuf, sbufsz - 2); + crc_actual = fu_common_crc16 (sbuf, sbufsz - 2); fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_WRITE); /* update fw2 first if fw1 correct */ @@ -689,7 +694,7 @@ fu_vli_pd_device_class_init (FuVliPdDeviceClass *klass) { FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuVliDeviceClass *klass_vli_device = FU_VLI_DEVICE_CLASS (klass); - klass_device->read_firmware = fu_vli_pd_device_read_firmware; + klass_device->dump_firmware = fu_vli_pd_device_dump_firmware; klass_device->write_firmware = fu_vli_pd_device_write_firmware; klass_device->prepare_firmware = fu_vli_pd_device_prepare_firmware; klass_device->attach = fu_vli_pd_device_attach; diff --git a/plugins/vli/fu-vli-pd-firmware.c b/plugins/vli/fu-vli-pd-firmware.c index a3391ce9f..176ac6a7b 100644 --- a/plugins/vli/fu-vli-pd-firmware.c +++ b/plugins/vli/fu-vli-pd-firmware.c @@ -7,6 +7,8 @@ #include "config.h" +#include "fu-common.h" + #include "fu-vli-pd-common.h" #include "fu-vli-pd-firmware.h" @@ -46,6 +48,8 @@ fu_vli_pd_firmware_validate_header (FuVliPdFirmware *self) return TRUE; if (GUINT16_FROM_LE (self->hdr.vid) == 0x17EF) return TRUE; + if (GUINT16_FROM_LE (self->hdr.vid) == 0x2D01) + return TRUE; return FALSE; } @@ -128,7 +132,7 @@ fu_vli_pd_firmware_parse (FuFirmware *firmware, } /* check CRC */ - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { guint16 crc_actual; guint16 crc_file = 0x0; if (!fu_common_read_uint16_safe (buf, bufsz, bufsz - 2, &crc_file, @@ -136,7 +140,7 @@ fu_vli_pd_firmware_parse (FuFirmware *firmware, g_prefix_error (error, "failed to read file CRC: "); return FALSE; } - crc_actual = fu_vli_common_crc16 (buf, bufsz - 2); + crc_actual = fu_common_crc16 (buf, bufsz - 2); if (crc_actual != crc_file) { g_set_error (error, FWUPD_ERROR, diff --git a/plugins/vli/fu-vli-pd-parade-device.c b/plugins/vli/fu-vli-pd-parade-device.c index 6da3d283a..deab6d632 100644 --- a/plugins/vli/fu-vli-pd-parade-device.c +++ b/plugins/vli/fu-vli-pd-parade-device.c @@ -579,8 +579,8 @@ fu_vli_pd_parade_device_write_firmware (FuDevice *device, return TRUE; } -static FuFirmware * -fu_vli_pd_parade_device_read_firmware (FuDevice *device, GError **error) +static GBytes * +fu_vli_pd_parade_device_dump_firmware (FuDevice *device, GError **error) { FuVliPdDevice *parent = FU_VLI_PD_DEVICE (fu_device_get_parent (device)); FuVliPdParadeDevice *self = FU_VLI_PD_PARADE_DEVICE (device); @@ -610,27 +610,7 @@ fu_vli_pd_parade_device_read_firmware (FuDevice *device, GError **error) error)) return NULL; } - return fu_firmware_new_from_bytes (fw); -} - - -static FuFirmware * -fu_vli_pd_parade_device_prepare_firmware (FuDevice *device, - GBytes *fw, - FwupdInstallFlags flags, - GError **error) -{ - /* check size */ - if (g_bytes_get_size (fw) < fu_device_get_firmware_size_min (device)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware too small, got 0x%x, expected >= 0x%x", - (guint) g_bytes_get_size (fw), - (guint) fu_device_get_firmware_size_min (device)); - return NULL; - } - return fu_firmware_new_from_bytes (fw); + return g_steal_pointer (&fw); } static gboolean @@ -677,8 +657,7 @@ fu_vli_pd_parade_device_class_init (FuVliPdParadeDeviceClass *klass) FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); klass_device->to_string = fu_vli_pd_parade_device_to_string; klass_device->probe = fu_vli_pd_parade_device_probe; - klass_device->read_firmware = fu_vli_pd_parade_device_read_firmware; - klass_device->prepare_firmware = fu_vli_pd_parade_device_prepare_firmware; + klass_device->dump_firmware = fu_vli_pd_parade_device_dump_firmware; klass_device->write_firmware = fu_vli_pd_parade_device_write_firmware; } diff --git a/plugins/vli/fu-vli-usbhub-common.c b/plugins/vli/fu-vli-usbhub-common.c index e45ee85d8..3fa77f964 100644 --- a/plugins/vli/fu-vli-usbhub-common.c +++ b/plugins/vli/fu-vli-usbhub-common.c @@ -12,7 +12,7 @@ guint8 fu_vli_usbhub_header_crc8 (FuVliUsbhubHeader *hdr) { - return fu_vli_common_crc8 ((const guint8 *) hdr, sizeof(*hdr) - 1); + return ~fu_common_crc8 ((const guint8 *) hdr, sizeof(*hdr) - 1); } void diff --git a/plugins/vli/fu-vli-usbhub-device.c b/plugins/vli/fu-vli-usbhub-device.c index 9502d2d97..b8f96b9e7 100644 --- a/plugins/vli/fu-vli-usbhub-device.c +++ b/plugins/vli/fu-vli-usbhub-device.c @@ -419,14 +419,16 @@ fu_vli_usbhub_device_guess_kind (FuVliUsbhubDevice *self, GError **error) g_prefix_error (error, "Read_820Q7Q8 failed: "); return FALSE; } - g_debug ("chipver = 0x%02x", chipver); - g_debug ("chipver2 = 0x%02x", chipver2); - g_debug ("b811P812 = 0x%02x", b811P812); - g_debug ("chipid1 = 0x%02x", chipid1); - g_debug ("chipid2 = 0x%02x", chipid2); - g_debug ("chipid12 = 0x%02x", chipid12); - g_debug ("chipid22 = 0x%02x", chipid22); - g_debug ("b820Q7Q8 = 0x%02x", b820Q7Q8); + if (g_getenv ("FWUPD_VLI_USBHUB_VERBOSE") != NULL) { + g_debug ("chipver = 0x%02x", chipver); + g_debug ("chipver2 = 0x%02x", chipver2); + g_debug ("b811P812 = 0x%02x", b811P812); + g_debug ("chipid1 = 0x%02x", chipid1); + g_debug ("chipid2 = 0x%02x", chipid2); + g_debug ("chipid12 = 0x%02x", chipid12); + g_debug ("chipid22 = 0x%02x", chipid22); + g_debug ("b820Q7Q8 = 0x%02x", b820Q7Q8); + } if (chipid2 == 0x35 && chipid1 == 0x07) { fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL210); @@ -496,43 +498,22 @@ fu_vli_usbhub_device_probe (FuDevice *device, GError **error) static gboolean fu_vli_usbhub_device_pd_setup (FuVliUsbhubDevice *self, GError **error) { - FuVliPdHdr hdr = { 0x0 }; g_autoptr(FuDevice) dev = NULL; g_autoptr(GError) error_local = NULL; - /* legacy location */ - if (!fu_vli_device_spi_read_block (FU_VLI_DEVICE (self), - VLI_USBHUB_FLASHMAP_ADDR_PD_LEGACY + - VLI_USBHUB_PD_FLASHMAP_ADDR_LEGACY, - (guint8 *) &hdr, sizeof(hdr), error)) { - g_prefix_error (error, "failed to read legacy PD header"); - return FALSE; - } - - /* new location */ - if (GUINT16_FROM_LE (hdr.vid) != 0x2109) { - g_debug ("PD VID was 0x%04x trying new location", - GUINT16_FROM_LE (hdr.vid)); - if (!fu_vli_device_spi_read_block (FU_VLI_DEVICE (self), - VLI_USBHUB_FLASHMAP_ADDR_PD + - VLI_USBHUB_PD_FLASHMAP_ADDR, - (guint8 *) &hdr, sizeof(hdr), error)) { - g_prefix_error (error, "failed to read PD header"); - return FALSE; - } - } - - /* just empty space */ - if (hdr.fwver == G_MAXUINT32) { - g_debug ("no PD device header found"); - return TRUE; - } - /* add child */ - dev = fu_vli_usbhub_pd_device_new (&hdr); - fu_device_set_quirks (dev, fu_device_get_quirks (FU_DEVICE (self))); - if (!fu_device_probe (dev, &error_local)) { - g_warning ("cannot create PD device: %s", error_local->message); + dev = fu_vli_usbhub_pd_device_new (self); + if (!fu_device_probe (dev, error)) + return FALSE; + if (!fu_device_setup (dev, &error_local)) { + if (g_error_matches (error_local, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND)) { + g_debug ("%s", error_local->message); + } else { + g_warning ("cannot create PD device: %s", + error_local->message); + } return TRUE; } fu_device_add_child (FU_DEVICE (self), dev); @@ -693,28 +674,7 @@ fu_vli_usbhub_device_prepare_firmware (FuDevice *device, guint16 device_id; g_autoptr(FuFirmware) firmware = fu_vli_usbhub_firmware_new (); - /* check size */ - if (g_bytes_get_size (fw) < fu_device_get_firmware_size_min (device)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware too small, got 0x%x, expected >= 0x%x", - (guint) g_bytes_get_size (fw), - (guint) fu_device_get_firmware_size_min (device)); - return NULL; - } - if (g_bytes_get_size (fw) > fu_device_get_firmware_size_max (device)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware too large, got 0x%x, expected <= 0x%x", - (guint) g_bytes_get_size (fw), - (guint) fu_device_get_firmware_size_max (device)); - return NULL; - } - /* check is compatible with firmware */ - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; device_kind = fu_vli_usbhub_firmware_get_device_kind (FU_VLI_USBHUB_FIRMWARE (firmware)); @@ -969,18 +929,14 @@ fu_vli_usbhub_device_update_v2 (FuVliUsbhubDevice *self, FuFirmware *firmware, G return TRUE; } -static FuFirmware * -fu_vli_usbhub_device_read_firmware (FuDevice *device, GError **error) +static GBytes * +fu_vli_usbhub_device_dump_firmware (FuDevice *device, GError **error) { FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE (device); - g_autoptr(GBytes) fw = NULL; - fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_VERIFY); - fw = fu_vli_device_spi_read (FU_VLI_DEVICE (self), 0x0, - fu_device_get_firmware_size_max (device), - error); - if (fw == NULL) - return NULL; - return fu_firmware_new_from_bytes (fw); + fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_READ); + return fu_vli_device_spi_read (FU_VLI_DEVICE (self), 0x0, + fu_device_get_firmware_size_max (device), + error); } static gboolean @@ -1028,7 +984,7 @@ fu_vli_usbhub_device_class_init (FuVliUsbhubDeviceClass *klass) FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuVliDeviceClass *klass_vli_device = FU_VLI_DEVICE_CLASS (klass); klass_device->probe = fu_vli_usbhub_device_probe; - klass_device->read_firmware = fu_vli_usbhub_device_read_firmware; + klass_device->dump_firmware = fu_vli_usbhub_device_dump_firmware; klass_device->write_firmware = fu_vli_usbhub_device_write_firmware; klass_device->prepare_firmware = fu_vli_usbhub_device_prepare_firmware; klass_device->attach = fu_vli_usbhub_device_attach; diff --git a/plugins/vli/fu-vli-usbhub-msp430-device.c b/plugins/vli/fu-vli-usbhub-msp430-device.c index e7597b8fd..0a2cdabed 100644 --- a/plugins/vli/fu-vli-usbhub-msp430-device.c +++ b/plugins/vli/fu-vli-usbhub-msp430-device.c @@ -149,8 +149,7 @@ fu_vli_usbhub_msp430_device_detach (FuDevice *device, GError **error) /* avoid power instability by waiting T1 */ fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); - fu_device_set_progress (device, 0); - g_usleep (G_USEC_PER_SEC); + fu_device_sleep_with_progress (device, 1); /* seconds */ /* check the device came back */ if (!fu_vli_usbhub_device_i2c_read_status (parent, &status, error)) { @@ -167,7 +166,6 @@ fu_vli_usbhub_msp430_device_prepare_firmware (FuDevice *device, GError **error) { g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new (); - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_tokenize (firmware, fw, flags, error)) return NULL; return g_steal_pointer (&firmware); @@ -246,28 +244,8 @@ fu_vli_usbhub_msp430_device_write_firmware (FuDevice *device, FuVliUsbhubDeviceRequest req = { 0x0 }; const gchar *line = rcd->buf->str; - /* check there's enough data for the smallest possible record */ - if (rcd->buf->len < 11) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "line %u is incomplete, length %u", - rcd->ln, (guint) rcd->buf->len); - return FALSE; - } - - /* check starting token */ - if (line[0] != ':') { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid starting token on line %u: %s", - rcd->ln, line); - return FALSE; - } - /* length, 16-bit address, type */ - req.len = fu_firmware_strparse_uint8 (line + 1); + req.len = rcd->byte_cnt; if (req.len >= sizeof(req.buf) - 7) { g_set_error (error, FWUPD_ERROR, diff --git a/plugins/vli/fu-vli-usbhub-pd-device.c b/plugins/vli/fu-vli-usbhub-pd-device.c index 9209e6436..684be5560 100644 --- a/plugins/vli/fu-vli-usbhub-pd-device.c +++ b/plugins/vli/fu-vli-usbhub-pd-device.c @@ -19,7 +19,6 @@ struct _FuVliUsbhubPdDevice { FuDevice parent_instance; - FuVliPdHdr hdr; FuVliDeviceKind device_kind; }; @@ -38,10 +37,11 @@ fu_vli_usbhub_pd_device_to_string (FuDevice *device, guint idt, GString *str) } static gboolean -fu_vli_usbhub_pd_device_probe (FuDevice *device, GError **error) +fu_vli_usbhub_pd_device_setup (FuDevice *device, GError **error) { + FuVliPdHdr hdr = { 0x0 }; FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device); - + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE (fu_device_get_parent (device)); guint32 fwver; g_autofree gchar *fwver_str = NULL; g_autofree gchar *instance_id0 = NULL; @@ -49,8 +49,39 @@ fu_vli_usbhub_pd_device_probe (FuDevice *device, GError **error) g_autofree gchar *instance_id2 = NULL; g_autofree gchar *instance_id3 = NULL; + /* legacy location */ + if (!fu_vli_device_spi_read_block (FU_VLI_DEVICE (parent), + VLI_USBHUB_FLASHMAP_ADDR_PD_LEGACY + + VLI_USBHUB_PD_FLASHMAP_ADDR_LEGACY, + (guint8 *) &hdr, sizeof(hdr), error)) { + g_prefix_error (error, "failed to read legacy PD header"); + return FALSE; + } + + /* new location */ + if (GUINT16_FROM_LE (hdr.vid) != 0x2109) { + g_debug ("PD VID was 0x%04x trying new location", + GUINT16_FROM_LE (hdr.vid)); + if (!fu_vli_device_spi_read_block (FU_VLI_DEVICE (parent), + VLI_USBHUB_FLASHMAP_ADDR_PD + + VLI_USBHUB_PD_FLASHMAP_ADDR, + (guint8 *) &hdr, sizeof(hdr), error)) { + g_prefix_error (error, "failed to read PD header"); + return FALSE; + } + } + + /* just empty space */ + if (hdr.fwver == G_MAXUINT32) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no PD device header found"); + return FALSE; + } + /* get version */ - fwver = GUINT32_FROM_BE (self->hdr.fwver); + fwver = GUINT32_FROM_BE (hdr.fwver); self->device_kind = fu_vli_pd_common_guess_device_kind (fwver); if (self->device_kind == FU_VLI_DEVICE_KIND_UNKNOWN) { g_set_error (error, @@ -66,23 +97,23 @@ fu_vli_usbhub_pd_device_probe (FuDevice *device, GError **error) fwver_str = fu_common_version_from_uint32 (fwver, FWUPD_VERSION_FORMAT_QUAD); fu_device_set_version (device, fwver_str); instance_id0 = g_strdup_printf ("USB\\VID_%04X&PID_%04X&APP_%02X", - GUINT16_FROM_LE (self->hdr.vid), - GUINT16_FROM_LE (self->hdr.pid), + GUINT16_FROM_LE (hdr.vid), + GUINT16_FROM_LE (hdr.pid), fwver & 0xff); fu_device_add_instance_id (device, instance_id0); instance_id1 = g_strdup_printf ("USB\\VID_%04X&PID_%04X&DEV_%s", - GUINT16_FROM_LE (self->hdr.vid), - GUINT16_FROM_LE (self->hdr.pid), + GUINT16_FROM_LE (hdr.vid), + GUINT16_FROM_LE (hdr.pid), fu_vli_common_device_kind_to_string (self->device_kind)); fu_device_add_instance_id (device, instance_id1); /* add standard GUIDs in order of priority */ instance_id2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X", - GUINT16_FROM_LE (self->hdr.vid), - GUINT16_FROM_LE (self->hdr.pid)); + GUINT16_FROM_LE (hdr.vid), + GUINT16_FROM_LE (hdr.pid)); fu_device_add_instance_id (device, instance_id2); instance_id3 = g_strdup_printf ("USB\\VID_%04X", - GUINT16_FROM_LE (self->hdr.vid)); + GUINT16_FROM_LE (hdr.vid)); fu_device_add_instance_id_full (device, instance_id3, FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); @@ -94,6 +125,19 @@ fu_vli_usbhub_pd_device_probe (FuDevice *device, GError **error) return TRUE; } +static gboolean +fu_vli_usbhub_pd_device_reload (FuDevice *device, GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE (fu_device_get_parent (device)); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open parent device */ + locker = fu_device_locker_new (parent, error); + if (locker == NULL) + return FALSE; + return fu_vli_usbhub_pd_device_setup (device, error); +} + static FuFirmware * fu_vli_usbhub_pd_device_prepare_firmware (FuDevice *device, GBytes *fw, @@ -104,28 +148,7 @@ fu_vli_usbhub_pd_device_prepare_firmware (FuDevice *device, FuVliDeviceKind device_kind; g_autoptr(FuFirmware) firmware = fu_vli_pd_firmware_new (); - /* check size */ - if (g_bytes_get_size (fw) < fu_device_get_firmware_size_min (device)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware too small, got 0x%x, expected >= 0x%x", - (guint) g_bytes_get_size (fw), - (guint) fu_device_get_firmware_size_min (device)); - return NULL; - } - if (g_bytes_get_size (fw) > fu_device_get_firmware_size_max (device)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware too large, got 0x%x, expected <= 0x%x", - (guint) g_bytes_get_size (fw), - (guint) fu_device_get_firmware_size_max (device)); - return NULL; - } - /* check is compatible with firmware */ - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; device_kind = fu_vli_pd_firmware_get_kind (FU_VLI_PD_FIRMWARE (firmware)); @@ -144,13 +167,12 @@ fu_vli_usbhub_pd_device_prepare_firmware (FuDevice *device, return g_steal_pointer (&firmware); } -static FuFirmware * -fu_vli_usbhub_pd_device_read_firmware (FuDevice *device, GError **error) +static GBytes * +fu_vli_usbhub_pd_device_dump_firmware (FuDevice *device, GError **error) { FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE (fu_device_get_parent (device)); FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device); g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GBytes) fw = NULL; /* open device */ locker = fu_device_locker_new (parent, error); @@ -158,14 +180,11 @@ fu_vli_usbhub_pd_device_read_firmware (FuDevice *device, GError **error) return NULL; /* read */ - fu_device_set_status (FU_DEVICE (device), FWUPD_STATUS_DEVICE_VERIFY); - fw = fu_vli_device_spi_read (FU_VLI_DEVICE (parent), - fu_vli_common_device_kind_get_offset (self->device_kind), - fu_device_get_firmware_size_max (device), - error); - if (fw == NULL) - return NULL; - return fu_firmware_new_from_bytes (fw); + fu_device_set_status (FU_DEVICE (device), FWUPD_STATUS_DEVICE_READ); + return fu_vli_device_spi_read (FU_VLI_DEVICE (parent), + fu_vli_common_device_kind_get_offset (self->device_kind), + fu_device_get_firmware_size_max (device), + error); } static gboolean @@ -228,6 +247,7 @@ fu_vli_usbhub_pd_device_init (FuVliUsbhubPdDevice *self) fu_device_set_protocol (FU_DEVICE (self), "com.vli.usbhub"); fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NO_GUID_MATCHING); fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_QUAD); fu_device_set_install_duration (FU_DEVICE (self), 15); /* seconds */ fu_device_set_logical_id (FU_DEVICE (self), "PD"); @@ -239,17 +259,19 @@ fu_vli_usbhub_pd_device_class_init (FuVliUsbhubPdDeviceClass *klass) { FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); klass_device->to_string = fu_vli_usbhub_pd_device_to_string; - klass_device->probe = fu_vli_usbhub_pd_device_probe; + klass_device->setup = fu_vli_usbhub_pd_device_setup; + klass_device->reload = fu_vli_usbhub_pd_device_reload; klass_device->attach = fu_vli_usbhub_pd_device_attach; - klass_device->read_firmware = fu_vli_usbhub_pd_device_read_firmware; + klass_device->dump_firmware = fu_vli_usbhub_pd_device_dump_firmware; klass_device->write_firmware = fu_vli_usbhub_pd_device_write_firmware; klass_device->prepare_firmware = fu_vli_usbhub_pd_device_prepare_firmware; } FuDevice * -fu_vli_usbhub_pd_device_new (FuVliPdHdr *hdr) +fu_vli_usbhub_pd_device_new (FuVliUsbhubDevice *parent) { - FuVliUsbhubPdDevice *self = g_object_new (FU_TYPE_VLI_USBHUB_PD_DEVICE, NULL); - memcpy (&self->hdr, hdr, sizeof(self->hdr)); + FuVliUsbhubPdDevice *self = g_object_new (FU_TYPE_VLI_USBHUB_PD_DEVICE, + "parent", parent, + NULL); return FU_DEVICE (self); } diff --git a/plugins/vli/fu-vli-usbhub-pd-device.h b/plugins/vli/fu-vli-usbhub-pd-device.h index 61280f328..cceb4c2c3 100644 --- a/plugins/vli/fu-vli-usbhub-pd-device.h +++ b/plugins/vli/fu-vli-usbhub-pd-device.h @@ -8,8 +8,6 @@ #include "fu-plugin.h" -#include "fu-vli-pd-common.h" - #define FU_TYPE_VLI_USBHUB_PD_DEVICE (fu_vli_usbhub_pd_device_get_type ()) G_DECLARE_FINAL_TYPE (FuVliUsbhubPdDevice, fu_vli_usbhub_pd_device, FU, VLI_USBHUB_PD_DEVICE, FuDevice) @@ -18,4 +16,4 @@ struct _FuVliUsbhubPdDeviceClass FuDeviceClass parent_class; }; -FuDevice *fu_vli_usbhub_pd_device_new (FuVliPdHdr *hdr); +FuDevice *fu_vli_usbhub_pd_device_new (FuVliUsbhubDevice *parent); diff --git a/plugins/vli/fu-vli-usbhub-rtd21xx-device.c b/plugins/vli/fu-vli-usbhub-rtd21xx-device.c index 269ebfa85..e011eb9d4 100644 --- a/plugins/vli/fu-vli-usbhub-rtd21xx-device.c +++ b/plugins/vli/fu-vli-usbhub-rtd21xx-device.c @@ -448,10 +448,7 @@ fu_vli_usbhub_rtd21xx_device_write_firmware (FuDevice *device, /* the device needs some time to restart with the new firmware before * it can be queried again */ - for (guint i = 0; i < 100; i++) { - g_usleep (200000); - fu_device_set_progress (device, i); - } + fu_device_sleep_with_progress (device, 20); /* success */ return TRUE; diff --git a/plugins/vli/meson.build b/plugins/vli/meson.build index 8c3deb891..e3e8599da 100644 --- a/plugins/vli/meson.build +++ b/plugins/vli/meson.build @@ -4,6 +4,7 @@ install_data([ 'vli-pd.quirk', 'vli-usbhub.quirk', 'vli-usbhub-lenovo.quirk', + 'vli-usbhub-hyper.quirk', ], install_dir: join_paths(datadir, 'fwupd', 'quirks.d') ) @@ -44,8 +45,6 @@ shared_module('fu_plugin_vli', ) if get_option('tests') - testdatadir = join_paths(meson.current_source_dir(), 'data') - cargs += '-DTESTDATADIR="' + testdatadir + '"' e = executable( 'vli-self-test', fu_hash, @@ -65,7 +64,9 @@ if get_option('tests') fwupd, fwupdplugin, ], - c_args : cargs + c_args : cargs, + install : true, + install_dir : installed_test_bindir, ) - test('vli-self-test', e) + test('vli-self-test', e) # added to installed-tests endif diff --git a/plugins/vli/vli-pd.quirk b/plugins/vli/vli-pd.quirk index 2c8a39e61..694b4a1df 100644 --- a/plugins/vli/vli-pd.quirk +++ b/plugins/vli/vli-pd.quirk @@ -62,9 +62,6 @@ GType = FuVliPdDevice [DeviceInstanceId=USB\VID_17EF&PID_7217] Plugin = vli GType = FuVliPdDevice -[DeviceInstanceId=USB\VID_17EF&PID_721C] -Plugin = vli -GType = FuVliPdDevice [DeviceInstanceId=USB\VID_17EF&PID_7223] Plugin = vli GType = FuVliPdDevice diff --git a/plugins/vli/vli-usbhub-hyper.quirk b/plugins/vli/vli-usbhub-hyper.quirk new file mode 100644 index 000000000..1e90f42a8 --- /dev/null +++ b/plugins/vli/vli-usbhub-hyper.quirk @@ -0,0 +1,11 @@ +# Hyper USB-C Hub +[DeviceInstanceId=USB\VID_2D01&PID_0385] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3,has-shared-spi-pd +CounterpartGuid = USB\VID_2D01&PID_0382 +[DeviceInstanceId=USB\VID_2D01&PID_0382] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_2D01&PID_0385 diff --git a/plugins/vli/vli-usbhub-lenovo.quirk b/plugins/vli/vli-usbhub-lenovo.quirk index e35fc5a9c..0317e247e 100644 --- a/plugins/vli/vli-usbhub-lenovo.quirk +++ b/plugins/vli/vli-usbhub-lenovo.quirk @@ -39,7 +39,7 @@ ParentGuid = USB\VID_17EF&PID_307F&HUB_0002 [DeviceInstanceId=USB\VID_17EF&PID_3080] Plugin = vli GType = FuVliUsbhubDevice -Flags = usb2,has-msp430 +Flags = usb2 [DeviceInstanceId=USB\VID_17EF&PID_3080&HUB_06] ParentGuid = USB\VID_17EF&PID_3080&HUB_20 [DeviceInstanceId=USB\VID_17EF&PID_3080&HUB_20] @@ -146,6 +146,7 @@ CounterpartGuid = USB\VID_17EF&PID_7224 Plugin = vli GType = FuVliUsbhubDevice Flags = usb2 +ParentGuid = USB\VID_17EF&PID_7216 # Lenovo Travel hub Gen2 [DeviceInstanceId=USB\VID_17EF&PID_721D] @@ -156,7 +157,8 @@ CounterpartGuid = USB\VID_17EF&PID_7225 [DeviceInstanceId=USB\VID_17EF&PID_7225] Plugin = vli GType = FuVliUsbhubDevice -Flags = usb2,has-shared-spi-pd +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_721D # Lenovo USB-C Mini dock [DeviceInstanceId=USB\VID_17EF&PID_3094] @@ -195,6 +197,7 @@ CounterpartGuid = USB\VID_17EF Plugin = vli GType = FuVliUsbhubDevice Flags = usb2 +ParentGuid = USB\VID_17EF&PID_7228 # Lenovo USB-C 7-in-1 Hub [DeviceInstanceId=USB\VID_17EF&PID_722A] @@ -205,7 +208,8 @@ CounterpartGuid = USB\VID_17EF&PID_7229 [DeviceInstanceId=USB\VID_17EF&PID_7229] Plugin = vli GType = FuVliUsbhubDevice -Flags = usb2,has-shared-spi-pd +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_722A # Lenovo USB-C to 4 USB-A Hub [DeviceInstanceId=USB\VID_17EF&PID_1039] @@ -216,7 +220,8 @@ CounterpartGuid = USB\VID_17EF&PID_103A [DeviceInstanceId=USB\VID_17EF&PID_103A] Plugin = vli GType = FuVliUsbhubDevice -Flags = usb2,has-shared-spi-pd,attach-with-gpiob +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_1039 # Lenovo Gen2 dock [DeviceInstanceId=USB\VID_17EF&PID_A391] @@ -265,9 +270,3 @@ Plugin = vli GType = FuVliUsbhubDevice Flags = usb2 ParentGuid = USB\VID_2109&PID_2822 - -# Lenovo Powered Hub -[DeviceInstanceId=USB\VID_17EF&PID_721C] -Plugin = vli -GType = FuVliUsbhubDevice -Flags = usb2 diff --git a/plugins/wacom-raw/README.md b/plugins/wacom-raw/README.md index 24eb155d9..0bf6afae0 100644 --- a/plugins/wacom-raw/README.md +++ b/plugins/wacom-raw/README.md @@ -36,3 +36,7 @@ Vendor ID Security ------------------ The vendor ID is set from the udev vendor, in this instance set to `HIDRAW:0x056A` + +External interface access +------------------------- +This plugin requires ioctl `HIDIOCSFEATURE` access. diff --git a/plugins/wacom-raw/fu-wacom-aes-device.c b/plugins/wacom-raw/fu-wacom-aes-device.c index 033a65004..61d11bcab 100644 --- a/plugins/wacom-raw/fu-wacom-aes-device.c +++ b/plugins/wacom-raw/fu-wacom-aes-device.c @@ -164,7 +164,7 @@ fu_wacom_aes_device_erase_all (FuWacomAesDevice *self, GError **error) g_prefix_error (error, "failed to send eraseall command: "); return FALSE; } - g_usleep (2 * G_USEC_PER_SEC); + fu_device_sleep_with_progress (FU_DEVICE (self), 2); /* seconds */ return TRUE; } diff --git a/plugins/wacom-usb/README.md b/plugins/wacom-usb/README.md index 7db56c244..5f75ba0e3 100644 --- a/plugins/wacom-usb/README.md +++ b/plugins/wacom-usb/README.md @@ -9,7 +9,7 @@ inspire everyone make the world a more creative place. From 2016 Wacom has been using a HID-based proprietary flashing algorithm which has been documented by support team at Wacom and provided under NDA under the -understanding it would be used to build a plugin under a LGPLv2+ licence. +understanding it would be used to build a plugin under a LGPLv2+ license. Wacom devices are actually composite devices, with the main ARM CPU being programmed using a more complicated erase, write, verify algorithm based @@ -44,3 +44,7 @@ Vendor ID Security ------------------ The vendor ID is set from the USB vendor, for example set to `USB:0x056A` + +External interface access +------------------------- +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/wacom-usb/fu-self-test.c b/plugins/wacom-usb/fu-self-test.c index eb5d27ed3..78de13a3a 100644 --- a/plugins/wacom-usb/fu-self-test.c +++ b/plugins/wacom-usb/fu-self-test.c @@ -27,7 +27,7 @@ fu_wac_firmware_parse_func (void) g_autoptr(GError) error = NULL; /* parse the test file */ - fn = g_build_filename (TESTDATADIR, "test.wac", NULL); + fn = g_test_build_filename (G_TEST_DIST, "tests", "test.wac", NULL); if (!g_file_test (fn, G_FILE_TEST_EXISTS)) { g_test_skip ("no data file found"); return; diff --git a/plugins/wacom-usb/fu-wac-device.c b/plugins/wacom-usb/fu-wac-device.c index a7ed34fb5..b49e80c72 100644 --- a/plugins/wacom-usb/fu-wac-device.c +++ b/plugins/wacom-usb/fu-wac-device.c @@ -435,7 +435,6 @@ fu_wac_device_prepare_firmware (FuDevice *device, GError **error) { g_autoptr(FuFirmware) firmware = fu_wac_firmware_new (); - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; return g_steal_pointer (&firmware); diff --git a/plugins/wacom-usb/meson.build b/plugins/wacom-usb/meson.build index 6a861254e..e8c5c8af6 100644 --- a/plugins/wacom-usb/meson.build +++ b/plugins/wacom-usb/meson.build @@ -33,8 +33,9 @@ shared_module('fu_plugin_wacom_usb', ) if get_option('tests') - testdatadir = join_paths(meson.current_source_dir(), 'tests') - cargs += '-DTESTDATADIR="' + testdatadir + '"' + testdatadirs = environment() + testdatadirs.set('G_TEST_SRCDIR', meson.current_source_dir()) + testdatadirs.set('G_TEST_BUILDDIR', meson.current_build_dir()) e = executable( 'wacom-usb-self-test', fu_hash, @@ -59,7 +60,9 @@ if get_option('tests') fwupd, fwupdplugin, ], - c_args : cargs + c_args : cargs, + install : true, + install_dir : installed_test_bindir, ) - test('wacom-usb-self-test', e) + test('wacom-usb-self-test', e, env : testdatadirs) # added to installed-tests endif diff --git a/po/LINGUAS b/po/LINGUAS index cf53114c8..9c5acfebb 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -10,6 +10,7 @@ eu fi fr fur +gl he hi hr diff --git a/po/POTFILES.in b/po/POTFILES.in index 312e3819e..3b44e8c43 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -13,6 +13,7 @@ src/fu-main.c src/fu-offline.c src/fu-progressbar.c src/fu-remote-list.c +src/fu-security-attr.c src/fu-tool.c src/fu-util.c src/fu-util-common.c diff --git a/po/af.po b/po/af.po index 4af7811a6..38670f230 100644 --- a/po/af.po +++ b/po/af.po @@ -123,6 +123,7 @@ msgstr "Toestelle wat suksesvol bygewerk is:" msgid "Devices that were not updated correctly:" msgstr "Toestelle wat nie korrek bygewerk is nie:" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Klaar!" @@ -143,6 +144,8 @@ msgstr "Gradeer tans %s af…" msgid "Downloading…" msgstr "Laai tans af…" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Geaktiveer" @@ -151,22 +154,6 @@ msgstr "Geaktiveer" msgid "Erasing…" msgstr "Vee tans uit…" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Kon nie aflaai nie a.g.v. bedienerlimiet" - -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Kry tans lêer" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Kry tans metadata" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Kry tans handtekening" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "Lêernaam" @@ -175,6 +162,10 @@ msgstr "Lêernaam" msgid "Filename Signature" msgstr "Lêernaamhandtekening" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Gevind" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Ledig…" @@ -199,6 +190,10 @@ msgstr "Laai tans…" msgid "Metadata URI" msgstr "Metadata-URI" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Goed" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Wagwoord" @@ -285,10 +280,6 @@ msgstr "Werk tans %s by vanaf %s na %s… " msgid "Updating %s…" msgstr "Werk tans %s by…" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Oplaaiboodskap:" - #. TRANSLATORS: ask the user to upload msgid "Upload report now?" msgstr "Laai verslag nou op?" @@ -301,6 +292,10 @@ msgstr "Gebruikernaam" msgid "Verifying…" msgstr "Verifieer tans…" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Weergawe" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Wag tans…" diff --git a/po/ca.po b/po/ca.po index 89f8517cf..ac89622a8 100644 --- a/po/ca.po +++ b/po/ca.po @@ -29,6 +29,12 @@ msgstr[1] "Manquen %.0f minuts" msgid "%s CPU Microcode Update" msgstr "%s Actualitza el microprogramari de la CPU" +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Actualització de la configuració %s" + #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format @@ -52,7 +58,7 @@ msgstr "Actualització ME corporativa de %s" #. * the first %s is the device name, e.g. 'Unifying Receiver` #, c-format msgid "%s Device Update" -msgstr "Actualizació del dispositiu %s" +msgstr "Actualització del dispositiu %s" #. TRANSLATORS: the EC is typically the keyboard controller chip, #. * the first %s is the device name, e.g. 'ThinkPad P50` @@ -70,7 +76,7 @@ msgstr "Actualització ME de %s" #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s System Update" -msgstr "Actualizació del sistema %s" +msgstr "Actualització del sistema %s" #. TRANSLATORS: the Thunderbolt controller is a device that #. * has other high speed Thunderbolt devices plugged into it; @@ -92,15 +98,35 @@ msgstr "Actualització de %s" msgid "%s and all connected devices may not be usable while updating." msgstr "%s i tots els dispositius connectats poden no ser usables en actualitzar." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Mode de fabricació %s" + #. TRANSLATORS: warn the user before updating, %1 is a device name #, c-format msgid "%s must remain connected for the duration of the update to avoid damage." -msgstr "%s haurà d'estar connectat durant tota l'actualització per evitar danys." +msgstr "%s haurà d'estar connectat durant tota l'actualització per a evitar danys." #. TRANSLATORS: warn the user before updating, %1 is a machine name #, c-format msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." -msgstr "%s haurà de romandre connectat a una font d'alimentació durant tota l'actualització per evitar danys." +msgstr "%s haurà de romandre connectat a una font d'alimentació durant tota l'actualització per a evitar danys." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "Superposa %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s versió %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Versió %s" #. TRANSLATORS: duration in days! #, c-format @@ -143,6 +169,10 @@ msgid_plural "%u seconds" msgstr[0] "%u segon" msgstr[1] "%u segons" +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(obsolet)" + #. TRANSLATORS: command description msgid "Activate devices" msgstr "Activa els dispositius" @@ -160,7 +190,7 @@ msgstr "Activació de l'actualització del microprogramari" #. TRANSLATORS: shown when shutting down to switch to the new version msgid "Activating firmware update for" -msgstr "Activa l’actualització del microprogramari per a" +msgstr "Activa l'actualització del microprogramari per a" #. TRANSLATORS: this is when a device is hotplugged msgid "Added" @@ -177,7 +207,7 @@ msgstr "Accepteu i habiliteu el remot?" #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format msgid "Alias to %s" -msgstr "Àlies per a %s" +msgstr "Àlies per a «%s»" #. TRANSLATORS: command line option msgid "Allow downgrading firmware versions" @@ -187,6 +217,10 @@ msgstr "Permet tornar a la versió anterior del microprogramari" msgid "Allow reinstalling existing firmware versions" msgstr "Permetre tornar a instal·lar les versions existents del microprogramari" +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Permet canviar de branca de microprogramari" + #. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Una actualització requereix un reinici per a completar-se." @@ -201,7 +235,19 @@ msgstr "Respon sí a totes les preguntes" #. TRANSLATORS: command line option msgid "Apply firmware updates" -msgstr "Aplica les actualizacions de microprogramari" +msgstr "Aplica les actualitzacions de microprogramari" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Aplica una actualització fins i tot quan no se us aconselli" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Aplica els fitxers d'actualització" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "S'està aplicant l'actualització" #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator @@ -210,6 +256,10 @@ msgid_plural "Approved firmware:" msgstr[0] "Microprogramari aprovat:" msgstr[1] "Microprogramari aprovat:" +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Demana-m'ho de nou la propera vegada?" + #. TRANSLATORS: command description msgid "Attach to firmware mode" msgstr "Ajunta al mode microprogramari" @@ -220,7 +270,7 @@ msgstr "S'està autenticant..." #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on a removable device" -msgstr "Es requereix autenticació per a desactualitzar el microprogramari en un dispositiu extraible" +msgstr "Es requereix autenticació per a desactualitzar el microprogramari en un dispositiu extraïble" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on this machine" @@ -244,7 +294,7 @@ msgstr "Es requereix autenticació per a signar les dades emprant el certificat #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to switch to the new firmware version" -msgstr "Es requereix autenticació per a canviar a la nova versió del micropogramari" +msgstr "Es requereix autenticació per a canviar a la nova versió del microprogramari" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" @@ -252,24 +302,52 @@ msgstr "Es requereix autenticació per a desbloquejar un dispositiu" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on a removable device" -msgstr "Es requereix autenticació per actualitzar el microprogramari en un dispositiu extraible" +msgstr "Es requereix autenticació per a actualitzar el microprogramari en un dispositiu extraïble" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on this machine" -msgstr "Es requereix autenticació per actualitzar el microprogramari en aquesta màquina" +msgstr "Es requereix autenticació per a actualitzar el microprogramari en aquesta màquina" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the stored checksums for the device" -msgstr "Es requereix autenticació per actualitzar les sumes de verificació emmagatzemades pels dispositius" +msgstr "Es requereix autenticació per a actualitzar les sumes de verificació emmagatzemades pels dispositius" #. TRANSLATORS: Boolean value to automatically send reports msgid "Automatic Reporting" msgstr "Informa automàticament" +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "El pujo automàticament cada vegada?" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Vincula el controlador actual" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Fitxers del microprogramari bloquejat:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Microprogramari bloquejat:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Bloqueja la instal·lació d'un microprogramari específic" + #. TRANSLATORS: firmware version of bootloader msgid "Bootloader Version" msgstr "Versió del carregador d'arrencada" +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Branca" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Construeix un fitxer de microprogramari" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Construeix el microprogramari usant un entorn de proves" @@ -282,6 +360,14 @@ msgstr "Cancel·la" msgid "Cancelled" msgstr "S'ha cancel·lat" +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "No s'ha pogut aplicar perquè l'actualització de la dbx ja s'ha aplicat." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "No s'han pogut aplicar les actualitzacions en els suports en viu" + #. TRANSLATORS: this is when a device is hotplugged #. TRANSLATORS: this is when the daemon state changes msgid "Changed" @@ -295,6 +381,11 @@ msgstr "Comprova que la suma criptogràfica coincideix amb el microprogramari" msgid "Checksum" msgstr "Suma de comprovació" +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Trieu una branca:" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Trieu un dispositiu:" @@ -307,9 +398,13 @@ msgstr "Trieu un tipus de microprogramari:" msgid "Choose a release:" msgstr "Trieu un alliberament:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Trieu un volum:" + #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" -msgstr "Neteja qualsevol actualització programyada per a ser actualitzada sense connexió" +msgstr "Neteja qualsevol actualització programada per a ser actualitzada sense connexió" #. TRANSLATORS: command description msgid "Clears the results from the last update" @@ -397,7 +492,7 @@ msgstr "El dispositiu està bloquejat" #. TRANSLATORS: a version check is required for all firmware msgid "Device is required to install all provided releases" -msgstr "El dispositiu és necessari per instal·lar totes les versions llançades" +msgstr "El dispositiu és necessari per a instal·lar totes les versions subministrades" #. TRANSLATORS: Device remains usable during update msgid "Device is usable for the duration of the update" @@ -411,10 +506,18 @@ msgstr "S'ha eliminat el dispositiu:" msgid "Device stages updates" msgstr "Etapes d'actualització del dispositiu" +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "El dispositiu admet el canvi a una branca diferent de microprogramari" + #. TRANSLATORS: Device update needs to be separately activated msgid "Device update needs activation" msgstr "Cal activar l'actualització del dispositiu" +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "El dispositiu farà una còpia de seguretat del microprogramari abans d'instal·lar" + #. TRANSLATORS: Device will not return after update completes msgid "Device will not re-appear after update completes" msgstr "El dispositiu no tornarà a aparèixer un cop finalitzada l'actualització" @@ -427,6 +530,20 @@ msgstr "Els dispositius que s'han actualitzat correctament:" msgid "Devices that were not updated correctly:" msgstr "Els dispositius que no s'han actualitzat correctament:" +#. TRANSLATORS: message letting the user know no device upgrade available due +#. to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Dispositius sense actualitzacions de microprogramari disponibles:" + +#. TRANSLATORS: message letting the user know no device upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "Dispositius amb la versió més recent del microprogramari disponible:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Inhabilitada" + msgid "Disabled fwupdate debugging" msgstr "La depuració del «fwupdate» està inhabilitada" @@ -476,6 +593,11 @@ msgstr[1] "No enviar els informes, i no sol·licitar-ho mai per a les futures ac msgid "Do not write to the history database" msgstr "No escriure a la base de dades de l'historial" +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Enteneu les conseqüències de canviar la branca del microprogramari?" + +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Fet!" @@ -520,6 +642,8 @@ msgstr "Habilita el suport per a l'actualització del microprogramari sobre sist msgid "Enable this remote?" msgstr "Habilito aquest remot?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Habilitat" @@ -538,6 +662,14 @@ msgstr "Si habiliteu aquesta funcionalitat, ho fareu sota el vostre propi risc, msgid "Enabling this remote is done at your own risk." msgstr "Si habiliteu aquest remot, ho fareu sota el vostre propi risc." +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Encriptada" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM encriptada" + #. TRANSLATORS: command description msgid "Erase all firmware update history" msgstr "Esborra tot l'historial de les actualitzacions de microprogramari" @@ -554,13 +686,25 @@ msgstr "Surt després d'un petit retard" msgid "Exit after the engine has loaded" msgstr "Sur una vegada s'hagi carregat el motor" +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Extreu un blob de microprogramari en imatges" + +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Ha fallat" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Ha fallat en aplicar l'actualització" + #. TRANSLATORS: we could not talk to the fwupd daemon msgid "Failed to connect to daemon" msgstr "Ha fallat en connectar amb el dimoni" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "La baixada ha fallat a causa del límit del servidor" +#. TRANSLATORS: could not parse file +msgid "Failed to extract local dbx " +msgstr "Ha fallat en extreure una dbx local" #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" @@ -568,12 +712,21 @@ msgstr "Ha fallat en obtenir els dispositius pendents" #. TRANSLATORS: we could not install for some reason msgid "Failed to install firmware update" -msgstr "Ha fallat en instal·lar l’actualització del microprogramari" +msgstr "Ha fallat en instal·lar l'actualització del microprogramari" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Ha fallat en carregar una dbx local" #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "No s'han pogut carregar les peculiaritats" +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Ha fallat en carregar una dbx del sistema" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Ha fallat en analitzar els arguments" @@ -586,6 +739,10 @@ msgstr "Ha fallat en analitzar el fitxer" msgid "Failed to parse flags for --filter" msgstr "Ha fallat en analitzar les etiquetes per a --filter" +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Ha fallat en analitzar una dbx local" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Ha fallat en tornar a arrencar" @@ -594,21 +751,10 @@ msgstr "Ha fallat en tornar a arrencar" msgid "Failed to set splash mode" msgstr "Ha fallat en establir el mode de presentació" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "S'està obtenint el fitxer" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "S'està obtenint el microprogramari" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "S'estan obtenint les metadades" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "S'està obtenint la signatura" +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Ha fallat en validar el contingut ESP" #. TRANSLATORS: filename of the local file msgid "Filename" @@ -618,6 +764,10 @@ msgstr "Nom del fitxer" msgid "Filename Signature" msgstr "Signatura del nom del fitxer" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "El nom del fitxer és obligatori" + #. TRANSLATORS: command line option msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" msgstr "Filtra amb un conjunt d'etiquetes del dispositiu amb un prefix «~» per a excloure, p. ex., «internal,~needs-reboot»" @@ -642,6 +792,22 @@ msgstr "Dimoni per a l'actualització de microprogramari" msgid "Firmware Utility" msgstr "Utilitat per al microprogramari" +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Atestat del microprogramari" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "Firmware can not be updated in legacy BIOS mode" +msgstr "El microprogramari no es pot actualitzar en el mode BIOS heretat" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "El microprogramari ja està bloquejat" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "El microprogramari ja no està bloquejat" + #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format msgid "Firmware metadata has not been updated for %u day and may not be up to date." @@ -649,19 +815,36 @@ msgid_plural "Firmware metadata has not been updated for %u days and may not be msgstr[0] "Les metadades del microprogramari no s'han actualitzat durant %u dia i podria ser que no estiguin actualitzades." msgstr[1] "Les metadades del microprogramari no s'han actualitzat durant %u dies i podria ser que no estiguin actualitzades." +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Actualitzacions del microprogramari" + msgid "Firmware updates are not supported on this machine." msgstr "Les actualitzacions de microprogramari no estan admeses en aquesta màquina." msgid "Firmware updates are supported on this machine." msgstr "Les actualitzacions de microprogramari estan admeses en aquesta màquina." +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Actualitzacions de microprogramari inhabilitades: executeu «fwupdmgr unlock» per a habilitar-les" + +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Etiquetes" +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Força l'acció relaxant algunes comprovacions del temps d'execució" + msgid "Force the action ignoring all warnings" msgstr "Força l'acció ignorant tots els avisos" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "S'ha trobat" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -692,9 +875,17 @@ msgstr "Obté la informació sobre un fitxer de microprogramari" msgid "Gets the configured remotes" msgstr "Obtén els remots configurats" +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Obté els atributs de seguretat de l'amfitrió" + #. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Obtén la llista del microprogramari aprovat." +msgid "Gets the list of approved firmware" +msgstr "Obtén la llista del microprogramari aprovat" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Obtén la llista del microprogramari bloquejat" #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -716,6 +907,15 @@ msgstr "El maquinari està esperant a ser endollat" msgid "High" msgstr "Alta" +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ID de seguretat de l'amfitrió:" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Està ociós..." @@ -724,10 +924,26 @@ msgstr "Està ociós..." msgid "Ignore SSL strict checks when downloading files" msgstr "Ignora les comprovacions estrictes de SSL quan es baixin els fitxers" +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignora les falles de la suma de comprovació del microprogramari" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignora les falles de discrepància del maquinari del microprogramari" + +#. TRANSLATORS: command line option +msgid "Ignore requirement of external power source" +msgstr "Ignora el requisit de la font d'alimentació externa" + #. TRANSLATORS: Ignore validation safety checks when flashing this device msgid "Ignore validation safety checks" msgstr "Ignora les comprovacions de seguretat de validació" +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "S'ignoren les comprovacions estrictes de SSL, per a fer-ho automàticament en el futur, exporteu DISABLE_SSL_STRICT en el vostre entorn" + #. TRANSLATORS: length of time the update takes to apply msgid "Install Duration" msgstr "Durada de la instal·lació" @@ -770,12 +986,59 @@ msgstr "S'està instal·lant l'actualització de microprogramari..." #. TRANSLATORS: %1 is a device name #, c-format msgid "Installing on %s…" -msgstr "S'està intal·lant a %s…" +msgstr "S'està instal·lant a %s…" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "BootGuard d'Intel" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Protegit amb l'ACM per al BootGuard d'Intel" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Fusible OTP del BootGuard d'Intel" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Política d'error del BootGuard d'Intel" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Arrencada verificada per al BootGuard d'Intel" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "CET d'Intel actiu" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "CET d'Intel habilitat" + +#. TRANSLATORS: Title: Direct Connect Interface (DCI) allows +#. * debugging of Intel processors using the USB3 port +msgid "Intel DCI debugger" +msgstr "Depurador DCI d'Intel" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "SMAP d'Intel" #. TRANSLATORS: Device cannot be removed easily msgid "Internal device" msgstr "Dispositiu intern" +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "No vàlid" + #. TRANSLATORS: Is currently in bootloader mode msgid "Is in bootloader mode" msgstr "Està en el mode carregador d'arrencada" @@ -807,6 +1070,22 @@ msgstr "Servei de microprogramari del proveïdor Linux (microprogramari estable) msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Servei de microprogramari del proveïdor Linux (microprogramari en proves)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Nucli Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Bloqueig del nucli Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Intercanvi de Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Llista les entrades a la dbx" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "Llista les actualitzacions de microprogramari compatibles" @@ -815,17 +1094,39 @@ msgstr "Llista les actualitzacions de microprogramari compatibles" msgid "List the available firmware types" msgstr "Llista els tipus de microprogramari disponibles" +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Llista els fitxers en l'ESP" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "S'està carregant..." +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Blocada" + #. TRANSLATORS: the release urgency msgid "Low" msgstr "Baixa" +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Mode de fabricació MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Superposa el MEI" + +msgid "MEI version" +msgstr "Versió del MEI" + #. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Connectors especíificats manualment a la llista blanca" +msgid "Manually enable specific plugins" +msgstr "Habilita manualment connectors específics" #. TRANSLATORS: the release urgency msgid "Medium" @@ -853,8 +1154,8 @@ msgid "Mismatched daemon and client, use %s instead" msgstr "El dimoni i el client no coincideixin, useu %s en el seu lloc" #. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Modifica un valor de la configuració del dimoni." +msgid "Modifies a daemon configuration value" +msgstr "Modifica un valor de la configuració del dimoni" #. TRANSLATORS: command description msgid "Modifies a given remote" @@ -868,7 +1169,11 @@ msgstr "Modifica la configuració del dimoni" #. TRANSLATORS: command description msgid "Monitor the daemon for events" -msgstr "Monitora el dimoni pels esdeveniments" +msgstr "Monitora el dimoni per als esdeveniments" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Munta l'ESP" #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware msgid "Needs a reboot after installation" @@ -882,6 +1187,7 @@ msgstr "Requereix aturar després de la instal·lació" msgid "New version" msgstr "Versió nova" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "No s'ha especificat cap acció!" @@ -917,16 +1223,24 @@ msgstr "No hi ha cap remot disponible" #. TRANSLATORS: nothing was updated offline msgid "No updates were applied" -msgstr "No s’han aplicat les actualitzacions" +msgstr "No s'han aplicat les actualitzacions" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "No s'ha trobat" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "No admesa" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "D'acord" #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Mostra només un únic valor de PCR" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Passa per alt els avisos del connector" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Preferència sobre el camí ESP predeterminat" @@ -939,6 +1253,14 @@ msgstr "Anul·la els avisos i força l'acció" msgid "Parse and show details about a firmware file" msgstr "Analitza i mostra els detalls sobre un fitxer de microprogramari" +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "S'està analitzant l'actualització de la dbx..." + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "S'està analitzant la dbx del sistema..." + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Contrasenya" @@ -955,6 +1277,10 @@ msgstr "Percentatge completat" msgid "Please enter a number from 0 to %u: " msgstr "Introduïu un número del 0 al %u: " +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Protecció DMA durant la prearrencada" + #. TRANSLATORS: version number of previous firmware msgid "Previous version" msgstr "Versió anterior" @@ -974,7 +1300,7 @@ msgstr "Continuo amb l'enviament?" #. TRANSLATORS: a non-free software license msgid "Proprietary" -msgstr "Proprietari" +msgstr "Propietari" #. TRANSLATORS: command line option msgid "Query for firmware update support" @@ -1003,15 +1329,15 @@ msgstr "S'està llegint..." #. TRANSLATORS: console message when not using plymouth msgid "Rebooting…" -msgstr "S'està tornant a arencar..." +msgstr "S'està tornant a arrencar..." #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Refresca les metadades des del servidor remot" #. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "Torna a instal·lar el microprogramari actual al dispositiu." +msgid "Reinstall current firmware on the device" +msgstr "Torna a instal·lar el microprogramari actual en el dispositiu" #. TRANSLATORS: command description msgid "Reinstall firmware on a device" @@ -1045,13 +1371,17 @@ msgstr "URI de l'informe" msgid "Reported to remote server" msgstr "Informat al servidor remot" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Requereix una font d'alimentació" +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "No s'ha trobat el sistema de fitxers «efivarfs» requerit" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "No s'ha trobat el maquinari requerit" #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" -msgstr "Requereix un carregador d’arrencada" +msgstr "Requereix un carregador d'arrencada" #. TRANSLATORS: metadata is downloaded from the Internet msgid "Requires internet connection" @@ -1084,13 +1414,29 @@ msgstr "Executa la rutina de neteja de la composició del connector en usar inst msgid "Run the plugin composite prepare routine when using install-blob" msgstr "Executa la rutina de preparació de la composició del connector en usar install-blob" +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Sufix d'execució" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "regió del BIOS per a l'SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Bloca l'SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Escriu l'SPI" + #. TRANSLATORS: command line option msgid "Save device state into a JSON file between executions" msgstr "Desa l'estat del dispositiu en un fitxer JSON entre les execucions" #. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" -msgstr "Planifica la instal·lació per al següent reinici quan sigui posible" +msgstr "Planifica la instal·lació per al següent reinici quan sigui possible" #. TRANSLATORS: scheduling an update to be done on the next boot msgid "Scheduling…" @@ -1100,6 +1446,10 @@ msgstr "Planificació..." msgid "Selected device" msgstr "Dispositiu seleccionat" +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volum seleccionat" + #. TRANSLATORS: serial number of hardware msgid "Serial Number" msgstr "Número de sèrie" @@ -1108,17 +1458,18 @@ msgstr "Número de sèrie" msgid "Set the debugging flag during update" msgstr "Estableix l'indicador de depuració durant l'actualització" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Estableix la llista de microprogramari aprovat" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Estableix la llista del microprogramari aprovat." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Comparteix l'historial de microprogramari amb els desenvolupadors" +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Mostra tots els resultats" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "Mostra les versions del client i el dimoni" @@ -1151,6 +1502,10 @@ msgstr "Mostra l'historial de les actualitzacions de microprogramari" msgid "Show plugin verbose information" msgstr "Mostra la informació detallada del connector" +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Mostra la versió calculada de la dbx" + #. TRANSLATORS: command line option msgid "Show the debug log from the last attempted update" msgstr "Mostra el registre de depuració del darrer intent d'actualització" @@ -1188,6 +1543,10 @@ msgstr "Origen" msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Especifiqueu el proveïdor/ID del producte del dispositiu DFU" +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Especifica el fitxer de base de dades dbx" + msgid "Specify the number of bytes per USB transfer" msgstr "Especifiqueu el nombre de bytes per a la transferència USB" @@ -1245,10 +1604,42 @@ msgstr "S'han verificat correctament les sumes de verificació del dispositiu" msgid "Summary" msgstr "Resum" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Admesa" + #. TRANSLATORS: Is found in current metadata msgid "Supported on remote server" msgstr "Admès en el servidor remot" +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspensió a inactiu" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspensió a la RAM" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Canviar la branca de microprogramari en el dispositiu" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "El sistema requereix una font d'alimentació externa" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Reconstrucció PCR0 del TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM versió 2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Contaminada" + msgid "Target" msgstr "Objectiu" @@ -1256,6 +1647,23 @@ msgstr "Objectiu" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "El LVFS és un servei gratuït que funciona com una entitat legal independent i no té cap vincle amb la $OS_RELEASE:NAME$. És possible que el vostre distribuïdor no hagi verificat cap de les actualitzacions del microprogramari per a la compatibilitat amb el vostre sistema o els dispositius connectats. Tot el microprogramari només és proporcionat pel fabricant original dels equips." +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "El PCR0 del TPM difereix de la reconstrucció." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "El dimoni ha carregat codi de tercers i ja no és admès pels desenvolupadors originals!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "El microprogramari des de %s no és proporcionat per %s, el proveïdor de maquinari." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "No hi ha cap fitxer de microprogramari bloquejat." + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "There is no approved firmware." @@ -1268,6 +1676,18 @@ msgstr "Aquest programa només pot funcionar correctament com a «root»" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "Aquest remot conté microprogramari que no està embargat, però encara l'ha de provar el proveïdor del maquinari. Haureu d'assegurar-vos que teniu una manera de desactualitzar manualment el microprogramari si l'actualització del microprogramari no funciona." +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Aquest sistema té problemes d'execució HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Aquest sistema té un nivell de seguretat HSI baix." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Aquesta eina permet a un administrador aplicar les actualitzacions des de la dbx UEFI." + #. TRANSLATORS: the user needs to stop playing with stuff msgid "This tool can only be used by the root user" msgstr "Aquesta eina només pot ser usada per l'usuari «root»." @@ -1276,10 +1696,42 @@ msgstr "Aquesta eina només pot ser usada per l'usuari «root»." msgid "Type" msgstr "Tipus" +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "No s'ha detectat o configurat la partició ESP de la UEFI" + #. TRANSLATORS: program name msgid "UEFI Firmware Utility" msgstr "Utilitat per al microprogramari UEFI" +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled" +msgstr "Les actualitzacions de la càpsula UEFI no estan disponibles o habilitades" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Utilitat dbx UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Arrancada segura de la UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "No s'ha pogut connectar amb el servei" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Desvincula el controlador actual" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Microprogramari desbloquejat:" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Desencriptada" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -1293,9 +1745,17 @@ msgstr "Dispositiu desconegut" msgid "Unlock the device to allow access" msgstr "Desbloqueja el dispositiu per a permetre l'accés" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Desblocada" + #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" -msgstr "Desbloqueja el dispositiu per accedir al microprogramari" +msgstr "Desbloqueja el dispositiu per a accedir al microprogramari" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Desmunta l'ESP" #. TRANSLATORS: command line option msgid "Unset the debugging flag during update" @@ -1306,6 +1766,10 @@ msgstr "No estableixis l'indicador de depuració durant l'actualització" msgid "Unsupported daemon version %s, client version is %s" msgstr "Versió %s no admesa del dimoni, la versió del client és %s" +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Sense contaminar" + #. TRANSLATORS: Device is updatable in this or any other mode msgid "Updatable" msgstr "Actualitzable" @@ -1329,7 +1793,7 @@ msgstr "Actualitza tots els dispositius que coincideixin amb les metadades local #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" -msgstr "Que falli en actualitzar és un problema conegut, visiteu aquest URL per obtenir més informació:" +msgstr "Que falli en actualitzar és un problema conegut, visiteu aquest URL per a obtenir més informació:" #. TRANSLATORS: ask the user if we can update the metadata msgid "Update now?" @@ -1354,6 +1818,9 @@ msgstr "Actualitza les metadades emmagatzemades amb el contingut actual" msgid "Updates all firmware to latest versions available" msgstr "Actualitza tot el microprogramari a les versions més recents" +msgid "Updating" +msgstr "S'està actualitzant" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" @@ -1372,10 +1839,6 @@ msgstr "S'està actualitzant %s…" msgid "Upgrade available for %s from %s to %s" msgstr "Actualització disponible per a %s des de %s a %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Missatge de l'enviament:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Envia l'informe només aquesta vegada, però sol·licita-ho de nou en les futures actualitzacions" @@ -1398,6 +1861,14 @@ msgstr "L'enviament dels informes de microprogramari ajudarà als proveïdors de msgid "Urgency" msgstr "Urgència" +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Empreu «fwupdmgr --help» per a l'ajuda" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Empreu «fwupdtool --help» per a l'ajuda" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "Usa les etiquetes peculiars en instal·lar el microprogramari" @@ -1410,6 +1881,14 @@ msgstr "S'ha notificat a l'usuari" msgid "Username" msgstr "Nom d'usuari" +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Vàlid" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "S'està validant el contingut ESP..." + #. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') msgid "Variant" msgstr "Variant" @@ -1422,9 +1901,13 @@ msgstr "Venedor" msgid "Verifying…" msgstr "S'està verificant..." -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "AVÍS: S'ignoren les comprovacions estrictes de SSL. Per a fer-ho automàticament en el futur, exporteu DISABLE_SSL_STRICT en el vostre entorn" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versió:" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "AVÍS:" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1446,6 +1929,10 @@ msgstr "Escriu el microprogramari des d'un fitxer a dins del dispositiu" msgid "Write firmware from file into one partition" msgstr "Escriu el microprogramari des d'un fitxer a dins d'una partició" +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Fitxer d'escriptura:" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "S'està escrivint..." @@ -1454,19 +1941,19 @@ msgstr "S'està escrivint..." msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." msgstr "És possible que el vostre distribuïdor no hagi verificat cap de les actualitzacions del microprogramari per a la compatibilitat amb el vostre sistema o els dispositius connectats." +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "El vostre maquinari pot danyar-se amb aquest microprogramari, i la instal·lació d'aquesta versió pot anul·lar qualsevol garantia amb %s." + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "predeterminat" + #. TRANSLATORS: program name msgid "fwupd TPM event log utility" -msgstr "Utilitat per al registre d’esdeveniments TPM del fwupd" +msgstr "Utilitat per al registre d'esdeveniments TPM del fwupd" -#. TRANSLATORS: message letting the user know no device upgrade available due -#. to missing on LVFS -#. * %1 is the device name -#, c-format -msgid "• %s has no available firmware updates" -msgstr "• %s no té disponible cap actualització de microprogramari" - -#. TRANSLATORS: message letting the user know no device upgrade available -#. * %1 is the device name -#, c-format -msgid "• %s has the latest available firmware version" -msgstr "• %s té disponible la darrera versió del microprogramari" +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Connectors del «fwupd»" diff --git a/po/cs.po b/po/cs.po index d10b6ebef..6e5dd07c2 100644 --- a/po/cs.po +++ b/po/cs.po @@ -6,6 +6,7 @@ # Ascii Wolf , 2017,2019-2020 # Ascii Wolf , 2017 # Marek Černocký , 2016,2018 +# Pavel Borecki , 2020 msgid "" msgstr "" "Project-Id-Version: fwupd\n" @@ -26,6 +27,159 @@ msgstr[1] "Zbývají %.0f minuty" msgstr[2] "Zbývá %.0f minut" msgstr[3] "Zbývá %.0f minuty" +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "Aktualizace mikrokódu procesoru %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Aktualizace nastavení %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Aktualizace spotřebitelského ME v %s" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "Aktualizace řadiče %s" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "Aktualizace korporátního ME v %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "Aktualizace zařízení %s" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "Aktualizace vestavěného řadiče v %s" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "Aktualizace ME v %s" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "Celková aktualizace %s" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Aktualizace řadiče Thunderbolt %s" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Aktualizace %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "režim pro výrobce v %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "přebití %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s verze %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Verze %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u den" +msgstr[1] "%u dny" +msgstr[2] "%u dnů" +msgstr[3] "%u dny" + +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "Je k dispozici novější firmware pro %u zařízení." +msgstr[1] "Jsou k dispozici novější firmware pro %u zařízení." +msgstr[2] "Jsou k dispozici novější firmware pro %u zařízení." +msgstr[3] "Jsou k dispozici novější firmware pro %u zařízení." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u hodina" +msgstr[1] "%u hodiny" +msgstr[2] "%u hodin" +msgstr[3] "%u hodiny" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minuta" +msgstr[1] "%u minuty" +msgstr[2] "%u minut" +msgstr[3] "%u minuty" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u sekunda" +msgstr[1] "%u sekundy" +msgstr[2] "%u sekund" +msgstr[3] "%u sekundy" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Aktivovat zařízení" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Aktivovat čekající zařízení" + +msgid "Activate the new firmware on the device" +msgstr "Aktivovat nový firmware na zařízení" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Aktivování aktualizace firmware" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Aktivace aktualizovaného firmware pro" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Přidáno" @@ -51,10 +205,18 @@ msgstr "Povolit přechod na nižší verze firmwaru" msgid "Allow reinstalling existing firmware versions" msgstr "Povolit reinstalaci stávající verze firmwaru" +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Umožnit přepnutí varianty firmware" + #. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Některá z aktualizací vyžaduje pro dokončení restart." +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Pro dokončení aktualizace je zapotřebí vypnutí počítače." + #. TRANSLATORS: command line option msgid "Answer yes to all questions" msgstr "Na všechny dotazy odpovědět ano" @@ -63,6 +225,31 @@ msgstr "Na všechny dotazy odpovědět ano" msgid "Apply firmware updates" msgstr "Použít aktualizace firmwaru" +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Použít aktualizaci i když to není doporučeno" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Použít soubory s aktualizací" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Provádění aktualizace…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Schválený firmware:" +msgstr[1] "Schválené firmwary:" +msgstr[2] "Schválených firmwarů:" +msgstr[3] "Schválené firmwary:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Ptát se i příště?" + #. TRANSLATORS: command description msgid "Attach to firmware mode" msgstr "Napojit do režimu firmwaru" @@ -83,6 +270,18 @@ msgstr "K přechodu na nižší verzi firmwaru na tomto počítači je vyžadov msgid "Authentication is required to modify a configured remote used for firmware updates" msgstr "Ke změně nastaveného vzdáleného zdroje, který se používá pro aktualizace firmwaru, je vyžadováno ověření" +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Ke změně nastavení procesu služby je vyžadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Pro nastavení seznamu schválených firmwarů je vyžadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Pro přepnutí na novou verzi firmware je vyžadováno ověření se" + #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "Pro odemknutí zařízení je požadováno ověření" @@ -99,6 +298,30 @@ msgstr "K aktualizaci firmwaru na tomto počítači je vyžadováno ověření" msgid "Authentication is required to update the stored checksums for the device" msgstr "K aktualizaci uložených kontrolních součtů zařízení je vyžadováno ověření" +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Pokaždé automaticky nahrát?" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Napojit nový ovladač jádra" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Blokované soubory s firmware:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blokuje se firmware:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blokuje instalaci konkrétního firmware" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Sestavit soubor s firmware" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Sestavit firmware za použití izolovaného prostředí" @@ -111,6 +334,14 @@ msgstr "Zrušit" msgid "Cancelled" msgstr "Zrušeno" +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Tuto aktualizaci dbx už není možné použít, protože už je používána." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Aktualizace není možné použít při provozování systému z instalačního média (live)" + #. TRANSLATORS: this is when a device is hotplugged #. TRANSLATORS: this is when the daemon state changes msgid "Changed" @@ -120,14 +351,27 @@ msgstr "Změněno" msgid "Checksum" msgstr "Kontrolní součet" +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Zvolte variantu firmware:" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Vyberte zařízení:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Zvolte typ firmware:" + #. TRANSLATORS: get interactive prompt msgid "Choose a release:" msgstr "Vyberte verzi:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Zvolte svazek:" + #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" msgstr "Smazat vše naplánované pro aktualizaci při odpojení" @@ -140,6 +384,18 @@ msgstr "Smazat výsledky z poslední aktualizace" msgid "Command not found" msgstr "Příkaz nebyl nalezen" +#. TRANSLATORS: prompt to apply the update +msgid "Continue with update?" +msgstr "Pokračovat v aktualizaci?" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Převést soubor s firmware" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Je k dispozici kryptografické ověření otisku" + #. TRANSLATORS: DFU stands for device firmware update msgid "DFU Utility" msgstr "Nástroj pro práci s DFU" @@ -160,6 +416,10 @@ msgstr "Popis" msgid "Detach to bootloader mode" msgstr "Odpojit do režimu zavaděče" +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Identifikátor zařízení" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Přidáno zařízení:" @@ -168,10 +428,22 @@ msgstr "Přidáno zařízení:" msgid "Device changed:" msgstr "Změněno zařízení:" +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Zařízení je uzamčeno" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device removed:" msgstr "Odebráno zařízení:" +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Zařízení je pro aktualizaci třeba aktivovat" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Po dokončení aktualizace se zařízení hned znovu neobjeví" + #. TRANSLATORS: a list of successful updates msgid "Devices that have been updated successfully:" msgstr "Zařízení, která byla úspěšně aktualizována:" @@ -180,6 +452,15 @@ msgstr "Zařízení, která byla úspěšně aktualizována:" msgid "Devices that were not updated correctly:" msgstr "Zařízení, která nebyla úspěšně aktualizována:" +#. TRANSLATORS: message letting the user know no device upgrade available due +#. to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Zařízení, ke kterém nejsou k dispozici aktualizace firmware:" + +#. TRANSLATORS: message letting the user know no device upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "Zařízení s nejnovějšími dostupnými verzemi firmware:" + msgid "Disabled fwupdate debugging" msgstr "Vypnout ladění fwupdate" @@ -203,10 +484,23 @@ msgstr "Nekontrolovat restart po aktualizaci" msgid "Do not check for unreported history" msgstr "Nekontrolovat nenahlášení historie" +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Nepřidávat dlouhou příponu oblasti" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Nepřidávat příponu s datem a časem" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Neprovádět kontroly bezpečnosti zařízení" + #. TRANSLATORS: command line option msgid "Do not write to the history database" msgstr "Nezapisovat do databáze historie" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Hotovo!" @@ -222,6 +516,11 @@ msgstr "Přejít na nižší verzi firmwaru na zařízení" msgid "Downgrading %s from %s to %s... " msgstr "Převádí se %s z verze %s na %s…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Vracení %s na starší verzi…" + #. TRANSLATORS: downloading from a remote server msgid "Downloading…" msgstr "Stahuje se…" @@ -242,6 +541,8 @@ msgstr "Povolit podporu aktualizace firmwaru na podporovaných systémech" msgid "Enable this remote?" msgstr "Povolit tento vzdálený zdroj?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Povoleno" @@ -260,6 +561,14 @@ msgstr "Zapnutí této funkcionality je na vaše vlastní riziko, což znamená, msgid "Enabling this remote is done at your own risk." msgstr "Povolení tohoto zdroje je na vaše vlastní riziko." +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Šifrované" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Šifrovaná RAM" + #. TRANSLATORS: command description msgid "Erase all firmware update history" msgstr "Smazat veškerou historii aktualizací" @@ -276,33 +585,75 @@ msgstr "Skončit po krátké prodlevě" msgid "Exit after the engine has loaded" msgstr "Skončit po načtení výkonné části" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Nezdařilo se stáhnout kvůli omezením serveru" +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Rozbalit firmware z blobu do obrazů" + +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Nezdařilo se" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Aktualizaci se nepodařilo provést" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Nepodařilo se spojit s procesem služby" + +#. TRANSLATORS: could not parse file +msgid "Failed to extract local dbx " +msgstr "Nepodařilo se rozbalit místní dbx" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Nepodařilo se získat čekající zařízení" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Aktualizaci firmware se nepodařilo nainstalovat" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Nepodařilo se načíst místní dbx" #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Selhalo načtení zvláštních požadavků" +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Nepodařilo se načíst systémové dbx" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Selhalo zpracování argumentů" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Stahuje se soubor" +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Soubor se nepodařilo zpracovat" -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Stahuje se firmware" +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Nepodařilo se zpracovat příznaky pro --filter" -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Stahují se metadata" +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Nepodařilo se zpracovat místní dbx" -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Stahuje se podpis" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Nepodařilo se restartovat" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Nepodařilo se nastavit režim startovací obrazovky" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Nepodařilo se ověřit obsah ESP oddílu" #. TRANSLATORS: filename of the local file msgid "Filename" @@ -312,6 +663,10 @@ msgstr "Název souboru" msgid "Filename Signature" msgstr "Podpis názvu souboru" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Je zapotřebí zadat název souboru" + #. TRANSLATORS: remote URI msgid "Firmware Base URI" msgstr "Základní URI firmwaru" @@ -328,6 +683,18 @@ msgstr "Démon pro aktualizaci firmwaru" msgid "Firmware Utility" msgstr "Nástroj pro práci s firmwarem" +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Atestace firmware" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Firmware už je blokován" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Firmware ještě není blokován" + #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format msgid "Firmware metadata has not been updated for %u day and may not be up to date." @@ -337,12 +704,35 @@ msgstr[1] "Metadata firmwaru nebyla akualizována již %u dny a nelze je aktuali msgstr[2] "Metadata firmwaru nebyla akualizována již %u dní a nelze je aktualizovat." msgstr[3] "Metadata firmwaru nebyla akualizována již %u dne a nelze je aktualizovat." +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Aktualizace firmware" + msgid "Firmware updates are not supported on this machine." msgstr "Aktualizace firmwaru nejsou na tomto počítači podporované." msgid "Firmware updates are supported on this machine." msgstr "Aktualizace firmwaru jsou na tomto počítači podporované." +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Vynutit akci uvolněním některých kontrol při vykonávání" + +msgid "Force the action ignoring all warnings" +msgstr "Vynutit, aby při akci byla ignorována všechna varování" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Nalezeno" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Získat všechny příznaky zařízení podporované fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Zjistit všechna zařízení a vydání, která jsou možná" + #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Zjistit všechna zařízení podporující aktualizaci firmwaru" @@ -359,6 +749,18 @@ msgstr "Zjistit podrobnosti o souboru s firmwarem" msgid "Gets the configured remotes" msgstr "Vypsat nastavené vzdálené zdroje" +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Zjistit atributy zabezpečení hostitele" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Získá seznam schválených firmware" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Získá seznam blokovaných firmware" + #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Vypsat seznam aktualizací pro připojený hardware" @@ -371,10 +773,43 @@ msgstr "Vypsat vydání pro zařízení" msgid "Gets the results from the last update" msgstr "Vypsat výsledky z poslední aktualizace" +#. TRANSLATORS: The hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Hardware čeká na opětovné připojení" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Identifikátor zabezpečení hostitele:" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Nečinný…" +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Při stahování souborů ignorovat některé nedostatky SSL certifikátů" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignorovat nesprávný kontrolní součet firmware" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignorovat neshody firmware s hardware" + +#. TRANSLATORS: command line option +msgid "Ignore requirement of external power source" +msgstr "Ignorovat požadavek na přítomnost externího napájení" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ignorovat výsledky kontrol ověření" + #. TRANSLATORS: command description msgid "Install a firmware blob on a device" msgstr "Nainstalovat na zařízení binární soubor s firmwarem" @@ -392,12 +827,20 @@ msgstr "Instalace podepsaného firmwaru zařízení" msgid "Install signed system firmware" msgstr "Instalace podepsaného systémového firmwaru" +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Nejprve nainstalovat do nadřazeného zařízení" + msgid "Install unsigned device firmware" msgstr "Instalace nepodepsaného firmwaru zařízení" msgid "Install unsigned system firmware" msgstr "Instalace nepodepsaného systémového firmwaru" +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Instalace firmware…" + #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Instaluje se aktualizace firmwaru…" @@ -407,6 +850,51 @@ msgstr "Instaluje se aktualizace firmwaru…" msgid "Installing on %s…" msgstr "Instaluje se na zařízení %s…" +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Co Intel BootGuard udělá v případě chyb" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Ověřené zavádění s Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET aktivní" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET podporováno" + +#. TRANSLATORS: Title: Direct Connect Interface (DCI) allows +#. * debugging of Intel processors using the USB3 port +msgid "Intel DCI debugger" +msgstr "Ladící nástroj Intel DCI" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Vestavěné zařízení" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Neplatné" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Je v režimu zavaděče" + msgid "Keyring" msgstr "Klíčenka" @@ -420,17 +908,48 @@ msgstr "Linux Vendor Firmware Service (stabilní firmware)" msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Linux Vendor Firmware Service (testovací firmware)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linuxové jádro" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Uzamčení linuxového jádra" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linuxový odkládací prostor" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Vypsat položky v dbx" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "Vypsat podporované aktualizace firmwarů" +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Vypsat typy firmware, které jsou k dispozici" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Vypíše soubory na ESP oddílu" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Načítá se…" +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Zamčeno" + +msgid "MEI version" +msgstr "Verze MEI" + #. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Ručně povolit konkrétní zásuvné moduly" +msgid "Manually enable specific plugins" +msgstr "Ručně zapnout konkrétní zásuvné moduly" #. TRANSLATORS: remote URI msgid "Metadata URI" @@ -447,13 +966,39 @@ msgstr "Změnit zadaný vzdálený zdroj" msgid "Modify a configured remote" msgstr "Upravit nastavený vzdálený zdroj" +msgid "Modify daemon configuration" +msgstr "Upravit nastavení procesu služby" + #. TRANSLATORS: command description msgid "Monitor the daemon for events" msgstr "Sledovat události démona" +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Připojí ESP oddíl" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Po instalaci vyžaduje restart" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Po instalaci vyžaduje vypnutí" + +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Není určena žádné akce!" +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Pro %s nejsou k dispozici žádné starší verze" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Nenalezeny žádné identifikátory firmware" + #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Nebylo nalezeno žádné zařízení schopné aktualizace firmwaru" @@ -462,18 +1007,58 @@ msgstr "Nebylo nalezeno žádné zařízení schopné aktualizace firmwaru" msgid "No plugins found" msgstr "Nebyl nalezen žádný zásuvný modul" +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Nejsou k dispozici žádná vydání" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "Zrovna nejsou povolené žádné vzdálené zdroje, takže nejsou k dispozici žádná metadata." +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Nejsou k dispozici žádné protějšky" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nebyly nainstalovány žádné aktualizace" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Nenalezeno" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Nepodporováno" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "V pořádku" + #. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Potlačit varování zásuvného modulu" +msgid "Only show single PCR value" +msgstr "Zobrazit pouze jedinou PCR hodnotu" #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Přepsat výchozí cestu ESP" +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Přebít varování a vynutit akci" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Zpracovat a zobrazit podrobnosti o souboru s firmware" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Zpracovávání aktualizace dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Zpracovávání systémového dbx…" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Heslo" @@ -481,11 +1066,25 @@ msgstr "Heslo" msgid "Payload" msgstr "Obsah" +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Dokončeno procent" + #. TRANSLATORS: the user isn't reading the question #, c-format msgid "Please enter a number from 0 to %u: " msgstr "Zadejte prosím číslo od 0 do %u:" +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Ochrana přímého přístupu do paměti před startem operačního systému" + +msgid "Print the version number" +msgstr "Vypsat číslo verze" + +msgid "Print verbose debug statements" +msgstr "Vypsat podrobná ladící hlášení" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Priorita" @@ -497,6 +1096,10 @@ msgstr "Pokračovat v nahrávání?" msgid "Query for firmware update support" msgstr "Dotázat se na podporu aktualizace firmwaru" +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Načíst blob firmware ze zařízení" + #. TRANSLATORS: command description msgid "Read firmware from device into a file" msgstr "Přečíst firmware ze zařízení do souboru" @@ -505,14 +1108,27 @@ msgstr "Přečíst firmware ze zařízení do souboru" msgid "Read firmware from one partition into a file" msgstr "Přečíst firmware z jednoho oddílu do souboru" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Načítání z %s…" + #. TRANSLATORS: reading from the flash chips msgid "Reading…" msgstr "Čte se…" +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Restartování…" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Aktualizovat metadata ze vzdáleného serveru" +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Přeinstaluje stávající firmware na zařízení" + #. TRANSLATORS: command description msgid "Reinstall firmware on a device" msgstr "Přeinstalovat firmware na zařízení" @@ -541,6 +1157,14 @@ msgstr "Nahradit data ve stávajícím souboru s firmwarem" msgid "Report URI" msgstr "URI hlášení" +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Nahlášeno na vzdálený server" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Vyžaduje zavaděč systému" + #. TRANSLATORS: metadata is downloaded from the Internet msgid "Requires internet connection" msgstr "Vyžaduje připojení k Internetu" @@ -549,6 +1173,10 @@ msgstr "Vyžaduje připojení k Internetu" msgid "Restart now?" msgstr "Restartovat nyní?" +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Restartovat proces služby, aby se změny uplatnily?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "Zařízení se restartuje…" @@ -557,6 +1185,21 @@ msgstr "Zařízení se restartuje…" msgid "Return all the hardware IDs for the machine" msgstr "Vrátit všechna ID hardwaru počítače" +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Podrobnosti získáte spuštěním `fwupdmgr get-upgrades`." + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Oblast SPI BIOS" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI uzamčení" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI zápis" + #. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" msgstr "Pokud je to možné, naplánovat instalaci na příští restart" @@ -565,18 +1208,42 @@ msgstr "Pokud je to možné, naplánovat instalaci na příští restart" msgid "Scheduling…" msgstr "Plánuje se…" +#. TRANSLATORS: Device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Vybrané zařízení" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Vybraný svazek" + #. TRANSLATORS: command line option msgid "Set the debugging flag during update" msgstr "Během aktualizace nastavit příznak ladění" +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Nastavuje seznam schváleného firmware" + #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Sdílet historii firmwaru s vývojáři" +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Zobrazit všechny výsledky" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "Zobrazit verzi klienta a démona" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Zobrazit podrobnější informace z procesu služby pro konkrétní oblast" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Zapnout vypisování ladících informací pro všechny oblasti" + #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Zobrazit volby ladění" @@ -597,6 +1264,10 @@ msgstr "Zobrazit historii aktualizací firmwaru" msgid "Show plugin verbose information" msgstr "Zobrazit podrobné informace o zásuvném modulu" +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Zobrazit vypočtenou verzi dbx" + #. TRANSLATORS: command line option msgid "Show the debug log from the last attempted update" msgstr "Zobrazit ladicí záznam z posledního pokusu o aktualizaci" @@ -605,10 +1276,67 @@ msgstr "Zobrazit ladicí záznam z posledního pokusu o aktualizaci" msgid "Show the information of firmware update status" msgstr "Zobrazit informace o stavu aktualizace firmwaru" +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Vypnout nyní?" + +msgid "Sign data using the client certificate" +msgstr "Podepsat data klientským certifikátem" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Podepsat data klientským certifikátem" + +msgid "Signature" +msgstr "Podpis" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Zadejte identifikátor(y) výrobce/produktu DFU zařízení" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Zadejte soubor dbx databáze" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Zadejte počet bajtů v jednotlivých přenosech přes USB sběrnici" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Všechna zařízení úspěšně aktivována" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Hodnota v nastavení úspěšně změněna" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Kontrolní součty zařízení úspěšně zaktualizovány" + #. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Souhrn" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Podporováno" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Podporováno na vzdáleném serveru" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Počítač vyžaduje externí napájení" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Rekonstrukce TPM PCR0" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + msgid "Target" msgstr "Cíl" @@ -616,6 +1344,19 @@ msgstr "Cíl" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "LVFS je svobodná služba, které funguje jako nezávislý právní subjekt a nemá žádné vazby na systém $OS_RELEASE:NAME$. Váš distributor nemusí některé z aktualizací firmwaru schválit kvůli kompatibilitě s vaším systémem nebo připojenými zařízeními. Veškerý firmware je poskytován pouze přímo výrobci daných zařízení." +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Proces služby načetl kód třetí strany a není už podporován původními vývojáři!" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Nejsou zde žádné blokované soubory s firmware" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Není k dispozici žádný schválený firmware" + #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" msgstr "Tento program může správně fungovat jen pod uživatelem root" @@ -623,6 +1364,14 @@ msgstr "Tento program může správně fungovat jen pod uživatelem root" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "Tento zdroj obsahuje firmware, který není zakázaný, ale zatím je u výrobce stále ve stádiu testování. Měli byste se ujistit, že znáte způsob, jak se vrátit k předchozí verzi firmwaru, kdyby došlo k selhání." +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Tento nástroj správci umožňuje použít UEFI dbx aktualizace." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Tento nástroj může používat pouze uživatel s oprávněními na úrovni správce systému (root)" + #. TRANSLATORS: remote type, e.g. remote or local msgid "Type" msgstr "Typ" @@ -631,23 +1380,68 @@ msgstr "Typ" msgid "UEFI Firmware Utility" msgstr "Nástroj pro práci s firmwarem UEFI" +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Nástroj pro UEFI dbx" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Nedaří se připojit se ke službě" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Odpojit stávající ovladač" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Odblokovává se firmware:" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Nešifrované" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency msgid "Unknown" msgstr "Neznámý" +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Neznámé zařízení" + msgid "Unlock the device to allow access" msgstr "Odemknutí zařízení pro umožnění přístupu" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Odemčené" + #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" msgstr "Odemknout zařízení pro přístup k firmwaru" +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Odpojí ESP oddíl" + #. TRANSLATORS: command line option msgid "Unset the debugging flag during update" msgstr "Během aktualizace zrušit příznak ladění" +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Nepodporovaná verze procesu služby %s, verze klienta je %s" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Možné aktualizovat" + +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Aktualizovat veškerá zařízení, která se shodují s místními metadaty" + #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "O selhání aktualizace se ví, více informací najdete na této adrese:" @@ -656,13 +1450,24 @@ msgstr "O selhání aktualizace se ví, více informací najdete na této adrese msgid "Update now?" msgstr "Aktualizovat nyní?" +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Aktualizace vyžaduje restart" + msgid "Update the stored device verification information" msgstr "Aktualizace uložené informace o ověření zařízení" +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Aktualizovat uložená metadata stávajícím obsahem" + #. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" msgstr "Aktualizovat všechen firmware na nejnovější dostupné verze" +msgid "Updating" +msgstr "Aktualizuje se" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" @@ -670,9 +1475,10 @@ msgstr "Aktualizovat všechen firmware na nejnovější dostupné verze" msgid "Updating %s from %s to %s... " msgstr "Aktualizuje se %s z verze %s na %s…" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Nahraná zpráva:" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Aktualizace %s…" #. TRANSLATORS: ask the user to upload msgid "Upload report now?" @@ -682,14 +1488,46 @@ msgstr "Nahrát hlášení nyní?" msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "Když nahrajete hlášení o firmwaru, pomůžete tím výrobcům hardwaru rychle rozpoznat nezdařené a úspěšné aktualizace na reálných zařízeních." +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Pro zobrazení nápovědy použijte příkaz fwupdmgr --help" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Nápovědu obdržíte spuštěním fwupdtool --help" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Při instalaci firmware použít příznaky pro kompatibilitu" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Uživatel byl upozorněn" + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Uživatelské jméno" +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Platné" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Ověřování obsahu ESP oddílu…" + #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" msgstr "Ověřuje se…" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Verze" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "VAROVÁNÍ:" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Čeká se…" @@ -710,6 +1548,10 @@ msgstr "Zapsat firmware ze souboru do zařízení" msgid "Write firmware from file into one partition" msgstr "Zapsat firmware ze souboru do jednoho oddílu" +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Zápis do souboru:" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Zapisuje se…" @@ -717,3 +1559,15 @@ msgstr "Zapisuje se…" #. TRANSLATORS: show the user a warning msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." msgstr "Váš distributor nemusí schválit některé aktualizace firmwaru kvůli kompatibilitě s vaším systémem nebo připojenými zařízeními." + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "výchozí" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd nástroj pro záznamy událostí v TPM" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "zásuvné moduly pro fwupd" diff --git a/po/da.po b/po/da.po index 7cc601529..3642c0fc3 100644 --- a/po/da.po +++ b/po/da.po @@ -23,6 +23,18 @@ msgid_plural "%.0f minutes remaining" msgstr[0] "%.0f minut tilbage" msgstr[1] "%.0f minutter tilbage" +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s opdatering af CPU-mikrokode" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Opdatering for konfiguration %s" + #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format @@ -86,6 +98,11 @@ msgstr "Opdatering for %s" msgid "%s and all connected devices may not be usable while updating." msgstr "%s og alle tilsluttede enheder vil måske ikke være anvendelige under opdatering." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s-fabrikstilstand" + #. TRANSLATORS: warn the user before updating, %1 is a device name #, c-format msgid "%s must remain connected for the duration of the update to avoid damage." @@ -96,6 +113,21 @@ msgstr "%s skal være tilsluttet under hele opdateringen, for at undgå skade." msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." msgstr "%s skal være tilsluttet en strømkilde under hele opdateringen, for at undgå skade." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s-tilsidesættelse" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s-version" + #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -137,6 +169,10 @@ msgid_plural "%u seconds" msgstr[0] "%u sekund" msgstr[1] "%u sekunder" +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(forældet)" + #. TRANSLATORS: command description msgid "Activate devices" msgstr "Aktivér enheder" @@ -181,6 +217,10 @@ msgstr "Tillad nedgradering af firmwareversioner" msgid "Allow reinstalling existing firmware versions" msgstr "Tillad geninstallering af eksisterende firmwareversioner" +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Tillad skift af firmwaregren" + #. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "For at fuldføre en opdatering skal systemet genstartes." @@ -197,6 +237,18 @@ msgstr "Svar ja til alle spørgsmål" msgid "Apply firmware updates" msgstr "Anvend firmwareopdateringer" +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Anvend opdatering, selv når det ikke tilrådes" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Anvend opdateringsfiler" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Anvender opdatering …" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "Approved firmware:" @@ -204,6 +256,10 @@ msgid_plural "Approved firmware:" msgstr[0] "Godkendt firmware:" msgstr[1] "Godkendt firmware:" +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Spørg igen næste gang?" + #. TRANSLATORS: command description msgid "Attach to firmware mode" msgstr "Tilkobl til firmwaretilstand" @@ -260,10 +316,38 @@ msgstr "Der kræves autentifikation for at opdatere de gemte checksumme for enhe msgid "Automatic Reporting" msgstr "Automatisk rapportering" +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Upload automatisk hver gang?" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Bind ny kernedriver" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Blokerede firmwarefiler:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blokerer firmware:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blokerer en bestemt firmware fra at blive installeret" + #. TRANSLATORS: firmware version of bootloader msgid "Bootloader Version" msgstr "Opstartsindlæser version" +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Gren" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Byg en firmwarefil" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Byg firmware med en sandkasse" @@ -276,6 +360,14 @@ msgstr "Annuller" msgid "Cancelled" msgstr "Annulleret" +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Kan ikke anvende eftersom dbx-opdatering allerede er blevet anvendt." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Kan ikke anvende opdateringer på livemedier" + #. TRANSLATORS: this is when a device is hotplugged #. TRANSLATORS: this is when the daemon state changes msgid "Changed" @@ -289,6 +381,11 @@ msgstr "Tjekker om den kryptografiske hash passer med firmwaren" msgid "Checksum" msgstr "Checksum" +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Vælg en gren:" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Vælg en enhed:" @@ -301,6 +398,10 @@ msgstr "Vælg en firmwaretype:" msgid "Choose a release:" msgstr "Vælg en udgivelse:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Vælg et diskområde:" + #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" msgstr "Rydder opdateringer som er planlagt til at blive opdateret offline" @@ -317,6 +418,18 @@ msgstr "Kommandoen blev ikke fundet" msgid "Continue with update?" msgstr "Fortsæt opdateringen?" +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Konverter en firmwarefil" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Oprettet" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Kritisk" + #. TRANSLATORS: Device supports some form of checksum verification msgid "Cryptographic hash verification is available" msgstr "Bekræftelse af kryptografisk hash er tilgængelig" @@ -393,10 +506,18 @@ msgstr "Enhed fjernet:" msgid "Device stages updates" msgstr "Enhedstrin-opdateringer" +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Enheden undertøtter at der kan skiftes til en anden gren med firmware" + #. TRANSLATORS: Device update needs to be separately activated msgid "Device update needs activation" msgstr "Enhedsopdatering behøver aktivering" +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Enheden sikkerhedskopierer firmwaren inden installation" + #. TRANSLATORS: Device will not return after update completes msgid "Device will not re-appear after update completes" msgstr "Enheden vises ikke igen når opdateringen er færdig" @@ -409,6 +530,20 @@ msgstr "Enheder som det lykkedes at opdatere:" msgid "Devices that were not updated correctly:" msgstr "Enheder som ikke blev opdateret korrekt:" +#. TRANSLATORS: message letting the user know no device upgrade available due +#. to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Enheder uden nogen tilgængelige firmwareopdateringer: " + +#. TRANSLATORS: message letting the user know no device upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "Enheder med den seneste tilgængelige firmwareversion:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Deaktiveret" + msgid "Disabled fwupdate debugging" msgstr "Deaktivér fejlsøgning af fwupdate" @@ -458,6 +593,11 @@ msgstr[1] "Upload ikke rapporter og spørg aldrig om at uploade rapporter om fre msgid "Do not write to the history database" msgstr "Skriv ikke til historikdatabasen" +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Forstår du de konsekvenser som er forbundet med ændring af firmwaregrenen?" + +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Færdig!" @@ -502,6 +642,8 @@ msgstr "Aktivér understøttelse af firmwareopdateringer på systemer som unders msgid "Enable this remote?" msgstr "Aktivér fjernen?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Aktiveret" @@ -520,6 +662,14 @@ msgstr "Aktivering af funktionen sker på egen risiko. Det betydet at du skal ko msgid "Enabling this remote is done at your own risk." msgstr "Aktivering af fjernen sker på egen risiko." +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Krypteret" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Krypteret RAM" + #. TRANSLATORS: command description msgid "Erase all firmware update history" msgstr "Slet al historik over firmwareopdateringer" @@ -536,13 +686,25 @@ msgstr "Afslut efter en lille forsinkelse" msgid "Exit after the engine has loaded" msgstr "Afslut efter motoren er indlæst" +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Udpak en firmwareblob til aftryk" + +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Mislykkedes" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Kunne ikke anvende opdatering" + #. TRANSLATORS: we could not talk to the fwupd daemon msgid "Failed to connect to daemon" msgstr "Kunne ikke oprette forbindelse til dæmonen" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Kunne ikke downloade pga. begrænsning på server" +#. TRANSLATORS: could not parse file +msgid "Failed to extract local dbx " +msgstr "Kunne ikke udpakke lokal dbx " #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" @@ -552,10 +714,19 @@ msgstr "Kunne ikke hente afventende enheder" msgid "Failed to install firmware update" msgstr "Kunne ikke installere firmwareopdateringen" +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Kunne ikke indlæse lokal dbx" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Kunne ikke indlæse quirks" +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Kunne ikke indlæse systemets dbx" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Kunne ikke fortolke argumenter" @@ -568,6 +739,10 @@ msgstr "Kunne ikke fortolke fil" msgid "Failed to parse flags for --filter" msgstr "Kunne ikke fortolke flag for --filter" +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Kunne ikke fortolke lokal dbx" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Kunne ikke genstarte" @@ -576,21 +751,10 @@ msgstr "Kunne ikke genstarte" msgid "Failed to set splash mode" msgstr "Kunne ikke indstille splash-tilstand" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Henter fil" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Henter firmware" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Henter metadata" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Henter underskrift" +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Kunne ikke validere ESP-indhold" #. TRANSLATORS: filename of the local file msgid "Filename" @@ -600,6 +764,10 @@ msgstr "Filnavn" msgid "Filename Signature" msgstr "Filnavnets underskrift" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Filnavn kræves" + #. TRANSLATORS: command line option msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" msgstr "Filtrer med et sæt enhedsflag med et ~-præfiks for at udelukke, f.eks. 'intern,~behøver-genstart'" @@ -624,6 +792,22 @@ msgstr "Firmwareopdateringsdæmon" msgid "Firmware Utility" msgstr "Firmwareredskab" +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Firmwareattest" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "Firmware can not be updated in legacy BIOS mode" +msgstr "Firmwaren kan ikke opdateres i udgået BIOS-tilstand" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Firmware er allerede blokeret" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Firmware er ikke allerede blokeret" + #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format msgid "Firmware metadata has not been updated for %u day and may not be up to date." @@ -631,18 +815,35 @@ msgid_plural "Firmware metadata has not been updated for %u days and may not be msgstr[0] "Firmwaremetadata er ikke blevet opdateret i %u dag og kan være forældet." msgstr[1] "Firmwaremetadata er ikke blevet opdateret i %u dage og kan være forældet." +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Firmwareopdateringer" + msgid "Firmware updates are not supported on this machine." msgstr "Maskinen understøtter ikke firmwareopdateringer." msgid "Firmware updates are supported on this machine." msgstr "Maskinen understøtter firmwareopdateringer." +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Firewareopdateringer deaktiveret; kør 'fwupdmgr unlock' for at aktivere" + +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Flag" +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Gennemtving handlingen ved at afslappe visse runtimetjek" + msgid "Force the action ignoring all warnings" -msgstr "Tving handlingen og ignorer alle advarsler" +msgstr "Gennemtving handlingen og ignorer alle advarsler" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Fundet" #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" @@ -674,9 +875,17 @@ msgstr "Hent detaljer om en firmwarefil" msgid "Gets the configured remotes" msgstr "Henter de konfigurerede fjerne" +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Henter værtens sikkerhedsattributter" + #. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Henter listen over godkendt firmware." +msgid "Gets the list of approved firmware" +msgstr "Henter listen over godkendt firmware" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Henter listen over blokerede firmware" #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -694,6 +903,19 @@ msgstr "Henter resultaterne fra den sidste opdatering" msgid "Hardware is waiting to be replugged" msgstr "Hardwaren venter på at bliver gentilkoblet" +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Høj" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Værtens sikkerheds-ID:" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Inaktiv …" @@ -702,10 +924,26 @@ msgstr "Inaktiv …" msgid "Ignore SSL strict checks when downloading files" msgstr "Ignorer strikse SSL-tjek ved download af filer" +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignorer mislykkede checksum for firmware" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignorer mislykkedes firmwarehardware som ikke passer sammen" + +#. TRANSLATORS: command line option +msgid "Ignore requirement of external power source" +msgstr "Ignorer krav om ekstern strømkilde" + #. TRANSLATORS: Ignore validation safety checks when flashing this device msgid "Ignore validation safety checks" msgstr "Ignorer sikkerhedstjek af bekræftelse" +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ignorerer strikse SSL-tjeks. Eksportér DISABLE_SSL_STRICT i dit miljø for at gøre det automatisk i fremtiden" + #. TRANSLATORS: length of time the update takes to apply msgid "Install Duration" msgstr "Varighed for installation" @@ -750,10 +988,57 @@ msgstr "Installerer firmwareopdateringer …" msgid "Installing on %s…" msgstr "Installerer på %s …" +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM-beskyttet" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP-fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard regler om fejl" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard bekræftet start" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET aktiv" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET-aktiveret" + +#. TRANSLATORS: Title: Direct Connect Interface (DCI) allows +#. * debugging of Intel processors using the USB3 port +msgid "Intel DCI debugger" +msgstr "Intel DCI-fejlsøgning" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + #. TRANSLATORS: Device cannot be removed easily msgid "Internal device" msgstr "Intern enhed" +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Ugyldig" + #. TRANSLATORS: Is currently in bootloader mode msgid "Is in bootloader mode" msgstr "Er i opstartsindlæsertilstand" @@ -785,6 +1070,22 @@ msgstr "Linux Vendor Firmware Service (stabilt firmware)" msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Linux Vendor Firmware Service (testning firmware)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux-kerne" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Nedlukning af Linux-kerne" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux-swap" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Vis poster i dbx" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "Vis understøttede firmwareopdateringer" @@ -793,13 +1094,43 @@ msgstr "Vis understøttede firmwareopdateringer" msgid "List the available firmware types" msgstr "Vis de tilgængelige firmwaretyper" +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Viser filer på ESP'en" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Indlæser …" +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Låst" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Lav" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI-fabrikstilstand" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI-tilsidesættelse" + +msgid "MEI version" +msgstr "MEI-version" + #. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Manuel hvidlistning af bestemte plugins" +msgid "Manually enable specific plugins" +msgstr "Aktivér bestemte plugins manuelt" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Mellem" #. TRANSLATORS: remote URI msgid "Metadata Signature" @@ -823,8 +1154,8 @@ msgid "Mismatched daemon and client, use %s instead" msgstr "Dæmon og klient passer ikke sammen, brug %s i stedet" #. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Rediger en værdi i dæmonkonfiguration." +msgid "Modifies a daemon configuration value" +msgstr "Rediger en værdi i dæmonkonfiguration" #. TRANSLATORS: command description msgid "Modifies a given remote" @@ -840,6 +1171,10 @@ msgstr "Rediger dæmonkonfiguration" msgid "Monitor the daemon for events" msgstr "Overvåg dæmonen for hændelser" +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Monterer ESP'en" + #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware msgid "Needs a reboot after installation" msgstr "Genstart efter installation er nødvendig" @@ -852,6 +1187,7 @@ msgstr "Nedlukning efter installation er nødvendig" msgid "New version" msgstr "Ny version" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Der er ikke angivet nogen handling!" @@ -889,14 +1225,22 @@ msgstr "Ingen tilgængelige fjerne" msgid "No updates were applied" msgstr "Der blev ikke anvendt nogen opdateringer" +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Ikke fundet" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Ikke-understøttet" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Vis kun én PCR-værdi" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Tilsidesæt advarsel for plugin" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Tilsidesæt standard-ESP-stien" @@ -909,6 +1253,14 @@ msgstr "Tilsidesæt advarsler og gennemtving handlingen" msgid "Parse and show details about a firmware file" msgstr "Fortolk og vis deltaljer om en firmwarefil" +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Fortolker dbx-opdatering …" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Fortolker systemets dbx …" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Adgangskode" @@ -925,6 +1277,14 @@ msgstr "Procent fuldført" msgid "Please enter a number from 0 to %u: " msgstr "Indtast venligst et tal nummer 0 og %u: " +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Plugin-afhængigheder mangler" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Præopstart DMA-beskyttelse" + #. TRANSLATORS: version number of previous firmware msgid "Previous version" msgstr "Forrige version" @@ -980,8 +1340,12 @@ msgid "Refresh metadata from remote server" msgstr "Genopfrisk metadata fra fjernserver" #. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "Geninstaller den nuværende firmware på enheden." +msgid "Reinstall current firmware on the device" +msgstr "Geninstaller den nuværende firmware på enheden" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Geninstaller firmware på en enhed" #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second is a version number @@ -1011,9 +1375,13 @@ msgstr "Rapport-URI" msgid "Reported to remote server" msgstr "Rapporteret til fjernserver" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Kræver strømforsygning" +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Krævede efivarfs-filsystem blev ikke fundet" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Det krævede hardware blev ikke fundet" #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" @@ -1050,6 +1418,22 @@ msgstr "Kør oprydningsrutinen for pluginkomposition når install-blob bruges" msgid "Run the plugin composite prepare routine when using install-blob" msgstr "Kør forberedelsesrutinen for pluginkomposition når install-blob bruges" +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Suffiks for runtime" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS-region" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI-lås" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI-skriv" + #. TRANSLATORS: command line option msgid "Save device state into a JSON file between executions" msgstr "Gem enhedens tilstand i en JSON-fil mellem udførsler" @@ -1066,6 +1450,10 @@ msgstr "Planlægger …" msgid "Selected device" msgstr "Valgte enhed" +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Valgte diskområde" + #. TRANSLATORS: serial number of hardware msgid "Serial Number" msgstr "Serienummer" @@ -1074,17 +1462,18 @@ msgstr "Serienummer" msgid "Set the debugging flag during update" msgstr "Indstil fejlsøgningsflaget under opdatering" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Indstiller listen over godkendt firmware" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Indstiller listen over godkendt firmware." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Del historik over firmware med udviklerne" +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Vis alle resultater" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "Vis klient- og dæmonversioner" @@ -1117,6 +1506,10 @@ msgstr "Vis historik over firmwareopdateringer" msgid "Show plugin verbose information" msgstr "Vis uddybende information om plugin" +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Vis den udregnede version af dbx'en" + #. TRANSLATORS: command line option msgid "Show the debug log from the last attempted update" msgstr "Vis fejlsøgningsloggen fra den opdatering der blev forsøgt sidst" @@ -1154,6 +1547,10 @@ msgstr "Kilde" msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Angiv producent-/produkt-id('er) for DFU-enhed" +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Angiv dbx-databasefilen" + msgid "Specify the number of bytes per USB transfer" msgstr "Angiv antal bytes pr. USB-overførsel" @@ -1211,10 +1608,42 @@ msgstr "Bekræftelse af enhedens tjeksumme lykkedes" msgid "Summary" msgstr "Opsummering" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Understøttet" + #. TRANSLATORS: Is found in current metadata msgid "Supported on remote server" msgstr "Understøttes på fjernserver" +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspender-til-inaktiv" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspender-til-ram" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Skift firmwaregrenen på enheden" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Systemet kræver ekstern strømkilde" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0-rekonstruktion" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Tainted" + msgid "Target" msgstr "Mål" @@ -1222,6 +1651,23 @@ msgstr "Mål" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "LVFS'en er en gratis tjeneste der opererer som en selvstændig juridisk enhed og har ingen forbindelse med $OS_RELEASE:NAME$. Din distributør har måske ikke bekræftet nogen af firmwareopdateringerne for kompatibilitet med dit system eller tilsluttede enheder. Al firmware leveres kun af den oprindelige udstyrsproducent." +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0'en er ikke magen til rekonstruktionen." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Dæmonen har indlæst tredjepartskode og understøttes ikke længere af opstrømsudviklerne!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Firmwaren fra %s leveres ikke af hardwareleverandøren %s." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Der er ikke nogen blokerede firmwarefiler" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "There is no approved firmware." @@ -1234,6 +1680,18 @@ msgstr "Programmet virker måske kun korrekt som root" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "Fjernen indeholder firmware som der ikke er embargo på, men som stadigvæk testes af hardwareproducenten. Du skal sikre dig at du har en manuel måde til at nedgradere firmwaren på hvis firmwareopdateringen mislykkedes." +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Systemet har problemer med HSI-runtime." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Systemet har et lavt HSI-sikkerhedsniveau." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Værktøjet giver en administrator mulighed for at anvende UEFI-dbx-opdateringer." + #. TRANSLATORS: the user needs to stop playing with stuff msgid "This tool can only be used by the root user" msgstr "Værktøjet kan kun bruges af root-brugeren" @@ -1242,10 +1700,42 @@ msgstr "Værktøjet kan kun bruges af root-brugeren" msgid "Type" msgstr "Type" +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP-partition ikke registreret eller konfigureret" + #. TRANSLATORS: program name msgid "UEFI Firmware Utility" msgstr "UEFI-firmwareredskab" +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled" +msgstr "UEFI-kapselopdateringer ikke tilgængelige eller aktiveret" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI-dbx-redskab" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI sikkeropstart" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Kan ikke oprette forbindelse til tjeneste" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Fjern binding af nuværende driver" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Fjerner blokering af firmware:" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Dekrypteret" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -1259,10 +1749,18 @@ msgstr "Ukendt enhed" msgid "Unlock the device to allow access" msgstr "Lås enheden op for at tillade adgang" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Låst op" + #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" msgstr "Låser op for enheden for at få adgang til firmwaren" +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Afmonterer ESP'en" + #. TRANSLATORS: command line option msgid "Unset the debugging flag during update" msgstr "Fjern fejlsøgningsflaget under opdatering" @@ -1272,6 +1770,10 @@ msgstr "Fjern fejlsøgningsflaget under opdatering" msgid "Unsupported daemon version %s, client version is %s" msgstr "Uunderstøttet dæmonversion %s, klientversionen er %s" +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Untainted" + #. TRANSLATORS: Device is updatable in this or any other mode msgid "Updatable" msgstr "Kan opdateres" @@ -1320,6 +1822,9 @@ msgstr "Opdater det gemte metadata med det nuværende indhold" msgid "Updates all firmware to latest versions available" msgstr "Opdaterer alle firmware til de seneste versioner" +msgid "Updating" +msgstr "Opdaterer" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" @@ -1338,10 +1843,6 @@ msgstr "Opdaterer %s …" msgid "Upgrade available for %s from %s to %s" msgstr "Opgradering tilgængelig for %s fra %s til %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Uploadmeddelelse:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Upload rapport denne ene gang men spørg igen om fremtidige opdateringer" @@ -1360,6 +1861,18 @@ msgstr[1] "Upload rapporter denne gang og upload automatisk rapporter efter fuld msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "Upload af firmwarerapporter hjælper hardwareproducenter til hurtigt at identificere opdateringer som fejlede og lykkedes på rigtigt hardware." +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Vigtighed" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Brug fwupdmgr --help for hjælp" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Brug fwupdtool --help for hjælp" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "Brug quirk-flag ved installation af firmware" @@ -1372,6 +1885,14 @@ msgstr "Brugeren er blevet underrettet" msgid "Username" msgstr "Brugernavn" +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Gyldig" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validerer ESP-indhold …" + #. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') msgid "Variant" msgstr "Variant" @@ -1384,9 +1905,13 @@ msgstr "Producent" msgid "Verifying…" msgstr "Verificerer …" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "ADVARSEL: Ignorerer strikse SSL-tjeks. Eksportér DISABLE_SSL_STRICT i dit miljø for at gøre det automatisk i fremtiden" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "ADVARSEL:" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1408,6 +1933,10 @@ msgstr "Skriv firmware fra fil ind i enhed" msgid "Write firmware from file into one partition" msgstr "Skriv firmware fra fil ind i en partition" +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Skriver fil:" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Skriver …" @@ -1416,19 +1945,19 @@ msgstr "Skriver …" msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." msgstr "Din distributør har måske ikke verificeret kompatibiliteten af firmwareopdateringerne med dit system eller tilsluttede enheder." +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Der er mulighed for at din hardware kan tage skade hvis firmwaren bruges og installation af udgivelsen kan ugyldiggøre garantien med %s." + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "standard" + #. TRANSLATORS: program name msgid "fwupd TPM event log utility" msgstr "fwupd TPM-hændelseslogredskab" -#. TRANSLATORS: message letting the user know no device upgrade available due -#. to missing on LVFS -#. * %1 is the device name -#, c-format -msgid "• %s has no available firmware updates" -msgstr "• %s har ingen tilgængelige firmwareopdateringer" - -#. TRANSLATORS: message letting the user know no device upgrade available -#. * %1 is the device name -#, c-format -msgid "• %s has the latest available firmware version" -msgstr "• %s har den seneste tilgængelige firmwareversion" +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd-plugins" diff --git a/po/de.po b/po/de.po index 54099fbc3..b056bae89 100644 --- a/po/de.po +++ b/po/de.po @@ -177,6 +177,7 @@ msgstr "Nicht auf nicht erfassten Verlauf prüfen" msgid "Do not write to the history database" msgstr "Nicht in die Verlaufsdatenbank schreiben" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Fertig." @@ -213,6 +214,8 @@ msgstr "Das angegebene ESP war nicht gültig" msgid "Enable firmware update support on supported systems" msgstr "Firmware-Aktualisierungsunterstützung auf unterstützten Systemen aktivieren" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Aktiviert" @@ -243,22 +246,6 @@ msgstr "Nach dem Laden der Engine beenden" msgid "Failed to parse arguments" msgstr "Verarbeitung der Argumente schlug fehl" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Datei wird abgerufen" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Firmware wird abgerufen" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Metadaten werden abgerufen" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Signatur wird abgerufen" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "Dateiname" @@ -299,6 +286,10 @@ msgstr "Firmware-Aktualisierungen werden auf diesem System unterstützt." msgid "Force the action ignoring all warnings" msgstr "Aktion erzwingen, bei der alle Warnungen ignoriert werden" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Gefunden" + #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Alle Geräte ermitteln, die Firmware-Aktualisierungen unterstützen" @@ -392,6 +383,7 @@ msgstr "Metadaten können über den Linux Vendor Firmware Service bezogen werden msgid "Monitor the daemon for events" msgstr "Hintergrundprogramm auf Ereignisse überwachen" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Keine Aktion angegeben!" @@ -403,9 +395,9 @@ msgstr "Es wurde keine Hardware erkannt, deren Firmware aktualisiert werden kann msgid "No plugins found" msgstr "Keine Plugins gefunden" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Plugin-Warnung überschreiben" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Ok" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -601,10 +593,6 @@ msgstr "Aktualisieren von %s von %s nach %s …" msgid "Updating %s…" msgstr "%s wird aktualisiert …" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Nachricht beim Hochladen:" - #. TRANSLATORS: ask the user to upload msgid "Upload report now?" msgstr "Bericht jetzt hochladen?" @@ -621,6 +609,10 @@ msgstr "Benutzername" msgid "Verifying…" msgstr "Überprüfung …" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Warten …" diff --git a/po/en_GB.po b/po/en_GB.po index c59a85661..bc1463ea2 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -3,8 +3,8 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Andi Chandler , 2019 -# Richard Hughes , 2015,2017-2019 +# Andi Chandler , 2019-2020 +# Richard Hughes , 2015,2017-2020 msgid "" msgstr "" "Project-Id-Version: fwupd\n" @@ -23,6 +23,18 @@ msgid_plural "%.0f minutes remaining" msgstr[0] "%.0f minute remaining" msgstr[1] "%.0f minutes remaining" +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s CPU Microcode Update" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s Configuration Update" + #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format @@ -86,6 +98,11 @@ msgstr "%s Update" msgid "%s and all connected devices may not be usable while updating." msgstr "%s and all connected devices may not be usable while updating." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s manufacturing mode" + #. TRANSLATORS: warn the user before updating, %1 is a device name #, c-format msgid "%s must remain connected for the duration of the update to avoid damage." @@ -96,6 +113,21 @@ msgstr "%s must remain connected for the duration of the update to avoid damage. msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." msgstr "%s must remain plugged into a power source for the duration of the update to avoid damage." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s override" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s version" + #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -103,6 +135,12 @@ msgid_plural "%u days" msgstr[0] "%u day" msgstr[1] "%u days" +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u device has a firmware upgrade available." +msgstr[1] "%u devices have a firmware upgrade available." + #. TRANSLATORS: duration in minutes #, c-format msgid "%u hour" @@ -131,6 +169,10 @@ msgid_plural "%u seconds" msgstr[0] "%u second" msgstr[1] "%u seconds" +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(obsoleted)" + #. TRANSLATORS: command description msgid "Activate devices" msgstr "Activate devices" @@ -175,6 +217,10 @@ msgstr "Allow downgrading firmware versions" msgid "Allow reinstalling existing firmware versions" msgstr "Allow reinstalling existing firmware versions" +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Allow switching firmware branch" + #. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "An update requires a reboot to complete." @@ -191,6 +237,18 @@ msgstr "Answer yes to all questions" msgid "Apply firmware updates" msgstr "Apply firmware updates" +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Apply update even when not advised" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Apply update files" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Applying update…" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "Approved firmware:" @@ -198,6 +256,10 @@ msgid_plural "Approved firmware:" msgstr[0] "Approved firmware:" msgstr[1] "Approved firmware:" +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Ask again next time?" + #. TRANSLATORS: command description msgid "Attach to firmware mode" msgstr "Attach to firmware mode" @@ -254,10 +316,38 @@ msgstr "Authentication is required to update the stored checksums for the device msgid "Automatic Reporting" msgstr "Automatic Reporting" +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Automatically upload every time?" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Bind new kernel driver" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Blocked firmware files:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blocking firmware:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blocks a specific firmware from being installed" + #. TRANSLATORS: firmware version of bootloader msgid "Bootloader Version" msgstr "Bootloader Version" +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Branch" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Build a firmware file" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Build firmware using a sandbox" @@ -270,6 +360,14 @@ msgstr "Cancel" msgid "Cancelled" msgstr "Cancelled" +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Cannot apply as dbx update has already been applied." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Cannot apply updates on live media" + #. TRANSLATORS: this is when a device is hotplugged #. TRANSLATORS: this is when the daemon state changes msgid "Changed" @@ -283,6 +381,11 @@ msgstr "Checks cryptographic hash matches firmware" msgid "Checksum" msgstr "Checksum" +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Choose a branch:" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Choose a device:" @@ -295,6 +398,10 @@ msgstr "Choose a firmware type:" msgid "Choose a release:" msgstr "Choose a release:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Choose a volume:" + #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" msgstr "Clears any updates scheduled to be updated offline" @@ -311,6 +418,18 @@ msgstr "Command not found" msgid "Continue with update?" msgstr "Continue with update?" +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Convert a firmware file" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Created" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Critical" + #. TRANSLATORS: Device supports some form of checksum verification msgid "Cryptographic hash verification is available" msgstr "Cryptographic hash verification is available" @@ -363,10 +482,18 @@ msgstr "Device can recover flash failures" msgid "Device changed:" msgstr "Device changed:" +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Device firmware is required to have a version check" + #. TRANSLATORS: Is locked and can be unlocked msgid "Device is locked" msgstr "Device is locked" +#. TRANSLATORS: a version check is required for all firmware +msgid "Device is required to install all provided releases" +msgstr "Device is required to install all provided releases" + #. TRANSLATORS: Device remains usable during update msgid "Device is usable for the duration of the update" msgstr "Device is usable for the duration of the update" @@ -379,10 +506,18 @@ msgstr "Device removed:" msgid "Device stages updates" msgstr "Device stages updates" +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Device supports switching to a different branch of firmware" + #. TRANSLATORS: Device update needs to be separately activated msgid "Device update needs activation" msgstr "Device update needs activation" +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Device will backup firmware before installing" + #. TRANSLATORS: Device will not return after update completes msgid "Device will not re-appear after update completes" msgstr "Device will not re-appear after update completes" @@ -395,6 +530,20 @@ msgstr "Devices that have been updated successfully:" msgid "Devices that were not updated correctly:" msgstr "Devices that were not updated correctly:" +#. TRANSLATORS: message letting the user know no device upgrade available due +#. to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Devices with no available firmware updates: " + +#. TRANSLATORS: message letting the user know no device upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "Devices with the latest available firmware version:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Disabled" + msgid "Disabled fwupdate debugging" msgstr "Disabled fwupdate debugging" @@ -444,6 +593,11 @@ msgstr[1] "Do not upload reports, and never ask to upload reports for future upd msgid "Do not write to the history database" msgstr "Do not write to the history database" +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Do you understand the consequences of changing the firmware branch?" + +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Done!" @@ -488,6 +642,8 @@ msgstr "Enable firmware update support on supported systems" msgid "Enable this remote?" msgstr "Enable this remote?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Enabled" @@ -506,6 +662,14 @@ msgstr "Enabling this functionality is done at your own risk, which means you ha msgid "Enabling this remote is done at your own risk." msgstr "Enabling this remote is done at your own risk." +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Encrypted" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Encrypted RAM" + #. TRANSLATORS: command description msgid "Erase all firmware update history" msgstr "Erase all firmware update history" @@ -522,13 +686,25 @@ msgstr "Exit after a small delay" msgid "Exit after the engine has loaded" msgstr "Exit after the engine has loaded" +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Extract a firmware blob to images" + +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Failed" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Failed to apply update" + #. TRANSLATORS: we could not talk to the fwupd daemon msgid "Failed to connect to daemon" msgstr "Failed to connect to daemon" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Failed to download due to server limit" +#. TRANSLATORS: could not parse file +msgid "Failed to extract local dbx " +msgstr "Failed to extract local dbx " #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" @@ -538,10 +714,19 @@ msgstr "Failed to get pending devices" msgid "Failed to install firmware update" msgstr "Failed to install firmware update" +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Failed to load local dbx" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Failed to load quirks" +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Failed to load system dbx" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Failed to parse arguments" @@ -554,6 +739,10 @@ msgstr "Failed to parse file" msgid "Failed to parse flags for --filter" msgstr "Failed to parse flags for --filter" +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Failed to parse local dbx" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Failed to reboot" @@ -562,21 +751,10 @@ msgstr "Failed to reboot" msgid "Failed to set splash mode" msgstr "Failed to set splash mode" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Fetching file" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Fetching firmware" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Fetching metadata" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Fetching signature" +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Failed to validate ESP contents" #. TRANSLATORS: filename of the local file msgid "Filename" @@ -586,6 +764,10 @@ msgstr "Filename" msgid "Filename Signature" msgstr "Filename Signature" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Filename required" + #. TRANSLATORS: command line option msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" msgstr "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" @@ -610,6 +792,22 @@ msgstr "Firmware Update Daemon" msgid "Firmware Utility" msgstr "Firmware Utility" +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Firmware attestation" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "Firmware can not be updated in legacy BIOS mode" +msgstr "Firmware can not be updated in legacy BIOS mode" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Firmware is already blocked" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Firmware is not already blocked" + #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format msgid "Firmware metadata has not been updated for %u day and may not be up to date." @@ -617,19 +815,36 @@ msgid_plural "Firmware metadata has not been updated for %u days and may not be msgstr[0] "Firmware metadata has not been updated for %u day and may not be up to date." msgstr[1] "Firmware metadata has not been updated for %u days and may not be up to date." +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Firmware updates" + msgid "Firmware updates are not supported on this machine." msgstr "Firmware updates are not supported on this machine." msgid "Firmware updates are supported on this machine." msgstr "Firmware updates are supported on this machine." +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Firmware updates disabled; run 'fwupdmgr unlock' to enable" + +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Flags" +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Force the action by relaxing some runtime checks" + msgid "Force the action ignoring all warnings" msgstr "Force the action ignoring all warnings" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Found" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -660,9 +875,17 @@ msgstr "Gets details about a firmware file" msgid "Gets the configured remotes" msgstr "Gets the configured remotes" +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Gets the host security attributes" + #. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Gets the list of approved firmware." +msgid "Gets the list of approved firmware" +msgstr "Gets the list of approved firmware" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Gets the list of blocked firmware" #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -680,6 +903,19 @@ msgstr "Gets the results from the last update" msgid "Hardware is waiting to be replugged" msgstr "Hardware is waiting to be replugged" +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "High" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Host Security ID:" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Idle…" @@ -688,10 +924,26 @@ msgstr "Idle…" msgid "Ignore SSL strict checks when downloading files" msgstr "Ignore SSL strict checks when downloading files" +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignore firmware checksum failures" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignore firmware hardware mismatch failures" + +#. TRANSLATORS: command line option +msgid "Ignore requirement of external power source" +msgstr "Ignore requirement of external power source" + #. TRANSLATORS: Ignore validation safety checks when flashing this device msgid "Ignore validation safety checks" msgstr "Ignore validation safety checks" +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" + #. TRANSLATORS: length of time the update takes to apply msgid "Install Duration" msgstr "Install Duration" @@ -736,10 +988,57 @@ msgstr "Installing firmware update…" msgid "Installing on %s…" msgstr "Installing on %s…" +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM protected" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard error policy" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard verified boot" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET Active" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET Enabled" + +#. TRANSLATORS: Title: Direct Connect Interface (DCI) allows +#. * debugging of Intel processors using the USB3 port +msgid "Intel DCI debugger" +msgstr "Intel DCI debugger" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + #. TRANSLATORS: Device cannot be removed easily msgid "Internal device" msgstr "Internal device" +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Invalid" + #. TRANSLATORS: Is currently in bootloader mode msgid "Is in bootloader mode" msgstr "Is in bootloader mode" @@ -771,6 +1070,22 @@ msgstr "Linux Vendor Firmware Service (stable firmware)" msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Linux Vendor Firmware Service (testing firmware)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux kernel" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Linux kernel lockdown" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux swap" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "List entries in dbx" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "List supported firmware updates" @@ -779,13 +1094,43 @@ msgstr "List supported firmware updates" msgid "List the available firmware types" msgstr "List the available firmware types" +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Lists files on the ESP" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Loading…" +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Locked" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Low" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI manufacturing mode" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI override" + +msgid "MEI version" +msgstr "MEI version" + #. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Manually whitelist specific plugins" +msgid "Manually enable specific plugins" +msgstr "Manually enable specific plugins" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Medium" #. TRANSLATORS: remote URI msgid "Metadata Signature" @@ -809,8 +1154,8 @@ msgid "Mismatched daemon and client, use %s instead" msgstr "Mismatched daemon and client, use %s instead" #. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Modifies a daemon configuration value." +msgid "Modifies a daemon configuration value" +msgstr "Modifies a daemon configuration value" #. TRANSLATORS: command description msgid "Modifies a given remote" @@ -826,6 +1171,10 @@ msgstr "Modify daemon configuration" msgid "Monitor the daemon for events" msgstr "Monitor the daemon for events" +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Mounts the ESP" + #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware msgid "Needs a reboot after installation" msgstr "Needs a reboot after installation" @@ -838,6 +1187,7 @@ msgstr "Needs shutdown after installation" msgid "New version" msgstr "New version" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "No action specified!" @@ -875,14 +1225,22 @@ msgstr "No remotes available" msgid "No updates were applied" msgstr "No updates were applied" +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Not found" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Not supported" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Only show single PCR value" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Override plugin warning" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Override the default ESP path" @@ -895,6 +1253,14 @@ msgstr "Override warnings and force the action" msgid "Parse and show details about a firmware file" msgstr "Parse and show details about a firmware file" +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Parsing dbx update…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Parsing system dbx…" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Password" @@ -911,6 +1277,14 @@ msgstr "Percentage complete" msgid "Please enter a number from 0 to %u: " msgstr "Please enter a number from 0 to %u: " +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Plugin dependencies missing" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Pre-boot DMA protection" + #. TRANSLATORS: version number of previous firmware msgid "Previous version" msgstr "Previous version" @@ -966,8 +1340,12 @@ msgid "Refresh metadata from remote server" msgstr "Refresh metadata from remote server" #. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "Reinstall current firmware on the device." +msgid "Reinstall current firmware on the device" +msgstr "Reinstall current firmware on the device" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Reinstall firmware on a device" #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second is a version number @@ -997,9 +1375,13 @@ msgstr "Report URI" msgid "Reported to remote server" msgstr "Reported to remote server" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Requires AC power" +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Required efivarfs filesystem was not found" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Required hardware was not found" #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" @@ -1025,6 +1407,9 @@ msgstr "Restarting device…" msgid "Return all the hardware IDs for the machine" msgstr "Return all the hardware IDs for the machine" +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Run `fwupdmgr get-upgrades` for more information." + #. TRANSLATORS: command line option msgid "Run the plugin composite cleanup routine when using install-blob" msgstr "Run the plugin composite cleanup routine when using install-blob" @@ -1033,6 +1418,22 @@ msgstr "Run the plugin composite cleanup routine when using install-blob" msgid "Run the plugin composite prepare routine when using install-blob" msgstr "Run the plugin composite prepare routine when using install-blob" +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Runtime Suffix" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS region" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI lock" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI write" + #. TRANSLATORS: command line option msgid "Save device state into a JSON file between executions" msgstr "Save device state into a JSON file between executions" @@ -1049,6 +1450,10 @@ msgstr "Scheduling…" msgid "Selected device" msgstr "Selected device" +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Selected volume" + #. TRANSLATORS: serial number of hardware msgid "Serial Number" msgstr "Serial Number" @@ -1057,17 +1462,18 @@ msgstr "Serial Number" msgid "Set the debugging flag during update" msgstr "Set the debugging flag during update" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Sets the list of approved firmware" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Sets the list of approved firmware." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Share firmware history with the developers" +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Show all results" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "Show client and daemon versions" @@ -1100,6 +1506,10 @@ msgstr "Show history of firmware updates" msgid "Show plugin verbose information" msgstr "Show plugin verbose information" +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Show the calculated version of the dbx" + #. TRANSLATORS: command line option msgid "Show the debug log from the last attempted update" msgstr "Show the debug log from the last attempted update" @@ -1137,6 +1547,10 @@ msgstr "Source" msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Specify Vendor/Product ID(s) of DFU device" +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Specify the dbx database file" + msgid "Specify the number of bytes per USB transfer" msgstr "Specify the number of bytes per USB transfer" @@ -1194,10 +1608,42 @@ msgstr "Successfully verified device checksums" msgid "Summary" msgstr "Summary" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Supported" + #. TRANSLATORS: Is found in current metadata msgid "Supported on remote server" msgstr "Supported on remote server" +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspend-to-idle" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspend-to-ram" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Switch the firmware branch on the device" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "System requires external power source" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 reconstruction" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Tainted" + msgid "Target" msgstr "Target" @@ -1205,6 +1651,23 @@ msgstr "Target" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "The TPM PCR0 differs from reconstruction." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "The firmware from %s is not supplied by %s, the hardware vendor." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "There are no blocked firmware files" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "There is no approved firmware." @@ -1217,6 +1680,18 @@ msgstr "This program may only work correctly as root" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "This system has HSI runtime issues." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "This system has a low HSI security level." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "This tool allows an administrator to apply UEFI dbx updates." + #. TRANSLATORS: the user needs to stop playing with stuff msgid "This tool can only be used by the root user" msgstr "This tool can only be used by the root user" @@ -1225,10 +1700,46 @@ msgstr "This tool can only be used by the root user" msgid "Type" msgstr "Type" +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP partition not detected or configured" + #. TRANSLATORS: program name msgid "UEFI Firmware Utility" msgstr "UEFI Firmware Utility" +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled" +msgstr "UEFI capsule updates not available or enabled" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx Utility" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI secure boot" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Unable to connect to service" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Unbind current driver" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Unblocking firmware:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Unblocks a specific firmware from being installed" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Unencrypted" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -1242,10 +1753,18 @@ msgstr "Unknown Device" msgid "Unlock the device to allow access" msgstr "Unlock the device to allow access" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Unlocked" + #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" msgstr "Unlocks the device for firmware access" +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Unmounts the ESP" + #. TRANSLATORS: command line option msgid "Unset the debugging flag during update" msgstr "Unset the debugging flag during update" @@ -1255,6 +1774,10 @@ msgstr "Unset the debugging flag during update" msgid "Unsupported daemon version %s, client version is %s" msgstr "Unsupported daemon version %s, client version is %s" +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Untainted" + #. TRANSLATORS: Device is updatable in this or any other mode msgid "Updatable" msgstr "Updatable" @@ -1303,6 +1826,9 @@ msgstr "Update the stored metadata with current contents" msgid "Updates all firmware to latest versions available" msgstr "Updates all firmware to latest versions available" +msgid "Updating" +msgstr "Updating" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" @@ -1321,10 +1847,6 @@ msgstr "Updating %s…" msgid "Upgrade available for %s from %s to %s" msgstr "Upgrade available for %s from %s to %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Upload message:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Upload report just this one time, but prompt again for future updates" @@ -1343,6 +1865,18 @@ msgstr[1] "Upload reports this time and automatically upload reports after compl msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Urgency" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Use fwupdmgr --help for help" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Use fwupdtool --help for help" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "Use quirk flags when installing firmware" @@ -1355,6 +1889,14 @@ msgstr "User has been notified" msgid "Username" msgstr "Username" +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Valid" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validating ESP contents…" + #. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') msgid "Variant" msgstr "Variant" @@ -1367,9 +1909,13 @@ msgstr "Vendor" msgid "Verifying…" msgstr "Verifying…" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "WARNING:" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1391,6 +1937,10 @@ msgstr "Write firmware from file into device" msgid "Write firmware from file into one partition" msgstr "Write firmware from file into one partition" +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Writing file:" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Writing…" @@ -1398,3 +1948,20 @@ msgstr "Writing…" #. TRANSLATORS: show the user a warning msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." msgstr "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "default" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM event log utility" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd plugins" diff --git a/po/fi.po b/po/fi.po index b8a961731..98de5cb20 100644 --- a/po/fi.po +++ b/po/fi.po @@ -3,7 +3,7 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Jiri Grönroos , 2017-2018 +# Jiri Grönroos , 2017-2018,2020 # Kimmo Kujansuu , 2019-2020 msgid "" msgstr "" @@ -20,64 +20,70 @@ msgstr "" #, c-format msgid "%.0f minute remaining" msgid_plural "%.0f minutes remaining" -msgstr[0] "%.0f minuuttia jäljellä" +msgstr[0] "%.0f minuutti jäljellä" msgstr[1] "%.0f minuuttia jäljellä" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU #. * at system bootup #, c-format msgid "%s CPU Microcode Update" -msgstr "%s CPU Microcode päivitys" +msgstr "Laitteen %s suorittimen mikrokoodipäivitys" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s kokoonpanon päivitys" #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s Consumer ME Update" -msgstr "%sKuluttajan ME-päivitys" +msgstr "Laitteen %s kuluttajan ME-päivitys" #. TRANSLATORS: the controller is a device that has other devices #. * plugged into it, for example ThunderBolt, FireWire or USB, #. * the first %s is the device name, e.g. 'Intel ThunderBolt` #, c-format msgid "%s Controller Update" -msgstr "%sOhjaimen päivitys" +msgstr "Laitten %s ohjaimen päivitys" #. TRANSLATORS: ME stands for Management Engine (with Intel AMT), #. * where the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s Corporate ME Update" -msgstr "%sYrityksen ME-päivitys" +msgstr "Laitteen %s yrityksen ME-päivitys" #. TRANSLATORS: a specific part of hardware, #. * the first %s is the device name, e.g. 'Unifying Receiver` #, c-format msgid "%s Device Update" -msgstr "%sLaitteen päivitys" +msgstr "Laitteen %s laitepäivitys" #. TRANSLATORS: the EC is typically the keyboard controller chip, #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s Embedded Controller Update" -msgstr "%sSulautetun ohjaimen päivitys" +msgstr "Laitteen %s sulautetun ohjaimen päivitys" #. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s ME Update" -msgstr "%sME päivitys" +msgstr "Laitteen %s ME-päivitys" #. TRANSLATORS: the entire system, e.g. all internal devices, #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s System Update" -msgstr "%sJärjestelmän päivitys" +msgstr "Laitteen %s järjestelmäpäivitys" #. TRANSLATORS: the Thunderbolt controller is a device that #. * has other high speed Thunderbolt devices plugged into it; #. * the first %s is the system name, e.g. 'ThinkPad P50` #, c-format msgid "%s Thunderbolt Controller Update" -msgstr "%s Thunderbolt-ohjaimen päivitys" +msgstr "Laitteen %s Thunderbolt-ohjaimen päivitys" #. TRANSLATORS: this is the fallback where we don't know if the release #. * is updating the system, the device, or a device class, or something else @@ -85,13 +91,18 @@ msgstr "%s Thunderbolt-ohjaimen päivitys" #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s Update" -msgstr "%sPäivitys" +msgstr "Laitteen %s päivitys" #. TRANSLATORS: warn the user before updating, %1 is a device name #, c-format msgid "%s and all connected devices may not be usable while updating." msgstr "%s ja kaikki kytketyt laitteet eivät välttämättä ole käyttökelpoisia päivityksen aikana." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s tuotantotekniikka" + #. TRANSLATORS: warn the user before updating, %1 is a device name #, c-format msgid "%s must remain connected for the duration of the update to avoid damage." @@ -102,12 +113,27 @@ msgstr "%s on oltava kytkettynä päivityksen ajaksi vaurioiden välttämiseksi. msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." msgstr "%s on oltava kytkettynä virtalähteeseen päivityksen ajaksi vaurioiden välttämiseksi." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s ohita" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s versio" + #. TRANSLATORS: duration in days! #, c-format msgid "%u day" msgid_plural "%u days" -msgstr[0] "%upäivää" -msgstr[1] "%upäivää" +msgstr[0] "%u päivä" +msgstr[1] "%u päivää" #, c-format msgid "%u device has a firmware upgrade available." @@ -119,8 +145,8 @@ msgstr[1] "%u laitteessa on saatavilla firmware -päivitys." #, c-format msgid "%u hour" msgid_plural "%u hours" -msgstr[0] "%u tuntia" -msgstr[1] "%utuntia" +msgstr[0] "%u tunti" +msgstr[1] "%u tuntia" #. TRANSLATORS: how many local devices can expect updates now #, c-format @@ -133,15 +159,19 @@ msgstr[1] "%upaikallisia laitteita tuettu" #, c-format msgid "%u minute" msgid_plural "%u minutes" -msgstr[0] "%u minuuttia" +msgstr[0] "%u minuutti" msgstr[1] "%u minuuttia" #. TRANSLATORS: duration in seconds #, c-format msgid "%u second" msgid_plural "%u seconds" -msgstr[0] "%u " -msgstr[1] "%u sekunttia" +msgstr[0] "%u sekunti" +msgstr[1] "%u sekuntia" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(vanhentunut)" #. TRANSLATORS: command description msgid "Activate devices" @@ -187,6 +217,10 @@ msgstr "Salli firmware-versioiden alentaminen" msgid "Allow reinstalling existing firmware versions" msgstr "Salli laiteohjelmiston versioiden asentaminen uudelleen" +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Salli vaihtaa laiteohjelmiston haara" + #. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Päivitys vaatii uudelleenkäynnistyksen." @@ -201,7 +235,19 @@ msgstr "Vastaa kaikkiin kysymyksiin kyllä" #. TRANSLATORS: command line option msgid "Apply firmware updates" -msgstr "Käytä firmware-päivityksiä" +msgstr "Toteuta firmware-päivitykset" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Käytä päivitystä myös silloin, kun sitä ei suositella" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Käytä päivitystiedostoja" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Sovelletaan päivitystä..." #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator @@ -210,6 +256,10 @@ msgid_plural "Approved firmware:" msgstr[0] "Hyväksytty laiteohjelmisto:" msgstr[1] "Hyväksytty laiteohjelmisto:" +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Kysy uudestaan ensi kerralla?" + #. TRANSLATORS: command description msgid "Attach to firmware mode" msgstr "Kiinnitä laiteohjelmistotilaan" @@ -224,7 +274,7 @@ msgstr "Todennus on tarpeen, jotta firmware voidaan alentaa siirrettävällä la #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on this machine" -msgstr "Todennus on tarpeen tämän laitteen firmwaren alentamiseksi" +msgstr "Tunnistautuminen vaaditaan tämän laitteen laiteohjelmiston version alentamiseksi" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify a configured remote used for firmware updates" @@ -232,7 +282,7 @@ msgstr "Todennusta tarvitaan firmware-päivityksiin käytettävän konfiguroidun #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify daemon configuration" -msgstr "Todennusta tarvitaan taustaprosessin asetusten muokkaamiseen" +msgstr "Tunnistautuminen vaaditaan taustaprosessin asetusten muokkaamiseksi" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to set the list of approved firmware" @@ -266,10 +316,38 @@ msgstr "Todennus edellyttää laitteen tallennettujen tarkistussummien päivitt msgid "Automatic Reporting" msgstr "Automaattinen raportointi" +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Ladataan automaattisesti joka kerta?" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Kytke uusi laiteohjain" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Estetyt laiteohjelmiston tiedostot:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Laiteohjelmiston estäminen:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Estää tietyn laiteohjelmiston asentamisen" + #. TRANSLATORS: firmware version of bootloader msgid "Bootloader Version" msgstr "Käynnistyslataimen versio" +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Haara" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Luo laiteohjelmiston tiedosto" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Rakenna laiteohjelmisto hiekkalaatikon avulla" @@ -282,6 +360,14 @@ msgstr "Peru" msgid "Cancelled" msgstr "Peruttu" +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Ei voida käyttää, koska dbx-päivitys on jo asennettu." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Päivityksiä ei voi käyttää asennus-mediassa" + #. TRANSLATORS: this is when a device is hotplugged #. TRANSLATORS: this is when the daemon state changes msgid "Changed" @@ -295,6 +381,11 @@ msgstr "Tarkistaa salaustekniikan, joka vastaa laiteohjelmistoa" msgid "Checksum" msgstr "Tarkistussumma" +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Valitse haara:" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Valitse laite:" @@ -307,6 +398,10 @@ msgstr "Valitse laiteohjelman tyyppi:" msgid "Choose a release:" msgstr "Valitse julkaisu:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Valitse asema:" + #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" msgstr "Tyhjentää päivitykset, jotka on tarkoitus päivittää offline-tilassa" @@ -411,10 +506,18 @@ msgstr "Laite poistettu:" msgid "Device stages updates" msgstr "Laitevyöhykkeen päivitykset" +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Laite tukee vaihtamista toiseen laiteohjelmiston haaraan" + #. TRANSLATORS: Device update needs to be separately activated msgid "Device update needs activation" msgstr "Laitteen päivitys tarvitsee aktivointia" +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Laite varmuuskopioi laiteohjelmiston ennen asennusta" + #. TRANSLATORS: Device will not return after update completes msgid "Device will not re-appear after update completes" msgstr "Laitetta ei näytetä uudelleen päivityksen päätyttyä" @@ -427,6 +530,20 @@ msgstr "Laitteet, jotka on päivitetty onnistuneesti:" msgid "Devices that were not updated correctly:" msgstr "Laitteet, joita ei päivitetty oikein:" +#. TRANSLATORS: message letting the user know no device upgrade available due +#. to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Laitteet, joista ei ole saatavilla firmware-päivityksiä:" + +#. TRANSLATORS: message letting the user know no device upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "Laitteet, joista on saatavilla uusin firmware-versio:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Ei käytössä" + msgid "Disabled fwupdate debugging" msgstr "fw-päivityksen virheenkorjaus poistettu" @@ -476,6 +593,11 @@ msgstr[1] "Älä lähetä raportteja äläkä koskaan pyydä lähettämään rap msgid "Do not write to the history database" msgstr "Älä kirjoita historiatietokantaan" +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Ymmärrätkö laiteohjelmiston haaran vaihtamisen seuraukset?" + +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Valmis!" @@ -494,7 +616,7 @@ msgstr "Alenee %s -sta %s tulos %s... " #. TRANSLATORS: %1 is a device name #, c-format msgid "Downgrading %s…" -msgstr "Alentaa %s…" +msgstr "Alennetaan laitetta %s…" #. TRANSLATORS: downloading from a remote server msgid "Downloading…" @@ -520,6 +642,8 @@ msgstr "Ota firmware-päivitystuki käyttöön tuetuissa järjestelmissä" msgid "Enable this remote?" msgstr "Ota etäyhteys käyttöön?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Käytössä" @@ -538,6 +662,14 @@ msgstr "Tämän toiminnon käyttöönotto tapahtuu omalla vastuullasi, joten sin msgid "Enabling this remote is done at your own risk." msgstr "Tämän etä-ohjaimen käyttöönotto tapahtuu omalla vastuullasi." +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Salattu" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Salattu RAM-muisti" + #. TRANSLATORS: command description msgid "Erase all firmware update history" msgstr "Poista kaikki laiteohjelmiston päivityshistoria" @@ -554,13 +686,25 @@ msgstr "Poistu pienen viiveen jälkeen" msgid "Exit after the engine has loaded" msgstr "Poistu kun moottori on ladattu" +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Laiteohjelmiston blob purkaminen kuvaksi " + +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Epäonnistui" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Päivityksen asentaminen epäonnistui" + #. TRANSLATORS: we could not talk to the fwupd daemon msgid "Failed to connect to daemon" msgstr "Yhteys taustaprosessiin epäonnistui" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Lataaminen epäonnistui palvelimen rajoituksen vuoksi" +#. TRANSLATORS: could not parse file +msgid "Failed to extract local dbx " +msgstr "Paikallisen dbx-tiedoston purkaminen epäonnistui" #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" @@ -568,47 +712,49 @@ msgstr "Vireillä olevien laitteiden saaminen epäonnistui" #. TRANSLATORS: we could not install for some reason msgid "Failed to install firmware update" -msgstr "Laiteohjelmiston firmware päivityksen asennus epäonnistui" +msgstr "Laiteohjelmiston päivityksen asennus epäonnistui" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Paikallisen dbx-tiedoston lataus epäonnistui" #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Quirksin lataaminen epäonnistui" +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Järjestelmän dbx-tiedoston lataus epäonnistui" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Argumenttien jäsentäminen epäonnistui" #. TRANSLATORS: failed to read measurements file msgid "Failed to parse file" -msgstr "Tiedoston käsittely epäonnistui" +msgstr "Tiedoston jäsentäminen epäonnistui" #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse flags for --filter" msgstr "Lippujen erittely ei onnistunut --suodatin" +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Paikallisen dbx-tiedoston lukeminen epäonnistui" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" -msgstr "Käynnistys epäonnistui" +msgstr "Uudelleenkäynnistys epäonnistui" #. TRANSLATORS: we could not talk to plymouth msgid "Failed to set splash mode" msgstr "Splash-tilan asettaminen epäonnistui" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Noudetaan tiedosto" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Noudetaan firmware" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Noudetaan metatietoja" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Noudetaan allekirjoitus" +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "ESP-sisällön vahvistaminen epäonnistui" #. TRANSLATORS: filename of the local file msgid "Filename" @@ -618,6 +764,10 @@ msgstr "Tiedostonimi" msgid "Filename Signature" msgstr "Tiedoston allekirjoitus" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Tiedostonimi vaaditaan" + #. TRANSLATORS: command line option msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" msgstr "Suodata laite lippuja käyttäen ~ etuliiteellä sulkea pois, esim. 'sisäiset, ~ vaatii käynnistyksen'" @@ -642,6 +792,22 @@ msgstr "Firmware-päivityksen taustaprosessi" msgid "Firmware Utility" msgstr "Firmware-työkalu" +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Firmware-aitoustodistus" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "Firmware can not be updated in legacy BIOS mode" +msgstr "Laiteohjelmistoa ei voi päivittää legacy BIOS-tilassa" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Laiteohjelmisto on jo estetty" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Laiteohjelmistoa ei ole jo estetty" + #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format msgid "Firmware metadata has not been updated for %u day and may not be up to date." @@ -649,19 +815,36 @@ msgid_plural "Firmware metadata has not been updated for %u days and may not be msgstr[0] "Laiteohjelmiston metatietoja ei ole päivitetty %upäivään ja ne eivät välttämättä ole ajan tasalla." msgstr[1] "Laiteohjelmiston metatietoja ei ole päivitetty %upäivään ja ne eivät välttämättä ole ajan tasalla." +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Firmware-päivitykset" + msgid "Firmware updates are not supported on this machine." msgstr "Ohjelmistopäivitys 'firmware' ei tue tätä laitetta" msgid "Firmware updates are supported on this machine." msgstr "Ohjelmistopäivitys 'firmware' tukee tätä laitetta" +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Laiteohjelmiston päivitykset poistettu käytöstä; suorita 'fwupdmgr unlock' ottaaksesi käyttöön" + +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Liput" +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Pakota toiminta vapauttamalla joitakin ajonaikaisia tarkistuksia" + msgid "Force the action ignoring all warnings" msgstr "Pakota toimenpide huomioimatta kaikki varoitukset" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Löydetty" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -692,9 +875,17 @@ msgstr "Hae tietoja firmware-tiedostosta" msgid "Gets the configured remotes" msgstr "Määrittää konfiguroidut etäyhteydet" +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Hanki tietoturvan määritteet" + #. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Hae hyväksytty laiteohjelmiston luettelo." +msgid "Gets the list of approved firmware" +msgstr "Hae hyväksytty laiteohjelmiston luettelo" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Hakee estettyjen laiteohjelmistojen luettelon" #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -716,6 +907,15 @@ msgstr "Laitteisto odottaa uudelleen kytkemistä" msgid "High" msgstr "Korkea" +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Koneen tietoturva ID:" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Jouten…" @@ -724,10 +924,26 @@ msgstr "Jouten…" msgid "Ignore SSL strict checks when downloading files" msgstr "Ohita tiukat SSL tarkistukset tiedostoja ladattaessa" +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ohita laiteohjelmiston tarkistussumman virheet" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ohita laitteiden yhteensopimattomuuden virheet" + +#. TRANSLATORS: command line option +msgid "Ignore requirement of external power source" +msgstr "Ohita ulkoisen virtalähteen vaatimus" + #. TRANSLATORS: Ignore validation safety checks when flashing this device msgid "Ignore validation safety checks" msgstr "Ohita turvatarkastukset" +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ohitetaan tiukat SSL-tarkistukset, jos haluat tehdä tämän tulevaisuudessa automaattisesti tee DISABLE_SSL_STRICT ympäristöösi" + #. TRANSLATORS: length of time the update takes to apply msgid "Install Duration" msgstr "Asennuksen kesto" @@ -741,7 +957,7 @@ msgid "Install a firmware file on this hardware" msgstr "Asenna laiteohjelmisto tähän laitteistoon" msgid "Install old version of system firmware" -msgstr "Asenna vanha firmware versio" +msgstr "Asenna vanha firmware-versio" msgid "Install signed device firmware" msgstr "Asenna allekirjoitettu laitteen firmware" @@ -770,15 +986,62 @@ msgstr "Asennetaan firmware-päivitystä…" #. TRANSLATORS: %1 is a device name #, c-format msgid "Installing on %s…" -msgstr "Asentaa %s…" +msgstr "Asentaa laitteelle %s…" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM suojattu" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard virhekäytäntö" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard vahvistettu käynnistys" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET aktiivinen" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET käytössä" + +#. TRANSLATORS: Title: Direct Connect Interface (DCI) allows +#. * debugging of Intel processors using the USB3 port +msgid "Intel DCI debugger" +msgstr "Intel DCI virheenkorjaus" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" #. TRANSLATORS: Device cannot be removed easily msgid "Internal device" msgstr "Sisäinen laite" +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Virheellinen" + #. TRANSLATORS: Is currently in bootloader mode msgid "Is in bootloader mode" -msgstr "On käynnistyslatain moodissa" +msgstr "On käynnistyslataintilassa" #. TRANSLATORS: issue fixed with the release, e.g. CVE msgid "Issue" @@ -807,6 +1070,22 @@ msgstr "Linux-toimittajan laiteohjelmisto (vakaa firmware)" msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Linux-toimittajan laiteohjelmisto (firmware testaus)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux kernel" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Linux kernel lukitus" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux swap" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Listaa merkinnät dbx-muodossa" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "Lista tuetuista firmware-päivityksistä" @@ -815,17 +1094,39 @@ msgstr "Lista tuetuista firmware-päivityksistä" msgid "List the available firmware types" msgstr "Luettelo käytettävissä olevista laiteohjelmistotyypeistä" +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Listaa ESP:n tiedostot" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Ladataan…" +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Lukittu" + #. TRANSLATORS: the release urgency msgid "Low" msgstr "Matala" +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI tuotantotekniikka" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Ohita MEI" + +msgid "MEI version" +msgstr "MEI versio" + #. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Luetteloi tiettyjä lisäosia manuaalisesti" +msgid "Manually enable specific plugins" +msgstr "Ota manuaalisesti tietyt laajennukset käyttöön" #. TRANSLATORS: the release urgency msgid "Medium" @@ -853,8 +1154,8 @@ msgid "Mismatched daemon and client, use %s instead" msgstr "Virheellinen taustaprosessi ja asiakas, käytä sen sijaan %s" #. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Muuta taustaprosessin määritysarvoja." +msgid "Modifies a daemon configuration value" +msgstr "Muuta taustaprosessin määritysarvoja" #. TRANSLATORS: command description msgid "Modifies a given remote" @@ -870,6 +1171,10 @@ msgstr "Muokkaa taustaprosessin kokoonpanoa" msgid "Monitor the daemon for events" msgstr "Seuraa tapahtumien taustaprosessia" +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Kytkee ESP:n käyttöön" + #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware msgid "Needs a reboot after installation" msgstr "Tarvitsee käynnistyksen asennuksen jälkeen" @@ -882,6 +1187,7 @@ msgstr "Tarvitsee sammutuksen asennuksen jälkeen" msgid "New version" msgstr "Uusi versio" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Toimintoa ei ole määritetty!" @@ -897,11 +1203,11 @@ msgstr "Laiteohjelmiston tunnuksia ei löytynyt" #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" -msgstr "Ei havaittu sopivaa laitteistoa firmware päivitykselle" +msgstr "Ei havaittu sopivaa laitteistoa firmware-päivitykselle" #. TRANSLATORS: nothing found msgid "No plugins found" -msgstr "Ei laajennuksia" +msgstr "Liitännäisiä ei löytynyt" #. TRANSLATORS: no repositories to download from msgid "No releases available" @@ -917,16 +1223,24 @@ msgstr "Kaukosäätimet ei saatavilla" #. TRANSLATORS: nothing was updated offline msgid "No updates were applied" -msgstr "Ei soveltuvia päivityksiä" +msgstr "Päivityksiä ei toteutettu" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Ei löydy" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Ei tueta" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Näytä vain yksi PCR-arvo" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Ohita plugin-varoitus" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Ohita oletusarvoinen ESP-polku" @@ -939,6 +1253,14 @@ msgstr "Ohita varoitukset ja pakota toiminto" msgid "Parse and show details about a firmware file" msgstr "Analysoi ja näytä tiedot laiteohjelmiston tiedostosta" +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Luetaan dbx päivitys..." + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Luetaan järjestelmä dbx..." + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Salasana" @@ -955,6 +1277,14 @@ msgstr "Prosenttiosuus valmis" msgid "Please enter a number from 0 to %u: " msgstr "Anna numero 0 -%u" +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Laajennuksen riippuvuudet puuttuvat" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Pre-boot DMA-suojaus" + #. TRANSLATORS: version number of previous firmware msgid "Previous version" msgstr "Aiempi versio" @@ -974,7 +1304,7 @@ msgstr "Jatka lähettämistä?" #. TRANSLATORS: a non-free software license msgid "Proprietary" -msgstr "Patentoitu" +msgstr "Suljettu" #. TRANSLATORS: command line option msgid "Query for firmware update support" @@ -995,7 +1325,7 @@ msgstr "Lue firmware osiolta tiedostoon" #. TRANSLATORS: %1 is a device name #, c-format msgid "Reading from %s…" -msgstr "Lukeminen %s…" +msgstr "Lukee laitteelta %s…" #. TRANSLATORS: reading from the flash chips msgid "Reading…" @@ -1010,8 +1340,8 @@ msgid "Refresh metadata from remote server" msgstr "Päivitä etäpalvelimen metatiedot" #. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "Asenna nykyinen laiteohjelmisto laitteeseen." +msgid "Reinstall current firmware on the device" +msgstr "Asenna nykyinen laiteohjelmisto laitteeseen" #. TRANSLATORS: command description msgid "Reinstall firmware on a device" @@ -1045,9 +1375,13 @@ msgstr "Ilmoita URI" msgid "Reported to remote server" msgstr "Raportoitu etäpalvelimelle" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Vaatii verkkovirtaa" +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Vaadittua efivarfs-tiedostojärjestelmää ei löytynyt" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Vaadittavaa laitteistoa ei löytynyt" #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" @@ -1055,7 +1389,7 @@ msgstr "Vaatii käynnistyslataimen" #. TRANSLATORS: metadata is downloaded from the Internet msgid "Requires internet connection" -msgstr "Vaatii Internet-yhteyden" +msgstr "Vaatii internetyhteyden" #. TRANSLATORS: reboot to apply the update msgid "Restart now?" @@ -1084,6 +1418,22 @@ msgstr "Suorita plugin-puhdistustoiminto, kun käytät asennus-blobia" msgid "Run the plugin composite prepare routine when using install-blob" msgstr "Asennus-blobia ajamalla käytät yhdistelmän valmiita laajennus rutiineja " +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Suoritusaika" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS-alue" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI-lukko" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI-kirjoitus" + #. TRANSLATORS: command line option msgid "Save device state into a JSON file between executions" msgstr "Tallenna laitteen tila JSON-tiedostoon suoritusten välillä" @@ -1100,6 +1450,10 @@ msgstr "Ajoitetaan…" msgid "Selected device" msgstr "Valittu laite" +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Valittu asema" + #. TRANSLATORS: serial number of hardware msgid "Serial Number" msgstr "Sarjanumero" @@ -1108,17 +1462,18 @@ msgstr "Sarjanumero" msgid "Set the debugging flag during update" msgstr "Aseta virheenkorjauksen lippu päivityksen aikana" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Asettaa listan hyväksytyjä 'firmware' laiteohjelmistoja " -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Asettaa hyväksytyn laiteohjelmiston luettelon." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Jaa laiteohjelmiston historia kehittäjien kanssa" +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Näytä kaikki tulokset" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "Näytä asiakas- ja taustaprosessin versiot" @@ -1151,6 +1506,10 @@ msgstr "Näytä laiteohjelmiston päivitysten historia" msgid "Show plugin verbose information" msgstr "Näytä plugin tiedot" +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Näytä dbx laskettu versio" + #. TRANSLATORS: command line option msgid "Show the debug log from the last attempted update" msgstr "Näytä virheenkorjausloki viimeisestä päivitysyrityksestä" @@ -1188,6 +1547,10 @@ msgstr "Lähde" msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Määritä DFU laitteen toimittaja/tuotetunnus(s)" +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Määritä dbx-tietokantatiedosto" + msgid "Specify the number of bytes per USB transfer" msgstr "Määritä tavujen määrä USB-siirtoa kohti" @@ -1245,10 +1608,42 @@ msgstr "Laitteiden tarkistussummat vahvistettu onnistuneesti" msgid "Summary" msgstr "Yhteenveto" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Tuettu" + #. TRANSLATORS: Is found in current metadata msgid "Supported on remote server" msgstr "Tuettu etäpalvelimella" +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Keskeytä-tyhjäkäynnille" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Keskeytä-muistiin" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Vaihda laitteen laiteohjelmiston haaraa" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Järjestelmä vaatii ulkoisen virtalähteen" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 -jälleenrakennus" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Pilaantunut" + msgid "Target" msgstr "Kohde" @@ -1256,6 +1651,23 @@ msgstr "Kohde" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "LVFS on ilmainen palvelu, joka toimii itsenäisenä oikeushenkilönä eikä sillä ole yhteyttä $OS_RELEASE:NAME$. Jakelijasi ei ehkä ole tarkistanut mitään laiteohjelmistopäivityksiä, jotka ovat yhteensopivia järjestelmän tai liitettyjen laitteiden kanssa. Kaikki laiteohjelmistot on tarkoitettu vain alkuperäisen laitteen valmistajalle." +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 eroaa rakennettavasta." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Palvelu on ladannut 3-osapuolen koodin, eikä kehittäjät enää jatkossa tue sitä!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Laitteistotoimittaja %s ei toimita ohjelmistoa kohteesta %s." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Estettyjä firmware-tiedostoja ei ole" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "There is no approved firmware." @@ -1263,11 +1675,23 @@ msgstr "Hyväksyttyä laiteohjelmistoa ei ole." #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" -msgstr "Tämä ohjelma voi toimia vain juuressa 'root'" +msgstr "Tämä ohjelma toimii oikein vain root-käyttäjän oikeuksin" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "Tämä sisältää firmwaren, jota ei ole vientikiellossa, mutta jota laitteistotoimittaja testaa edelleen. Varmista, että voit päivittää firmwaren manuaalisesti, jos päivitys epäonnistuu." +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Järjestelmässä on HSI-suoritusajan ongelma." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Tämän järjestelmän HSI-tietoturvataso on alhainen." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Tämän työkalun avulla järjestelmänvalvoja voi käyttää UEFI dbx-päivityksiä." + #. TRANSLATORS: the user needs to stop playing with stuff msgid "This tool can only be used by the root user" msgstr "Tätä työkalua voi käyttää vain root-käyttäjä" @@ -1276,10 +1700,42 @@ msgstr "Tätä työkalua voi käyttää vain root-käyttäjä" msgid "Type" msgstr "Tyyppi" +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP-osiota ei havaittu tai määritetty" + #. TRANSLATORS: program name msgid "UEFI Firmware Utility" msgstr "UEFI firmware -apuohjelma" +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled" +msgstr "UEFI-päivitykset eivät ole saatavilla tai käytössä" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx-apuohjelma" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI secure boot" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Yhteys palveluun ei onnistu" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Pura nykyinen ohjain" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Laiteohjelmiston poistaminen käytöstä:" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Salaamaton" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -1291,12 +1747,20 @@ msgid "Unknown Device" msgstr "Tuntematon laite" msgid "Unlock the device to allow access" -msgstr "Sallia pääsy laitteeseen" +msgstr "Avaa laitteen lukitus salliaksesi käytön" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Auki" #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" msgstr "Avaa pääsy laitteen laiteohjelmistoon" +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Poistaa ESP:n käytöstä" + #. TRANSLATORS: command line option msgid "Unset the debugging flag during update" msgstr "Poista virheenkorjauksen lippu päivityksen aikana" @@ -1306,13 +1770,17 @@ msgstr "Poista virheenkorjauksen lippu päivityksen aikana" msgid "Unsupported daemon version %s, client version is %s" msgstr "Ei tuettu taustaprosessin versio %s, asiakasversio on %s" +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Puhdistettu" + #. TRANSLATORS: Device is updatable in this or any other mode msgid "Updatable" msgstr "Päivitettävissä" #. TRANSLATORS: error message from last update attempt msgid "Update Error" -msgstr "Päivitys virhe" +msgstr "Päivitysvirhe" #. TRANSLATORS: helpful messages from last update #. TRANSLATORS: helpful messages for the update @@ -1337,7 +1805,7 @@ msgstr "Päivitä nyt?" #. TRANSLATORS: Update can only be done from offline mode msgid "Update requires a reboot" -msgstr "Päivitys edellyttää käynnistyksen" +msgstr "Päivitys edellyttää uudelleenkäynnistyksen" #. TRANSLATORS: command description msgid "Update the stored cryptographic hash with current ROM contents" @@ -1354,6 +1822,9 @@ msgstr "Päivitä tallennetut metatiedot nykyiseen sisältöön" msgid "Updates all firmware to latest versions available" msgstr "Päivittää kaikki laiteohjaimet uusimpiin saatavilla oleviin versioihin" +msgid "Updating" +msgstr "Päivittää" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" @@ -1364,7 +1835,7 @@ msgstr "Korotus %s -sta %s tulos %s... " #. TRANSLATORS: %1 is a device name #, c-format msgid "Updating %s…" -msgstr "Päivittää %s…" +msgstr "Päivittää laitetta %s…" #. TRANSLATORS: message letting the user know an upgrade is available #. * %1 is the device name and %2 and %3 are version strings @@ -1372,10 +1843,6 @@ msgstr "Päivittää %s…" msgid "Upgrade available for %s from %s to %s" msgstr "Päivitys saatavilla %s alkaen %s kohde %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Lähetä viesti:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Lataa raportit vain tällä kertaa, mutta pyydä uudelleen tulevissa päivityksissä" @@ -1383,7 +1850,7 @@ msgstr[1] "Lataa raportit vain tällä kertaa, mutta pyydä uudelleen tulevissa #. TRANSLATORS: ask the user to upload msgid "Upload report now?" -msgstr "Lataa raportti nyt?" +msgstr "Lähetetäänkö raportti nyt?" msgid "Upload report this time and automatically upload reports after completing future updates" msgid_plural "Upload reports this time and automatically upload reports after completing future updates" @@ -1398,6 +1865,14 @@ msgstr "Firmware-raporttien lataaminen auttaa laitteistotoimittajia tunnistamaan msgid "Urgency" msgstr "Kiireellisyys" +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Suorita fwupdmgr --help nähdäksesi ohjeet" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Suorita fwupdtool --help nähdäksesi ohjeet" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "Käytä Quirk-lippuja asennettaessa laiteohjelmistoa" @@ -1410,6 +1885,14 @@ msgstr "Käyttäjälle on ilmoitettu" msgid "Username" msgstr "Käyttäjätunnus" +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Voimassa" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Vahvistetaan ESP-sisältöä..." + #. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') msgid "Variant" msgstr "Muunnelma" @@ -1422,9 +1905,13 @@ msgstr "Toimittaja" msgid "Verifying…" msgstr "Vahvistetaan…" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "VAROITUS: SSL tarkistusten sivuuttaminen, jotta se tapahtuu automaattisesti tulevassa DISABLE_SSL_STRICT ympäristössä" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versio" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "VAROITUS:" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1446,6 +1933,10 @@ msgstr "Kirjoita firmware tiedostosta laitteeseen" msgid "Write firmware from file into one partition" msgstr "Kirjoita firmware tiedostosta osioon" +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Kirjoitetaan tiedosto:" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Kirjoitetaan…" @@ -1454,19 +1945,19 @@ msgstr "Kirjoitetaan…" msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." msgstr "Jakelijasi ei ehkä ole tarkistanut mitään laiteohjelmistopäivityksiä, jotka ovat yhteensopivia järjestelmän tai liitettyjen laitteiden kanssa." +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Laitteistosi saattaa vaurioitua tämän laiteohjelmiston avulla ja asentaminen voi mitätöidä %stakuun." + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "oletus" + #. TRANSLATORS: program name msgid "fwupd TPM event log utility" -msgstr "fwupd TPM tapahtumalokin apuohjelma" +msgstr "fwupd:n TPM-tapahtumalokin apuohjelma" -#. TRANSLATORS: message letting the user know no device upgrade available due -#. to missing on LVFS -#. * %1 is the device name -#, c-format -msgid "• %s has no available firmware updates" -msgstr "• %s ei ole saatavilla firmware päivityksiä" - -#. TRANSLATORS: message letting the user know no device upgrade available -#. * %1 is the device name -#, c-format -msgid "• %s has the latest available firmware version" -msgstr "• %s on uusin saatavilla oleva firmware-versio" +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd-liitännäiset" diff --git a/po/fr.po b/po/fr.po index d6eae3871..ffadc821b 100644 --- a/po/fr.po +++ b/po/fr.po @@ -3,7 +3,9 @@ # This file is distributed under the same license as the fwupd package. # # Translators: +# Corentin Noël , 2020 # Franck , 2015 +# Julien Humbert , 2020 msgid "" msgstr "" "Project-Id-Version: fwupd\n" @@ -15,19 +17,135 @@ msgstr "" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f minute restante" +msgstr[1] "%.0f minutes restantes" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Version %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u jour" +msgstr[1] "%u jours" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u heure" +msgstr[1] "%u heures" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minute" +msgstr[1] "%u minutes" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u seconde" +msgstr[1] "%u secondes" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Added" +msgstr "Ajouté" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Âge" + #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format msgid "Alias to %s" msgstr "Alias de %s" +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Authentification…" + #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on this machine" msgstr "Une authentification est nécessaire pour mettre à jour le micrologiciel sur cette machine" +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Annuler" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Annulé" + +#. TRANSLATORS: this is when a device is hotplugged +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Modifié" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Somme de contrôle" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Commande non trouvée" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Critique" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Version actuelle" + #. TRANSLATORS: for the --verbose arg msgid "Debugging Options" msgstr "Options de débogage" +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Décompression…" + +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Description" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Détails" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Périphérique ajouté :" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Périphérique modifié :" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Périphérique retiré :" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Désactivé" + +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Terminé !" @@ -39,6 +157,24 @@ msgstr "Terminé !" msgid "Downgrading %s from %s to %s... " msgstr "Rétrogradation de %s de %s en %s" +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Téléchargement…" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Durée" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Activé" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Chiffré" + #. TRANSLATORS: exit after we've started up, used for user profiling msgid "Exit after a small delay" msgstr "Quitter après un bref délai" @@ -47,14 +183,31 @@ msgstr "Quitter après un bref délai" msgid "Exit after the engine has loaded" msgstr "Quitter après le chargement du moteur" +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Échec" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Echec de l'analyse des paramètres" +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Nom de fichier" + #. TRANSLATORS: program summary msgid "Firmware Update D-Bus Service" msgstr "Service D-Bus de mise à jour des micrologiciels" +#. TRANSLATORS: description of plugin state, e.g. disabled +#. TRANSLATORS: release properties +msgid "Flags" +msgstr "Drapeaux" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Trouvé" + #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Obtenir la liste des périphériques supportant les mises à jour de micrologiciel" @@ -63,14 +216,127 @@ msgstr "Obtenir la liste des périphériques supportant les mises à jour de mic msgid "Gets details about a firmware file" msgstr "Obtenir les détails d'un fichier de micrologiciel" +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Haute" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: command description msgid "Install a firmware file on this hardware" msgstr "Installer un fichier de micrologiciel sur ce matériel" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Installation sur %s…" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Périphérique interne" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Invalide" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Moins d’une minute restante" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licence" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Noyau Linux" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Chargement…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Verrouillé" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Faible" + +msgid "MEI version" +msgstr "Version MEI" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Moyenne" + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Version minimum" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Nouvelle version" + #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Aucun matériel ayant des capacités de mise à jour du micrologiciel n'a été détecté" +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Non trouvé" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Non pris en charge" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Mot de passe" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Version précédente" + +msgid "Print the version number" +msgstr "Afficher le numéro de version" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Priorité" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Propriétaire" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Lecture depuis %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Lecture…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Redémarrage…" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second is a version number #. * e.g. "1.2.3" @@ -78,6 +344,14 @@ msgstr "Aucun matériel ayant des capacités de mise à jour du micrologiciel n' msgid "Reinstalling %s with %s... " msgstr "Réinstallation de %s en %s" +#. TRANSLATORS: this is when a device is hotplugged +msgid "Removed" +msgstr "Retiré" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Redémarrage du périphérique…" + #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Montrer les options de débogage" @@ -86,9 +360,72 @@ msgstr "Montrer les options de débogage" msgid "Show extra debugging information" msgstr "Montre des informations de débogage complémentaires" +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Taille" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Source" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Résumé" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Pris en charge" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Type" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Déchiffré" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Inconnu" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Déverrouillé" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" #, c-format msgid "Updating %s from %s to %s... " msgstr "Mise à jour de %s de %s en %s" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Mise à jour de %s…" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Nom d’utilisateur" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Valide" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variante" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Vérification…" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Écriture…" diff --git a/po/fur.po b/po/fur.po index f1e13964d..307107dde 100644 --- a/po/fur.po +++ b/po/fur.po @@ -252,6 +252,7 @@ msgid_plural "Do not upload reports, and never ask to upload reports for future msgstr[0] "No sta inviâ il rapuart e no sta domandâ plui di inviâ i rapuarts pai inzornaments futûrs" msgstr[1] "No sta inviâ i rapuarts e no sta domandâ plui di inviâ i rapuarts pai inzornaments futûrs" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Fat!" @@ -292,6 +293,8 @@ msgstr "Abilite il supuart dal inzornament dal firmware sui sistemis supuartâts msgid "Enable this remote?" msgstr "Abilitâ chest rimot?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Abilitât" @@ -394,6 +397,10 @@ msgstr "Su cheste machine a son supuartâts i inzornaments firmware." msgid "Force the action ignoring all warnings" msgstr "Sfuarce la azion ignorant ducj i avertiments" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Cjatât" + #. TRANSLATORS: command description msgid "Get all devices and possible releases" msgstr "Oten ducj i dispositîfs e lis pussibilis publicazions" @@ -497,6 +504,7 @@ msgstr "Modifiche la configurazion dal demoni" msgid "Monitor the daemon for events" msgstr "Monitore il demoni pai events" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Nissune azion specificade!" @@ -530,14 +538,14 @@ msgstr "Nissun rimot disponibil" msgid "No updates were applied" msgstr "Nol è stât aplicât nissun inzornament" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Va ben" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Mostre dome il valôr PCR" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Ignore i avertiments dal plugin" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Passe parsore al valôr dal percors ESP predefinît" @@ -664,6 +672,7 @@ msgstr "Dispositîf selezionât" msgid "Set the debugging flag during update" msgstr "Stabilìs la opzion di debug dilunc l'inzornament" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Al stabilìs la liste dai firmware aprovâts" @@ -858,10 +867,6 @@ msgstr "Daûr a inzornâ %s…" msgid "Upgrade available for %s from %s to %s" msgstr "Inzornament disponibil par %s di %s a %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Messaç dal inviament:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Invie il rapuart dome cheste volte, ma torne domande pai inzornaments futûrs" @@ -888,6 +893,10 @@ msgstr "Non utent" msgid "Verifying…" msgstr "Daûr a verificâ…" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "In spiete…" diff --git a/po/gl.po b/po/gl.po new file mode 100644 index 000000000..1232a25f4 --- /dev/null +++ b/po/gl.po @@ -0,0 +1,1010 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Fran Diéguez , 2020 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Galician (http://www.transifex.com/freedesktop/fwupd/language/gl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: gl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] " Falta %.0f minuto" +msgstr[1] " Faltan %.0f minutos" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Modo de fabricación %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Activar os dispositivos pendentes" + +msgid "Activate the new firmware on the device" +msgstr "Activar o novo firmware no dispositivo" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Added" +msgstr "Engadido" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Aceptar e activar o remoto?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alias a %s" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Permitir a desactualización de versións de firmware" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Permitir a reinstalación de versións de firmware existentes" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Aplicar actualizacións de firmware" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Aplicar actualización incluso cando non se avise" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Aplicar ficheiros de actualización" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Aplicando actualización…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Firmware aprovados:" +msgstr[1] "Firmware aprovado:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Preguntar de novo a seguinte vez?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Anexarse ao modo de firmware" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Autenticando…" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Requírese autenticación para desactualizar o firmware nun dispositivo extraíbel" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Requírese autenticación para desactualizar o firmware nesta máquina" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Requírese autenticación para modificar a configuración do remoto usado para actualizar firmwares" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Requírese autenticación para modificar a configuración do demonio" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Requírese autenticación para estabelecer a lista do firmware aprovado" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Requírese autenticación para asinar os datos usando o certificado do cliente" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Requírese autenticación para trocar a unha nova versión do firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Requírese autenticación para desbloquear un dispositivo" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Requírese autenticación para actualizar o firmware nun dispositivo extraíbel" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Requírese autenticación para actualizar o firmware nesta máquina" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Requírese autenticación para actualizar as sumas de verificación para o dispositivo" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Cancelar" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Cancelado" + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Non foi posíbel aplicar as actualizacións no soporte multimedia" + +#. TRANSLATORS: this is when a device is hotplugged +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Cambiado" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Seleccione un dispositivo:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Escolla o tipo de firmware:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Escolla a publicación:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Escolla un volume:" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Orde non atopada" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Converter un ficheiro de firmware" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Utilidad DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Opcións de depuración" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Descomprimindo…" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Desanexarse ao modo de firmware" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Dispositivo engadido:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Dispositivo cambiado" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Dispositivo retirado:" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Dispositivos que foron actualizados con éxito:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Desactivado" + +msgid "Disabled fwupdate debugging" +msgstr "Desactivar a depuración de fwupdate" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Mostrar versión" + +#. TRANSLATORS: command line option +msgid "Do not check for reboot after update" +msgstr "Non marcar para reiniciar despois de actualizar" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Non incluír o dominio do rexistro" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Non incluír o prefixo de marca de tempo" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Non levar a cabo comprobacións de dispositivo" + +msgid "Do not upload report at this time, but prompt again for future updates" +msgid_plural "Do not upload reports at this time, but prompt again for future updates" +msgstr[0] "Non subir reporte esta vez, pero preguntar de novo en futuras actualizacións" +msgstr[1] "Non subir reportes esta vez, pero preguntar de novo en futuras actualizacións" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Feito!" + +#. TRANSLATORS: the first replacement is a display name +#. * e.g. "ColorHugALS" and the second and third are +#. * version numbers e.g. "1.2.3" +#, c-format +msgid "Downgrading %s from %s to %s... " +msgstr "Desactualizando %s desde %s a %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Desactualizando %s" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Descargando…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Volcar os datos da SMBIOS desde un ficheiro " + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "O ESP especificado non é válido" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Activar a compatibilidade de actualización de firmware nos sistemas admitidos" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Desexa activar este remoto?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Activad" + +msgid "Enabled fwupdate debugging" +msgstr "Activar a depuración de fwupdate" + +msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." +msgstr "Active esta funcionalidade baixo o seu risco, o que significa que ten que contactar co seu fabricante de equipamento orixinal se ten calquera problema con estas actualizacións. Só os problemas co proceso de actualización en si deberían enviarse en $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Cifrad" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM cifrada" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Borrando…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Saír despois dun pequeno atraso" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Saír despois de que se cargue o motor" + +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Fallido" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Produciuse un fallo ao aplicar a actualización" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Produciuse un fallo ao conectarse ao demoni" + +#. TRANSLATORS: could not parse file +msgid "Failed to extract local dbx " +msgstr "Produciuse un fallo ao extraer o dbx local" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Produciuse un fallo ao obter a lista de dispositivos pendentes" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Produciuse un fallo ao instalar a actualización do firmware" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Produciuse un fallo ao cargar o dbx local" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Produciuse un fallo ao cargar o dbx do sistema" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Produciuse un fallo ao analizar os argumentos" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Produciuse un fallo ao analizar o ficheiro" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Produciuse un fallo ao analizar as bandeiras para --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Produciuse un fallo ao analizar a dbx local" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Produciuse un fallo ao reiniciar" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Produciuse un fallo ao modo splash" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Produciuse un fallo ao validar os contidos do ESP" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Requírese un nome de ficheiro" + +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Axente do firmware" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Servizo D-Bus da Actualización do Firmware" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Demonio de Actualización de Firmware" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Utilidade de Firmware" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Actualizacións de firmware" + +msgid "Firmware updates are not supported on this machine." +msgstr "Esta máquina non admite as actualizacións de firmware." + +msgid "Firmware updates are supported on this machine." +msgstr "Esta máquina admite as actualizacións de firmware." + +msgid "Force the action ignoring all warnings" +msgstr "Forzar a acción ignorando todas as advertencias" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Atopado" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Obter todas as bandeiras de dispositivos admitidos por fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices and possible releases" +msgstr "Obter todos os dispositivos e publicacións posíbeis" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Obtén todos os dispositivos que admiten actualizacións de firmware" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Obter todos os engadidos activos rexistrados no sistema" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Obtén a información sobre o ficheiro de firmware" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Obten os remotos configurados" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Obtén os atributos de seguranza do equipo" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Obtén a lista de actualizacións para o hardware conectado" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ID de seguranza do equipo:" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Ocioso…" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Instalar un blob de firmware nun dispositivo" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Instalar un ficheiro de firmware neste hardware" + +msgid "Install old version of system firmware" +msgstr "Instalar unha versión antiga do firmware do sistema" + +msgid "Install signed device firmware" +msgstr "Instalar firmware de dispositivo asinado" + +msgid "Install signed system firmware" +msgstr "Instalar firmware do sistema asinado" + +msgid "Install unsigned device firmware" +msgstr "Instalar firmware de dispositivo non asinado" + +msgid "Install unsigned system firmware" +msgstr "Instalar firmware do sistema non asinado" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Instalando Firmware…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Instalando actualización do firmware…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Instalando en %s…" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "ACM protexido Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Política de erro de Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Arrinque verificado de Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET activo" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET activado" + +#. TRANSLATORS: Title: Direct Connect Interface (DCI) allows +#. * debugging of Intel processors using the USB3 port +msgid "Intel DCI debugger" +msgstr "Depurador de Intel DCI" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "SMAP de Intel" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Non válido" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Falta menos dun minuto" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Servizo de Linux Vendor Firmware (firmware estábel)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Servizo de Linux Vendor Firmware (firmware de probas)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Núcleo de Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Bloqueo de kernel de Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Swap de Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Mostrar as entradas no dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Mostrar actualizacións de firmware compatíbeis" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Lista todos os tipos de firmware dispoñíbeis" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Cargando…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Bloqueado" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Modo de fabricación MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Omitir MEI" + +msgid "MEI version" +msgstr "Versión do MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Activar manualmente engadidos específicos" + +msgid "Modify a configured remote" +msgstr "Modificar un remoto configuración" + +msgid "Modify daemon configuration" +msgstr "Modificación configuración do demonio" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Monitorizar eventos no demonio" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Non se require ningunha acción!" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Non se atoparon IDs de firmware" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Non se atoparon engadidos" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Non hai publicacións dispoñíbeis" + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Non hai remotos dispoñíbeis" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Non se aplicou ningunha actualización" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Non atopado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Non compatíbel" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Mostrar só un valor de PCR" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Sobrescribir a ruta predefinida do ESP" + +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Omitir avisos e forzar a acción" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Analixar e mostrar a información dun ficheiro de firmware" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Analizando a dbx de actualizacións…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Analizando o dbx do sistema…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Porcentaxe completado" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Protección de DMA pre-arrinque" + +msgid "Print the version number" +msgstr "Imprime o número de versión" + +msgid "Print verbose debug statements" +msgstr "Imprime as sentencias de depuración verbosas" + +msgid "Proceed with upload?" +msgstr "Desexa seguir coa subida?" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Consultar compatibilidade da actualización do firmware" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Ler un blob de firmware desde un dispositivo" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Ler firmware desde o dispositivo nun ficheiro" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Ler firmware desde unha partición nun ficheiro" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Lendo de %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Lendo…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Reiniciando…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Actualiza os metadatos desde un servidor remoto" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Reinstalar firmware nun dispositivo" + +#. TRANSLATORS: the first replacement is a display name +#. * e.g. "ColorHugALS" and the second is a version number +#. * e.g. "1.2.3" +#, c-format +msgid "Reinstalling %s with %s... " +msgstr "Reinstalando %s con %s... " + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Removed" +msgstr "Retirado" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Substituír datos nun ficheiro de firmware existente" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Require conexión a internet" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Desexa reiniciar o demonio para facer o cambio efectivo?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Reiniciando dispositivo…" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Devolve todos os IDs do hardware da máquina" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Rexión da BIOS Do SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Bloqueo do SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Escritura do SPI" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Planificando…" + +#. TRANSLATORS: Device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Seleccionar un dispositivo" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volume seleccionado" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Establecer a bandeira de depuración durante a actualización" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Estabelece a lista do firmware aprobado" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Mostrar as versións de cliente e demonio" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Mostrar opcións de depuración" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Mostrar os dispositivos que non son actualizábeis" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Mostrar información de depuración adicional" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Mostrar o historial das actualizacións de firmware" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Mostrar información de depuración do engadido" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Mostrar a versión calculada do dbx" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Mostrar rexistro de depuración desde o último intento de actualización" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Mostrar a información de estado da actualización do firmware" + +msgid "Sign data using the client certificate" +msgstr "Asinar os datos usando o certificafo do cliente" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Asinar os datos usando o certificafo do cliente" + +msgid "Signature" +msgstr "Sinatura" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Especifique os IDs do Fabricante/Produto do dispositivo DFU" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Especificar o ficheiro de base de datos dbx" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Especifique o número de bytes por transferenvia USB" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Remoto desactivado correctamente" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Remoto activado correctamente" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Instalación do firmware exitosa" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Remoto modficado correctamente" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Comprobáronse correctamente as sumas de verificación do dispositivo" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Compatíbel" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspend-a-ocioso" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspender-a-ram" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Reconstrución do PCR0 de TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +msgid "Target" +msgstr "Obxectivo" + +#. TRANSLATORS: do not translate the variables marked using $ +msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." +msgstr "O LVFS é o servizo que opera como unha entidade legal independente e non ten ligazón con $OS_RELEASE:NAME$. O seu distribuidor podería non ter que comprobar se as actualizacións de firmware teñen compatibilidade co seu sistema ou dispositivos conectados. Todos os firmware son fornecidos por fabricantes de equipamento orixinal." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Este programa podería funcionar correctamente só como root" + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Esta ferrametne só pode ser usada por un usuario root" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Utilidade de firmware de UEFI" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Utilidade UEFI dbx" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Arrinque seguro de UEFI" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Descifrado" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Descoñecido" + +msgid "Unlock the device to allow access" +msgstr "Desbloquear o dispositivo para permitir o acceso" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Desbloqueado" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Quitar a bandeira de depuración durante a actualización" + +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Actualizar todos os dispositivos que coincidan os metadatos locais" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Actualizar agora" + +msgid "Update the stored device verification information" +msgstr "Actualizar a información de verificación almacenada no dispositivo" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Actualizaar os metadatos almacenados cos contidos actuais" + +msgid "Updating" +msgstr "Actualizando" + +#. TRANSLATORS: the first replacement is a display name +#. * e.g. "ColorHugALS" and the second and third are +#. * version numbers e.g. "1.2.3" +#, c-format +msgid "Updating %s from %s to %s... " +msgstr "Actualizando %s desde %s a %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Actualizando %s…" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Desexa subir o informe agora?" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Usar fwupdtool --help para obter axuda" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Válido" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validando os contidos do ESP…" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Verificando…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versión" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Agardando…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Facer seguimento dos cambios de hardware" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Escribir firmware desde un ficheiro nun dispositivo" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Escribir firmware desde un ficheiro nunha partición" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Escribindo…" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "Utilidade de rexistro de eventos de TPM de fwupd" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Engadidos de fwupd" diff --git a/po/he.po b/po/he.po index 4436ff1e8..6cbf390b9 100644 --- a/po/he.po +++ b/po/he.po @@ -41,6 +41,7 @@ msgstr "אפשרויות ניפוי שגיאות" msgid "Description" msgstr "תיאור" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "הסתיים!" @@ -88,6 +89,10 @@ msgstr "מתקין קובץ קושחה בחומרה זו" msgid "No hardware detected with firmware update capability" msgstr "לא אותרה חומרה בעלת יכולת עדכון קושחה" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "אישור" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second is a version number #. * e.g. "1.2.3" @@ -109,3 +114,7 @@ msgstr "הצג מידע ניפוי שגיאות מורחב" #, c-format msgid "Updating %s from %s to %s... " msgstr "מעדכן %s מ־%s ל־%s..." + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "גרסא" diff --git a/po/hr.po b/po/hr.po index 0dc31ddc7..a060c9a7d 100644 --- a/po/hr.po +++ b/po/hr.po @@ -5,6 +5,7 @@ # Translators: # FIRST AUTHOR , 2016 # gogo , 2016 +# milotype , 2020 # gogo , 2016-2020 msgid "" msgstr "" @@ -31,6 +32,12 @@ msgstr[2] "%.0f minuta preostalo" msgid "%s CPU Microcode Update" msgstr "%s nadopuna CPU mikrokôda" +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s nadopuna podešavanja" + #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format @@ -94,6 +101,11 @@ msgstr "%s nadopuna" msgid "%s and all connected devices may not be usable while updating." msgstr "%s i svi spojeni uređaji možda neće biti upotrebljivi tijekom nadopune." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s način rada za proizvođače" + #. TRANSLATORS: warn the user before updating, %1 is a device name #, c-format msgid "%s must remain connected for the duration of the update to avoid damage." @@ -104,6 +116,21 @@ msgstr "%s mora ostati spojen tijekom trajanja nadopune kako bi se izbjeglo ošt msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." msgstr "%s mora ostati spojen s izvorom energije tijekom trajanja nadopune kako bi se izbjeglo oštećenje." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s zaobilaženje" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s inačica" + #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -151,6 +178,10 @@ msgstr[0] "%u sekunda" msgstr[1] "%u sekunde" msgstr[2] "%u sekundi" +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(zastarjelo)" + #. TRANSLATORS: command description msgid "Activate devices" msgstr "Aktiviraj uređaj" @@ -211,6 +242,18 @@ msgstr "Odgovori 'da' na sva pitanja" msgid "Apply firmware updates" msgstr "Primijeni nadopune firmvera" +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Primijeni nadopunu iako nije preporučljivo" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Primijeni nadopunu datoteka" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Primjena nadopuna…" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "Approved firmware:" @@ -219,6 +262,10 @@ msgstr[0] "Odobreni firmver:" msgstr[1] "Odobreni firmveri:" msgstr[2] "Odobreni firmveri:" +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Upitaj ponovno sljedeći put?" + #. TRANSLATORS: command description msgid "Attach to firmware mode" msgstr "Prebaci u način firmvera" @@ -275,10 +322,26 @@ msgstr "Potrebna je ovjera za nadopunu spremljenog kontrolnog zbroja uređaja" msgid "Automatic Reporting" msgstr "Automatsko izvještavanje" +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Automatski pošalji svaki put?" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Datoteke blokiranog firmvera:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blokiranje firmvera:" + #. TRANSLATORS: firmware version of bootloader msgid "Bootloader Version" msgstr "Inačica učitača pokretanja" +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Grana" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Izgradi firmver u osiguranom okruženju" @@ -291,6 +354,14 @@ msgstr "Odustani" msgid "Cancelled" msgstr "Prekinuto" +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Nemoguća primjena kao dbx nadopune jer je već primijenjeno." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Nemoguća primjena na live medij" + #. TRANSLATORS: this is when a device is hotplugged #. TRANSLATORS: this is when the daemon state changes msgid "Changed" @@ -304,6 +375,11 @@ msgstr "Provjeri podudarnost kriptografske jedinstvene vrijednosti firmvera" msgid "Checksum" msgstr "Kontrolni zbroj" +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Odaberi granu:" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Odaberite uređaj:" @@ -316,6 +392,10 @@ msgstr "Odaberi vrstu firmvera:" msgid "Choose a release:" msgstr "Odaberi izdanje:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Odaberite uređaj:" + #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" msgstr "Uklanja sve nadopune zakazne za izvanmrežno nadopunjivanje" @@ -436,6 +516,20 @@ msgstr "Uređaji koji su uspješno nadopunjeni:" msgid "Devices that were not updated correctly:" msgstr "Uređaji koji nisu ispravno nadopunjeni:" +#. TRANSLATORS: message letting the user know no device upgrade available due +#. to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Uređaji bez dostupnih nadopuna firmvera: " + +#. TRANSLATORS: message letting the user know no device upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "Uređaji s najnovijom dostupnom inačicom firmvera:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Onemogućeno" + msgid "Disabled fwupdate debugging" msgstr "Onemogući fwupdate otklanjanje grešaka" @@ -487,6 +581,7 @@ msgstr[2] "Ne šaljite izvještaje, i nemoj nikada upitati za slanje izvještaja msgid "Do not write to the history database" msgstr "Ne zapisuj bazu podataka povijesti" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Završeno!" @@ -531,6 +626,8 @@ msgstr "Omogući podršku nadopune firmvera na podržanim sustavima" msgid "Enable this remote?" msgstr "Omogući ovu udaljenu lokaciju?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Omogućeno" @@ -549,6 +646,14 @@ msgstr "Omogućujete ovu funkcionalnost na vlastiti rizik, što znači da morate msgid "Enabling this remote is done at your own risk." msgstr "Ovu udaljenu lokaciju omogućavate na vlastiti rizik." +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Šifrirano" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Šifrirani RAM" + #. TRANSLATORS: command description msgid "Erase all firmware update history" msgstr "Obriši svu povijest nadopune firmvera" @@ -565,13 +670,21 @@ msgstr "Izađi nakon kratke odgode" msgid "Exit after the engine has loaded" msgstr "Izađi nakon učitavanja pogona" +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Neuspjelo" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Neuspjela primjena nadopune" + #. TRANSLATORS: we could not talk to the fwupd daemon msgid "Failed to connect to daemon" msgstr "Neuspjelo povezivanje s pozadinskim programom" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Neuspjelo preuzimanje zbog ograničenja poslužitelja" +#. TRANSLATORS: could not parse file +msgid "Failed to extract local dbx " +msgstr "Neuspjelo raspakiravanje lokalnog dbx-a" #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" @@ -581,10 +694,19 @@ msgstr "Neuspjeli prikaz uređaja na čekanju" msgid "Failed to install firmware update" msgstr "Neuspjela instalacija nadopune firmvera" +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Neuspjelo učitavanje lokalnog dbx-a" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Neuspjelo učitavanje okolnosti uređaja" +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Neuspjelo učitavanje dbx-a sustava" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Neuspjela obrada argumenata" @@ -597,6 +719,10 @@ msgstr "Neuspjela obrada datoteke" msgid "Failed to parse flags for --filter" msgstr "Neuspjela obrada oznake za --filter" +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Neuspjela obrada lokalnog dbx-a" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Neuspjelo ponovno pokretanje" @@ -605,21 +731,10 @@ msgstr "Neuspjelo ponovno pokretanje" msgid "Failed to set splash mode" msgstr "Neuspjelo postavljanje splash načina" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Dohvaćanje datoteke" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Dohvaćanje firmvera" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Dohvaćanje metapodataka" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Dohvaćanje potpisa" +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Neuspjela provjera ESP sadržaja" #. TRANSLATORS: filename of the local file msgid "Filename" @@ -629,6 +744,10 @@ msgstr "Naziv datoteke" msgid "Filename Signature" msgstr "Potpis naziva datoteke" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Potreban je naziv datoteke" + #. TRANSLATORS: command line option msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" msgstr "Filter sa skupom oznaka uređaja koji koristi ~ prefiks za izuzimanje, npr. 'internal,~needs-reboot'" @@ -653,6 +772,18 @@ msgstr "Pozadinski program nadopune firmvera" msgid "Firmware Utility" msgstr "Firmver pomagalo" +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Frimver provjera" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Firmver je već blokiran" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Firmver još nije blokiran" + #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format msgid "Firmware metadata has not been updated for %u day and may not be up to date." @@ -661,25 +792,34 @@ msgstr[0] "Metapodaci firmvera nisu nadopunjeni %u dan i možda nisu najnoviji." msgstr[1] "Metapodaci firmvera nisu nadopunjeni %u dana i možda nisu najnoviji." msgstr[2] "Metapodaci firmvera nisu nadopunjeni %u dana i možda nisu najnoviji." +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Nadopune frimvera" + msgid "Firmware updates are not supported on this machine." msgstr "Nadopuna firmvera nije podržana na ovom računalu." msgid "Firmware updates are supported on this machine." msgstr "Nadopuna firmvera je podržana na ovom računalu." +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Oznake" msgid "Force the action ignoring all warnings" -msgstr "Prisili radnju zanemarivanja svih upozorenja" +msgstr "Prisili radnju zanemarujući sva upozorenja" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Pronađeno" #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" -msgstr[0] "GUID" -msgstr[1] "GUID" -msgstr[2] "GUID" +msgstr[0] "Globalna jedinstvena oznaka" +msgstr[1] "Globalne jedinstvene oznake" +msgstr[2] "Globalne jedinstvene oznake" #. TRANSLATORS: command description msgid "Get all device flags supported by fwupd" @@ -705,9 +845,9 @@ msgstr "Prikaži pojedinosti datoteke firmvera" msgid "Gets the configured remotes" msgstr "Prikazuje udaljena podešavanja" -#. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Preuzima popis odobrenih firmvera." +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Prikaži sigurnosne značajke poslužitelja" #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -729,6 +869,15 @@ msgstr "Hardver čeka da ponovno bude spojen" msgid "High" msgstr "Visoka" +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Sigurnosni ID poslužitelja:" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Mirovanje..." @@ -785,10 +934,57 @@ msgstr "Instalacija nadopune firmvera..." msgid "Installing on %s…" msgstr "Instaliram na %s…" +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM zaštićen" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP osigurač" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard pravilo greške" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard provjereno pokretanje" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET aktivan" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET omogućen" + +#. TRANSLATORS: Title: Direct Connect Interface (DCI) allows +#. * debugging of Intel processors using the USB3 port +msgid "Intel DCI debugger" +msgstr "Intel DCI otklanatelj grešaka" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + #. TRANSLATORS: Device cannot be removed easily msgid "Internal device" msgstr "Unutrašnji uređaj" +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Nevaljano" + #. TRANSLATORS: Is currently in bootloader mode msgid "Is in bootloader mode" msgstr "U načinu učitača pokretanja je" @@ -821,6 +1017,22 @@ msgstr "Firmver Usluga Linux Proizvođača (LVFS) (stabilni firmver)." msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Firmver Usluga Linux Proizvođača (LVFS) (testni firmver)." +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux kernel" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Linux kernel zaključavanje" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux swap" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Prikaži unose u dbx-u" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "Prikaži nadopune podržanih firmvera" @@ -829,17 +1041,39 @@ msgstr "Prikaži nadopune podržanih firmvera" msgid "List the available firmware types" msgstr "Prikaži dostupne vrste firmvera" +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Nabraja datoteke na ESP-u" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Učitavanje..." +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Zaključano" + #. TRANSLATORS: the release urgency msgid "Low" msgstr "Niska" +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI način rada za proizvođače" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI zaobilaženje" + +msgid "MEI version" +msgstr "MEI inačica" + #. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Ručno dopusti određene priključke" +msgid "Manually enable specific plugins" +msgstr "Ručno omogući određene priključke" #. TRANSLATORS: the release urgency msgid "Medium" @@ -866,10 +1100,6 @@ msgstr "Najmanja inačica" msgid "Mismatched daemon and client, use %s instead" msgstr "Pozadinski program i klijent se ne podudaraju, umjesto koristite %s" -#. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Prilagođava vrijednost podešavanja pozadinskog programa." - #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Promjena zadane udaljene lokacije" @@ -884,6 +1114,10 @@ msgstr "Prilagodi podešavanje pozadinskog programa" msgid "Monitor the daemon for events" msgstr "Nadgledaj događaje pozadinskim programom" +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Montira ESP" + #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware msgid "Needs a reboot after installation" msgstr "Potrebno je ponovno pokretanje nakon instalacije" @@ -896,6 +1130,7 @@ msgstr "Potrebno je isključivanje nakon instalacije" msgid "New version" msgstr "Nova inačica" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Nema zadane radnje!" @@ -933,14 +1168,22 @@ msgstr "Nema dostupnih udaljenih lokacija" msgid "No updates were applied" msgstr "Nema primijenjenih nadopuna" +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Nije pronađeno" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Nije podržano" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "U redu" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Prikaži samo jednu PRC vrijednost" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Zaobiđi upozorenja priključka" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Zaobiđi zadanu ESP putanju" @@ -953,10 +1196,21 @@ msgstr "Zaobiđi upozorenja i prisili radnju" msgid "Parse and show details about a firmware file" msgstr "Analiziraj i prikaži pojedinosti datoteke firmvera" +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Obrada dbx nadopune…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Obrada dbx-a sustava…" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Lozinka" +msgid "Payload" +msgstr "Sadržaj prijenosa" + #. TRANSLATORS: console message when not using plymouth msgid "Percentage complete" msgstr "Postotak završetka" @@ -966,6 +1220,14 @@ msgstr "Postotak završetka" msgid "Please enter a number from 0 to %u: " msgstr "Odaberite broj od 0 do %u: " +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Nedostaju ovisnosti dodatka" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "DMA zaštita predpokretanja" + #. TRANSLATORS: version number of previous firmware msgid "Previous version" msgstr "Prijašnja inačica" @@ -1020,10 +1282,6 @@ msgstr "Ponovno pokretanje…" msgid "Refresh metadata from remote server" msgstr "Osvježi metapodatke s udaljenog poslužitelja" -#. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "Ponovno instaliraj trenutni firmver na uređaju." - #. TRANSLATORS: command description msgid "Reinstall firmware on a device" msgstr "Ponovno instaliraj firmver na uređaj" @@ -1056,9 +1314,13 @@ msgstr "URI izvještaja" msgid "Reported to remote server" msgstr "Prijavljeno na udaljenom poslužitelju" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Zahtijeva AC napajanje" +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Potrebni efivarfs datotečni sustav nije pronađen" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Potrebni uređaj nije proneđen" #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" @@ -1095,6 +1357,22 @@ msgstr "Pokreni rutinu čišćenja sastavljanja priključka kada se koristi inst msgid "Run the plugin composite prepare routine when using install-blob" msgstr "Pokreni rutinu pripreme sastavljanja priključka kada se koristi install-blob" +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Sufiks vremenskog izvršavanja" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS područje" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI zaključavanje" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI zapisivanje" + #. TRANSLATORS: command line option msgid "Save device state into a JSON file between executions" msgstr "Spremi stanje uređaja u JSON datoteku između izvršavanja" @@ -1111,6 +1389,10 @@ msgstr "Zakazivanje..." msgid "Selected device" msgstr "Odabrani uređaj" +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Odabrani uređaj" + #. TRANSLATORS: serial number of hardware msgid "Serial Number" msgstr "Serijski broj" @@ -1119,17 +1401,18 @@ msgstr "Serijski broj" msgid "Set the debugging flag during update" msgstr "Postavi oznaku otklanjanja grešaka tijekom nadopune" -msgid "Sets the list of approved firmware" -msgstr "Postavlja popis odobrenih firmvera" - #. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." +msgid "Sets the list of approved firmware" msgstr "Postavlja popis odobrenih firmvera" #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Podijeli povijest firmvera sa razvijateljima" +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Prikaži sve rezultate" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "Prikaži inačicu klijenta i pozadinskog programa" @@ -1162,6 +1445,10 @@ msgstr "Prikaži povijest nadopune metapodataka" msgid "Show plugin verbose information" msgstr "Prikaži dodatne informacije priključka" +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Prikaži predviđenu inčicu DBX-a" + #. TRANSLATORS: command line option msgid "Show the debug log from the last attempted update" msgstr "Prikaži zapis otklanjanja grešaka posljednjeg pokušaja nadopune" @@ -1199,6 +1486,10 @@ msgstr "Izvor" msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Odredi ID-ove Proizvođača/Proizvoda DFU uređaja" +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Odredi datoteku dbx baze podataka" + msgid "Specify the number of bytes per USB transfer" msgstr "Odredi broj bajtova po USB prijenosu" @@ -1257,10 +1548,38 @@ msgstr "Kontrolni zbroj uređaja je uspješno provjeren" msgid "Summary" msgstr "Sažetak" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Podržano" + #. TRANSLATORS: Is found in current metadata msgid "Supported on remote server" msgstr "Podržano na udaljenom poslužitelju" +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspendiraju-u-mirovanje" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspendiraju-u-ram" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Sustav zahtijeva vanjski izvor energije" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 rekonstrukcija" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Pokvareno" + msgid "Target" msgstr "Odredište" @@ -1268,6 +1587,14 @@ msgstr "Odredište" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "LVFS (Firmver Usluga Linux Proizvođača) je besplatna usluga koja djeluje kao neovisna pravna osoba i nema veze sa $OS_RELEASE:NAME$. Vaš distributer možda nije provjerio nadopune firmvera za kompatibilnost s vašim sustavom ili priključenim uređajima. Svi firmveri su pružani od strane izvornih proizvođača opreme." +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 razlikuje se od rekonstrukcije." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Nema blokiranih firmvera" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "There is no approved firmware." @@ -1280,6 +1607,18 @@ msgstr "Ovaj program možda radi ispravno samo ako je pokrenut kao korijenski ko msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "Ova udaljena lokacija sadrži firmver koji nije zabranjen, ali se još uvijek testira od strane proizvođača hardvera. Provjerite da imate način na ručno vraćanje starije inačice firmvera ako nadopuna firmvera ne uspije." +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Ovaj sustav ima HSI probleme s vremenskim izvršavanjem." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Ovaj sustav ima nisku HSI sigurnosnu razinu." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Ovaj alat omogućuje administratoru primjenu UEFI dbx nadopuna." + #. TRANSLATORS: the user needs to stop playing with stuff msgid "This tool can only be used by the root user" msgstr "Ovaj alat može koristiti samo korijenski korisnik" @@ -1288,10 +1627,34 @@ msgstr "Ovaj alat može koristiti samo korijenski korisnik" msgid "Type" msgstr "Vrsta" +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP particija nije otkrivena ili konfigurirana" + #. TRANSLATORS: program name msgid "UEFI Firmware Utility" msgstr "UEFI firmver pomagalo" +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx pomagalo" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI sigurno pokretanje (secure boot)" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Neuspjelo povezivanje s udaljenom uslugom" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Neblokiranje firmvera:" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Nije šifrirano" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -1305,10 +1668,18 @@ msgstr "Nepoznat uređaj" msgid "Unlock the device to allow access" msgstr "Otključaj uređaj za dopuštenje pristupa" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Otključano" + #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" msgstr "Otključava uređaj za pristup firmveru" +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Demontira ESP" + #. TRANSLATORS: command line option msgid "Unset the debugging flag during update" msgstr "Ukloni oznaku otklanjanja grešaka tijekom nadopune" @@ -1318,6 +1689,10 @@ msgstr "Ukloni oznaku otklanjanja grešaka tijekom nadopune" msgid "Unsupported daemon version %s, client version is %s" msgstr "Nepodržana inačica pozadinskog programa %s, inačica klijenta je %s" +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Ispravno" + #. TRANSLATORS: Device is updatable in this or any other mode msgid "Updatable" msgstr "Nadopunjivo" @@ -1366,6 +1741,9 @@ msgstr "Nadopuni pohranjene metapodatke s trenutnim sadržajem" msgid "Updates all firmware to latest versions available" msgstr "Nadopuni sav firmver na najnovije dostupne inačice" +msgid "Updating" +msgstr "Nadopunjivanje" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" @@ -1384,10 +1762,6 @@ msgstr "Nadopunjujem %s…" msgid "Upgrade available for %s from %s to %s" msgstr "Nadopuna je dostupna za %s sa %s na %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Pošalji poruku:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Pošalji izvještaj samo ovaj puta, ali upitaj ponovno za buduće nadopune" @@ -1412,6 +1786,14 @@ msgstr "Slanje izvještaja firmvera pomaže proizvođačima hardvera brzo otkriv msgid "Urgency" msgstr "Hitnost" +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Koristite fwupdmgr --help za prikaz pomoći" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Koristite fwupdtool --help za prikaz pomoći" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "Koristi oznake okolnosti računala tijekom instalacije firmvera" @@ -1424,6 +1806,14 @@ msgstr "Korisnik je obaviješten" msgid "Username" msgstr "Korisničko ime" +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Valjano" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Provjera ESP sadržaja…" + #. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') msgid "Variant" msgstr "Varijanta" @@ -1436,9 +1826,13 @@ msgstr "Proizvođač" msgid "Verifying…" msgstr "Provjeravanje..." -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "UPOZORENJE: Zanemarivanje SSL ograničenja provjera, kako bi se ovo obavljalo ubuduće automatski postavite DISABLE_SSL_STRICT u vašem radnom okruženju" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Inačica" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "UPOZORENJE:" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1460,6 +1854,10 @@ msgstr "Zapiši firmver iz datoteke u uređaj" msgid "Write firmware from file into one partition" msgstr "Zapiši firmver iz datoteke u jednu particiju uređaja" +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Zapisivanje datoteke:" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Zapisivanje..." @@ -1468,19 +1866,14 @@ msgstr "Zapisivanje..." msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." msgstr "Vaš distributer možda nije provjerio nadopune firmvera za kompatibilnost s vašim sustavom ili priključenim uređajima." +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "standardno" + #. TRANSLATORS: program name msgid "fwupd TPM event log utility" msgstr "fwupd TPM pomagalo zapisa događaja" -#. TRANSLATORS: message letting the user know no device upgrade available due -#. to missing on LVFS -#. * %1 is the device name -#, c-format -msgid "• %s has no available firmware updates" -msgstr "• %s nema dostupnu nadopunu firmvera" - -#. TRANSLATORS: message letting the user know no device upgrade available -#. * %1 is the device name -#, c-format -msgid "• %s has the latest available firmware version" -msgstr "• %s ima najnoviju inačicu firmvera" +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd priključci" diff --git a/po/hu.po b/po/hu.po index 3fe3a811e..c11bdb354 100644 --- a/po/hu.po +++ b/po/hu.po @@ -3,7 +3,7 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Balázs Meskó , 2017-2019 +# Balázs Meskó , 2017-2019 # Balázs Úr, 2015-2018 # Gabor Kelemen , 2016 # kelemeng , 2016 @@ -446,6 +446,7 @@ msgstr[1] "Ne töltsön fel jelentéseket, és sose kérdezzen rá a jövőben" msgid "Do not write to the history database" msgstr "Ne írjon az előzmények adatbázisába" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Kész!" @@ -490,6 +491,8 @@ msgstr "Firmware frissítési támogatás engedélyezése a támogatott rendszer msgid "Enable this remote?" msgstr "Engedélyezi ezt a távoli tárolót?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Engedélyezve" @@ -528,10 +531,6 @@ msgstr "Kilépés a motor betöltődése után" msgid "Failed to connect to daemon" msgstr "A démonhoz kapcsolódás sikertelen" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "A letöltés kiszolgálókorlát miatt meghiúsult" - #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" msgstr "A függőben lévő eszközök lekérése sikertelen" @@ -560,22 +559,6 @@ msgstr "Újraindítás sikertelen" msgid "Failed to set splash mode" msgstr "Az indítóképernyő módjának beállítása sikertelen" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Fájl lekérése" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Firmware lekérése" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Metaadatok lekérése" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Aláírás lekérése" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "Fájlnév" @@ -621,6 +604,7 @@ msgstr "A firmware frissítések nem támogatottak ezen a gépen." msgid "Firmware updates are supported on this machine." msgstr "A firmware frissítések támogatottak ezen a gépen." +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Jelzők" @@ -628,6 +612,10 @@ msgstr "Jelzők" msgid "Force the action ignoring all warnings" msgstr "A művelet erőltetése, az összes figyelmeztetés mellőzése" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Megtalálva" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -658,10 +646,6 @@ msgstr "Részleteket kér le egy firmware fájlról" msgid "Gets the configured remotes" msgstr "Lekéri a beállított távoli tárolókat" -#. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Lekéri a jóváhagyott firmwarek listáját." - #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "A frissítések listáját kéri le a csatlakoztatott hardverhez" @@ -781,10 +765,6 @@ msgstr "Az elérhető firmware típusok listázása" msgid "Loading…" msgstr "Betöltés…" -#. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Egyes bővítmények kézi fehérlistára tétele" - #. TRANSLATORS: remote URI msgid "Metadata Signature" msgstr "Metaadatok aláírása" @@ -806,10 +786,6 @@ msgstr "Legkisebb verzió" msgid "Mismatched daemon and client, use %s instead" msgstr "Eltérő démon és kliens, inkább ezt használja: %s" -#. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Módosítja a démon egy konfigurációs értékét." - #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "A megadott távoli tároló módosítása" @@ -836,6 +812,7 @@ msgstr "A telepítés után kikapcsolást igényel" msgid "New version" msgstr "Új verzi" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Nincs művelet megadva!" @@ -867,9 +844,9 @@ msgstr "Nincsenek elérhető távoli tárolók" msgid "No updates were applied" msgstr "Nem lett frissítés alkalmazva" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Bővítmény figyelmeztetés felülbírálása" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -953,10 +930,6 @@ msgstr "Újraindítás…" msgid "Refresh metadata from remote server" msgstr "Metaadatok frissítése a távoli kiszolgálóról" -#. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "Újratelepíti a jelenlegi firmware-t az eszközön." - #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second is a version number #. * e.g. "1.2.3" @@ -985,10 +958,6 @@ msgstr "Jelentési URI" msgid "Reported to remote server" msgstr "Jelentve a távoli kiszolgálónak" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Hálózati áramforrás szükséges" - #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" msgstr "Rendszerbetöltő szükséges" @@ -1045,13 +1014,10 @@ msgstr "Sorozatszám" msgid "Set the debugging flag during update" msgstr "A hibakeresési jelző beállítása frissítéskor" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Beállítja a jóváhagyott firmwarek listáját" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Beállítja a jóváhagyott firmwarek listáját." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Firmware frissítése előzmények megosztása a fejlesztőkkel" @@ -1309,10 +1275,6 @@ msgstr "%s frissítése…" msgid "Upgrade available for %s from %s to %s" msgstr "%s frissítés érhető el, erről: %s, erre: %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Feltöltési üzenet:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Most töltse fel a jelentést, de kérdezzen rá a jövőben" @@ -1355,9 +1317,9 @@ msgstr "Gyártó" msgid "Verifying…" msgstr "Ellenőrzés…" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "FIGYELMEZTETÉS: A szigorú SSL ellenőrzések mellőzése, ahhoz hogy ezt automatikusan megtegye a jövőben, exportálja a DISABLE_SSL_STRICT változót a környezetében" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Verzió" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" diff --git a/po/id.po b/po/id.po index c2f439b08..e9973c97a 100644 --- a/po/id.po +++ b/po/id.po @@ -447,6 +447,7 @@ msgstr[0] "Jangan unggah laporan, dan jangan pernah meminta untuk mengunggah lap msgid "Do not write to the history database" msgstr "Jangan menulis ke basis data riwayat" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Selesai!" @@ -491,6 +492,8 @@ msgstr "Aktifkan dukungan pembaruan firmware pada sistem yang didukung" msgid "Enable this remote?" msgstr "Aktifkan remote ini?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Difungsikan" @@ -529,10 +532,6 @@ msgstr "Keluar setelah mesin telah dimuat" msgid "Failed to connect to daemon" msgstr "Gagal menyambung ke daemon" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Gagal mengunduh karena batas server" - #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" msgstr "Gagal mendapatkan perangkat yang tertunda" @@ -565,22 +564,6 @@ msgstr "Gagal reboot" msgid "Failed to set splash mode" msgstr "Gagal mengatur mode splash" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Sedang mengambil berkas" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Sedang mengambil firmware" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Sedang mengambil metadata" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Sedang mengambil tanda tangan" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "Nama Berkas" @@ -625,6 +608,7 @@ msgstr "Pembaruan firmware tidak didukung pada mesin ini." msgid "Firmware updates are supported on this machine." msgstr "Pembaruan firmware didukung pada mesin ini." +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Flag" @@ -632,6 +616,10 @@ msgstr "Flag" msgid "Force the action ignoring all warnings" msgstr "Paksa tindakan mengabaikan semua peringatan" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Ditemukan" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -661,10 +649,6 @@ msgstr "Dapatkan rincian tentang suatu berkas firmware" msgid "Gets the configured remotes" msgstr "Dapatkan remote-remote yang terkonfigurasi" -#. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Dapatkan daftar firmware yang disetujui." - #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Dapatkan daftar pemutakhiran bagi perangkat keras yang tersambung" @@ -783,10 +767,6 @@ msgstr "Daftar tipe firmware yang tersedia" msgid "Loading…" msgstr "Memuat…" -#. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Secara manual memasukkan daftar putih plugin tertentu" - #. TRANSLATORS: remote URI msgid "Metadata Signature" msgstr "Tanda Tangan Metadata" @@ -808,10 +788,6 @@ msgstr "Versi Minimum" msgid "Mismatched daemon and client, use %s instead" msgstr "Daemon dan klien tidak cocok, gunakan %s sebagai gantinya" -#. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Mengubah suatu nilai konfigurasi daemon." - #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Mengubah suatu remote yang diberikan" @@ -838,6 +814,7 @@ msgstr "Perlu dimatikan setelah instalasi" msgid "New version" msgstr "Versi baru" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Tidak ada tindakan yang ditentukan!" @@ -875,14 +852,14 @@ msgstr "Tidak ada remote yang tersedia" msgid "No updates were applied" msgstr "Tidak ada pembaruan yang diterapkan" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Hanya tampilkan nilai PCR tunggal" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Timpa peringatan plugin" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Timpa path ESP default" @@ -965,10 +942,6 @@ msgstr "Reboot…" msgid "Refresh metadata from remote server" msgstr "Segarkan metadata dari server remote" -#. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "Menginstal ulang firmware saat ini di perangkat." - #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second is a version number #. * e.g. "1.2.3" @@ -997,10 +970,6 @@ msgstr "URI Lapor" msgid "Reported to remote server" msgstr "Dilaporkan ke server remote" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Membutuhkan daya AC" - #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" msgstr "Membutuhkan bootloader" @@ -1060,13 +1029,10 @@ msgstr "Nomor Seri" msgid "Set the debugging flag during update" msgstr "Atur flag debugging selama pembaruan" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Setel daftar firmware yang disetujui" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Tata daftar firmware yang disetujui." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Membagikan riwayat firmware dengan para pengembang" @@ -1323,10 +1289,6 @@ msgstr "Memutakhirkan %s…" msgid "Upgrade available for %s from %s to %s" msgstr "Pembaruan tersedia untuk %s dari %s ke %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Pesan unggah:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Unggah laporan hanya sekali ini saja, tetapi minta lagi untuk pembaruan di masa mendatang" @@ -1367,9 +1329,9 @@ msgstr "Vendor" msgid "Verifying…" msgstr "Verifikasi…" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "PERINGATAN: Mengabaikan pemeriksaan ketat SSL, untuk melakukan ini secara otomatis di masa mendatang ekspor DISABLE_SSL_STRICT dalam lingkungan Anda" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versi" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1402,16 +1364,3 @@ msgstr "Distributor Anda mungkin belum memverifikasi pembaruan firmware untuk ko #. TRANSLATORS: program name msgid "fwupd TPM event log utility" msgstr "Utilitas log kejadian TPM fwupd" - -#. TRANSLATORS: message letting the user know no device upgrade available due -#. to missing on LVFS -#. * %1 is the device name -#, c-format -msgid "• %s has no available firmware updates" -msgstr "• %s tidak memiliki pembaruan firmware yang tersedia" - -#. TRANSLATORS: message letting the user know no device upgrade available -#. * %1 is the device name -#, c-format -msgid "• %s has the latest available firmware version" -msgstr "• %s memiliki versi firmware terbaru yang tersedia" diff --git a/po/it.po b/po/it.po index 3b000ebfd..aa0e495bd 100644 --- a/po/it.po +++ b/po/it.po @@ -29,6 +29,12 @@ msgstr[1] "Mancano %.0f minuti" msgid "%s CPU Microcode Update" msgstr "Aggiornamento microcode CPU %s" +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Aggiornamento configurazione %s" + #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format @@ -92,6 +98,11 @@ msgstr "Aggiornamento di %s" msgid "%s and all connected devices may not be usable while updating." msgstr "%s e tutti i dispositivi collegati potrebbero non essere utilizzabili durante l'aggiornamento." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Modalità costruttore %s" + #. TRANSLATORS: warn the user before updating, %1 is a device name #, c-format msgid "%s must remain connected for the duration of the update to avoid damage." @@ -102,6 +113,21 @@ msgstr "%s deve rimanere collegato durante l'aggiornamento per evitare possibili msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." msgstr "%s deve rimanere collegato alla rete elettrica durante l'aggiornamento per evitare possibili danni." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "Override %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Versione %s" + #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -143,6 +169,10 @@ msgid_plural "%u seconds" msgstr[0] "%u secondo" msgstr[1] "%u secondi" +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(obsoleto)" + #. TRANSLATORS: command description msgid "Activate devices" msgstr "Attiva dispositivi" @@ -187,6 +217,10 @@ msgstr "Consente di tornare alle precedenti versioni del firmware" msgid "Allow reinstalling existing firmware versions" msgstr "Consente la re-installazione di versioni esistenti del firmware" +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Consente il cambio del ramo firmware" + #. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Per essere completato, un aggiornamento richiede un riavvio." @@ -203,6 +237,18 @@ msgstr "Risponde affermativamente a tutte le domande" msgid "Apply firmware updates" msgstr "Applica aggiornamenti firmware" +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Applica aggiornamento anche se non consigliato" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Applica file di aggiornamento" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Applicazione aggiornamento…" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "Approved firmware:" @@ -210,6 +256,10 @@ msgid_plural "Approved firmware:" msgstr[0] "Firmware approvato:" msgstr[1] "Firmware approvati:" +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Chiedere ancora la prossima volta?" + #. TRANSLATORS: command description msgid "Attach to firmware mode" msgstr "Collega in modalità firmware" @@ -266,10 +316,38 @@ msgstr "È richiesto autenticarsi per aggiornare il codice di controllo del disp msgid "Automatic Reporting" msgstr "Rapporti automatici" +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Caricare automaticamente ogni volta?" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Vincola in nuovo driver kernel" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "File di firmware bloccati:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Firmware bloccato:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blocca un firmware specifico così da non installarlo" + #. TRANSLATORS: firmware version of bootloader msgid "Bootloader Version" msgstr "Versione bootloader" +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Ramo" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Compila una file firmware" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Compila il firmware utilizzando una sandbox" @@ -282,6 +360,14 @@ msgstr "Annulla" msgid "Cancelled" msgstr "Annullato" +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Impossibile applicare poiché l'aggiornamento dbx è già stato applicato." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Impossibile applicare gli aggiornamenti su supporti live" + #. TRANSLATORS: this is when a device is hotplugged #. TRANSLATORS: this is when the daemon state changes msgid "Changed" @@ -295,6 +381,11 @@ msgstr "Verifica che l'hash crittografico corrisponda col firmware" msgid "Checksum" msgstr "Codice di controllo" +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Scegliere un ramo:" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Scegliere un dispositivo:" @@ -307,6 +398,10 @@ msgstr "Scegliere un tipo di firmware:" msgid "Choose a release:" msgstr "Scegliere un rilascio:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Scegliere un volume:" + #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" msgstr "Annulla gli aggiornamenti pianificati per essere eseguiti offline" @@ -411,10 +506,18 @@ msgstr "Dispositivo rimosso:" msgid "Device stages updates" msgstr "Il dispositivo applica gli aggiornamenti a fasi" +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Il dispositivo supporta il passaggio a un ramo diverso del firmware" + #. TRANSLATORS: Device update needs to be separately activated msgid "Device update needs activation" msgstr "L'aggiornamento del dispositivo richiede l'attivazione" +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Il dispositivo eseguirà un backup del firmware prima dell'installazione" + #. TRANSLATORS: Device will not return after update completes msgid "Device will not re-appear after update completes" msgstr "Il dispositivo non comparirà nuovamente dopo l'aggiornamento" @@ -427,6 +530,20 @@ msgstr "Dispositivi aggiornati con successo:" msgid "Devices that were not updated correctly:" msgstr "Dispositivi non aggiornati correttamente:" +#. TRANSLATORS: message letting the user know no device upgrade available due +#. to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Dispositivi senza aggiornamenti firmware:" + +#. TRANSLATORS: message letting the user know no device upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "Dispositivi con l'ultima versione disponibile del firmware:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Disabilitato" + msgid "Disabled fwupdate debugging" msgstr "Debug fwupdate disabilitato" @@ -476,6 +593,11 @@ msgstr[1] "Non caricare i rapporti e non chiedere nuovamente con i prossimi aggi msgid "Do not write to the history database" msgstr "Non scrive la cronologia" +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Le conseguenze del cambio di ramo del firmware sono state comprese?" + +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Fatto." @@ -520,6 +642,8 @@ msgstr "Abilita il supporto all'aggiornamento firmware sui sistemi compatibili" msgid "Enable this remote?" msgstr "Abilitare questo remoto?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Abilitato" @@ -538,6 +662,14 @@ msgstr "Abilitare questa funzionalità a proprio rischio: in caso di problemi co msgid "Enabling this remote is done at your own risk." msgstr "Abilitare questo remoto a proprio rischio." +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Cifrato" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM cifrata" + #. TRANSLATORS: command description msgid "Erase all firmware update history" msgstr "Elimina tutta la cronologia degli aggiornamenti firmware" @@ -554,13 +686,25 @@ msgstr "Esce dopo una breve attesa" msgid "Exit after the engine has loaded" msgstr "Esce dopo che il motore è stato caricato" +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Estrae un blob firmware in immagini" + +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Non riuscito" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Applicazione aggiornamento non riuscita" + #. TRANSLATORS: we could not talk to the fwupd daemon msgid "Failed to connect to daemon" msgstr "Connessione al demone non riuscita" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Scaricamento non riuscito a causa di un limite sul server" +#. TRANSLATORS: could not parse file +msgid "Failed to extract local dbx " +msgstr "Estrazione dbx locale non riuscita" #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" @@ -570,10 +714,19 @@ msgstr "Recupero dei dispositivi in attesa non riuscito" msgid "Failed to install firmware update" msgstr "Installazione aggiornamento firmware non riuscita" +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Caricamento dbx locale non riuscito" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Caricamento stranezze non riuscito" +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Caricamento dbx di sistema non riuscito" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Analisi degli argomenti non riuscita" @@ -586,6 +739,10 @@ msgstr "Lettura del file non riuscita" msgid "Failed to parse flags for --filter" msgstr "Analisi del flag --filter non riuscita" +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Lettura dbx locale non riuscita" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Riavvio non riuscito" @@ -594,21 +751,10 @@ msgstr "Riavvio non riuscito" msgid "Failed to set splash mode" msgstr "Impostazione della modalità splash non riuscita" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Recupero file" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Recupero firmware" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Recupero metadati" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Recupero firma" +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Verifica contenuti ESP non riuscita" #. TRANSLATORS: filename of the local file msgid "Filename" @@ -618,6 +764,10 @@ msgstr "Nome file" msgid "Filename Signature" msgstr "Firma nome file" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Nome file richiesto" + #. TRANSLATORS: command line option msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" msgstr "Filtra tramite un insieme di flag utilizzando ~ per escludere, per esempio «internal, ~needs-reboot»" @@ -642,6 +792,22 @@ msgstr "Demone di aggiornamento firmware" msgid "Firmware Utility" msgstr "Strumento gestione firmware" +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Convalida firmware" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "Firmware can not be updated in legacy BIOS mode" +msgstr "Impossibile aggiornare il firmware in modalità BIOS legacy" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Il firmware è già bloccato" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Il firmware non è bloccato" + #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format msgid "Firmware metadata has not been updated for %u day and may not be up to date." @@ -649,19 +815,36 @@ msgid_plural "Firmware metadata has not been updated for %u days and may not be msgstr[0] "I metadati del firmware non sono stati controllati per %u giorno e potrebbero non essere aggiornati." msgstr[1] "I metadati del firmware non sono stati controllati per %u giorni e potrebbero non essere aggiornati." +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Aggiornamenti firmware" + msgid "Firmware updates are not supported on this machine." msgstr "Gli aggiornamenti firmware non sono supportati su questo dispositivo." msgid "Firmware updates are supported on this machine." msgstr "Gli aggiornamenti firmware sono supportati su questo dispositivo." +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Aggiornamenti firmware disabilitati; eseguire «fwupdmgr unlock» per abilitarli" + +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Flag" +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Forza l'azione riducendo alcuni controlli runtime" + msgid "Force the action ignoring all warnings" msgstr "Forza l'azione ignorando gli avvisi" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Trovato" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -692,9 +875,17 @@ msgstr "Ottiene le informazioni su un file di firmware" msgid "Gets the configured remotes" msgstr "Ottiene i remoti configurati" +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Recupera gli attributi di sicurezza dell'host" + #. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Recupera l'elenco dei firmware approvati." +msgid "Gets the list of approved firmware" +msgstr "Recupera l'elenco dei firmware approvati" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Recupera l'elenco del firmware bloccato" #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -716,6 +907,15 @@ msgstr "L'hardware è in attesa di essere ricollegato" msgid "High" msgstr "Elevata" +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ID sicurezza host:" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Inattivo…" @@ -724,10 +924,26 @@ msgstr "Inattivo…" msgid "Ignore SSL strict checks when downloading files" msgstr "Ignora controlli SSL nello scaricare i file" +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignora i checksum del firmware non riusciti" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignora corrispondenze errate firmware hardware" + +#. TRANSLATORS: command line option +msgid "Ignore requirement of external power source" +msgstr "Ignora richiesta di sorgente elettrica esterna" + #. TRANSLATORS: Ignore validation safety checks when flashing this device msgid "Ignore validation safety checks" msgstr "Ignora i controlli di sicurezza di validazione" +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Controlli SSL ignorati, per fare ciò automaticamente in futuro, esportare DISABLE_SSL_STRICT nel proprio ambiente" + #. TRANSLATORS: length of time the update takes to apply msgid "Install Duration" msgstr "Tempo di installazione" @@ -772,10 +988,57 @@ msgstr "Installazione aggiornamento firmware…" msgid "Installing on %s…" msgstr "Installazione su %s…" +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "ACM protetto da Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Fusibile OTP Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Regole di errore Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Avvio verificato da Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET attivo" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET abilitato" + +#. TRANSLATORS: Title: Direct Connect Interface (DCI) allows +#. * debugging of Intel processors using the USB3 port +msgid "Intel DCI debugger" +msgstr "Debugger Intel DCI" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + #. TRANSLATORS: Device cannot be removed easily msgid "Internal device" msgstr "Dispositivo interno" +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Non valido" + #. TRANSLATORS: Is currently in bootloader mode msgid "Is in bootloader mode" msgstr "È in modalità bootloader" @@ -807,6 +1070,22 @@ msgstr "Linux Vendor Firmware Service (firmware stabile)" msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Linux Vendor Firmware Service (firmware in prova)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Kernel Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Lockdown kernel Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Swap Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Elenca le voci nel dbx" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "Elenca gli aggiornamenti firmware supportati" @@ -815,17 +1094,39 @@ msgstr "Elenca gli aggiornamenti firmware supportati" msgid "List the available firmware types" msgstr "Elenca tutti i tipi di firmware disponibili" +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Elenca i file nell'ESP" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Caricamento…" +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Bloccato" + #. TRANSLATORS: the release urgency msgid "Low" msgstr "Bassa" +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Modalità costruttore MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Override MEI" + +msgid "MEI version" +msgstr "Versione MEI" + #. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Abilita manualmente plugin specifici" +msgid "Manually enable specific plugins" +msgstr "Abilita manualmente i plugin selezionati" #. TRANSLATORS: the release urgency msgid "Medium" @@ -853,7 +1154,7 @@ msgid "Mismatched daemon and client, use %s instead" msgstr "Versioni di demone e client non corrispondenti, usare %s" #. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." +msgid "Modifies a daemon configuration value" msgstr "Modifica il valore della configurazione del demone" #. TRANSLATORS: command description @@ -870,6 +1171,10 @@ msgstr "Modifica la configurazione del demone" msgid "Monitor the daemon for events" msgstr "Controlla il demone per gli eventi" +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Monta l'ESP" + #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware msgid "Needs a reboot after installation" msgstr "Necessita un riavvio dopo l'installazione" @@ -882,6 +1187,7 @@ msgstr "Necessita l'arresto dopo l'installazione" msgid "New version" msgstr "Nuova versione" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Nessuna azione specificata." @@ -919,14 +1225,22 @@ msgstr "Nessun server disponibile" msgid "No updates were applied" msgstr "Non è stato applicato alcun aggiornamento" +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Non trovato" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Non supportato" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Fatto" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Mostra solo il valore PCR" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Scavalca l'avviso sul plugin" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Sovrascrive il percorso ESP predefinito" @@ -939,6 +1253,14 @@ msgstr "Scavalca gli avvisi e forza l'azione" msgid "Parse and show details about a firmware file" msgstr "Legge e mostra i dettagli riguardo a un file firmware" +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Lettura aggiornamento dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Lettura dbx di sistema…" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Password" @@ -955,6 +1277,14 @@ msgstr "Percentuale completamento" msgid "Please enter a number from 0 to %u: " msgstr "Inserire un numero tra 0 e %u:" +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Dipendenze plugin mancanti" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Protezione DMA pre-boot" + #. TRANSLATORS: version number of previous firmware msgid "Previous version" msgstr "Versione precedente" @@ -1010,7 +1340,7 @@ msgid "Refresh metadata from remote server" msgstr "Ricarica i metadati dal server remoto" #. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." +msgid "Reinstall current firmware on the device" msgstr "Installa nuovamente il firmware attuale sul dispositivo" #. TRANSLATORS: command description @@ -1045,9 +1375,13 @@ msgstr "URI del rapporto" msgid "Reported to remote server" msgstr "Segnalato al server remoto" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Richiede corrente elettrica" +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Il file system richiesto efivarfs non è stato trovato" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "L'hardware richiesto non è stato trovato" #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" @@ -1084,6 +1418,22 @@ msgstr "Esegue la procedura di pulizia del plugin quando si utilizza install-blo msgid "Run the plugin composite prepare routine when using install-blob" msgstr "Esegue la procedura di preparazione del plugin quando si utilizza install-blob" +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Suffisso di runtime" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Regione BIOS SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Blocco SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Scrittura SPI" + #. TRANSLATORS: command line option msgid "Save device state into a JSON file between executions" msgstr "Salva lo stato del dispositivo tra le esecuzioni su un file JSON" @@ -1100,6 +1450,10 @@ msgstr "Pianificazione…" msgid "Selected device" msgstr "Dispositivo selezionato" +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volume selezionato" + #. TRANSLATORS: serial number of hardware msgid "Serial Number" msgstr "Numero di serie" @@ -1108,17 +1462,18 @@ msgstr "Numero di serie" msgid "Set the debugging flag during update" msgstr "Imposta il flag di debug durante l'aggiornamento" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Imposta l'elenco dei firmware approvati" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Imposta l'elenco dei firmware approvati." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Condivide la cronologia del firmware con gli sviluppatori" +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Mostra tutti i risultati" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "Mostra la versione del client e del demone" @@ -1151,6 +1506,10 @@ msgstr "Mostra la cronologia degli aggiornamenti firmware" msgid "Show plugin verbose information" msgstr "Mostra informazioni dettagliate del plugin" +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Mostra la versione calcolata del dbx" + #. TRANSLATORS: command line option msgid "Show the debug log from the last attempted update" msgstr "Mostra debug dell'ultimo tentativo di aggiornamento" @@ -1188,6 +1547,10 @@ msgstr "Sorgente" msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Specifica vendor/ID prodotto di un dispositivo DFU" +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Indica il file del database dbx" + msgid "Specify the number of bytes per USB transfer" msgstr "Specifica il numero di byte per trasferimento USB" @@ -1245,10 +1608,42 @@ msgstr "Codici di controllo del dispositivo verificati con successo" msgid "Summary" msgstr "Riepilogo" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Supportato" + #. TRANSLATORS: Is found in current metadata msgid "Supported on remote server" msgstr "Supportato sul server remoto" +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspend-to-idle" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspend-to-ram" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Cambia il ramo del firmware sul dispositivo" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Il sistema richiede una sorgente elettrica esterna" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Ricostruzione PCR0 TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Non integro" + msgid "Target" msgstr "Obiettivo" @@ -1256,6 +1651,23 @@ msgstr "Obiettivo" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "LVFS è un servizio gratuito che opera come entità legale indipendente e non ha alcun legame con $OS_RELEASE:NAME$. Il distributore potrebbe non aver verificato la compatibilità degli aggiornamenti firmware col proprio sistema o con i propri dispositivi collegati. Il firmware viene fornito solamente dall'OEM." +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 è diverso dalla ricostruzione." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Il demone ha caricato codice di terze parti e non è più supportato dagli sviluppatori." + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Il firmware su %s non è fornito da %s, il fornitore dell'hardware." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Non ci sono file di firmware bloccati" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "There is no approved firmware." @@ -1268,6 +1680,18 @@ msgstr "Questo programma può funzionare correttamente solo come utente root" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "Questo remoto contiene firmware che non è bloccato, ma è ancora in fase di verifica dal produttore hardware. Assicurarsi di poter ripristinare, manualmente o con altre procedure, il vecchio firmware nel caso in cui l'aggiornamento non riuscisse." +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Questo sistema presenta dei problemi di runtime HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Questo sistema ha un livello di sicurezza HSI basso." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Questo strumento consente a un amministratore di applicare aggiornamenti UEFI dbx." + #. TRANSLATORS: the user needs to stop playing with stuff msgid "This tool can only be used by the root user" msgstr "Questo strumento può essere usato solamente dall'utente root" @@ -1276,10 +1700,46 @@ msgstr "Questo strumento può essere usato solamente dall'utente root" msgid "Type" msgstr "Tipo" +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Partizione ESP UEFI non rilavata o non configurata" + #. TRANSLATORS: program name msgid "UEFI Firmware Utility" msgstr "Strumento firmware UEFI" +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled" +msgstr "Aggiornamenti capsula UEFI non disponibili o non abilitati" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Strumento EUFI dbx" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Avvio sicuro UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Impossibile collegarsi al servizio" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Svincola il driver attuale" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Sblocco del firmware:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Sblocca un firmware specifico dal non essere installato" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Non cifrato" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -1293,10 +1753,18 @@ msgstr "Dispositivo sconosciuto" msgid "Unlock the device to allow access" msgstr "Sblocco del dispositivo per consentire l'accesso" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Sbloccato" + #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" msgstr "Sblocca il dispositivo per accedere al firmware" +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Smonta l'ESP" + #. TRANSLATORS: command line option msgid "Unset the debugging flag during update" msgstr "Ripristina il flag di debug durante l'aggiornamento" @@ -1306,6 +1774,10 @@ msgstr "Ripristina il flag di debug durante l'aggiornamento" msgid "Unsupported daemon version %s, client version is %s" msgstr "Demone versione %s non supportato, la versione del client è %s" +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Integro" + #. TRANSLATORS: Device is updatable in this or any other mode msgid "Updatable" msgstr "Aggiornabile" @@ -1354,6 +1826,9 @@ msgstr "Aggiorna i metadati salvati con il contenuto attuale" msgid "Updates all firmware to latest versions available" msgstr "Aggiorna tutti i firmware all'ultima versione disponibile" +msgid "Updating" +msgstr "Aggiornamento in corso" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" @@ -1372,10 +1847,6 @@ msgstr "Aggiornamento di %s…" msgid "Upgrade available for %s from %s to %s" msgstr "Aggiornamento disponibile per %s da %s a %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Messaggio di caricamento:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Caricare il rapporto ora e chiedere nuovamente con i prossimi aggiornamenti" @@ -1398,6 +1869,14 @@ msgstr "Inviare resoconti sul firmware aiuta gli sviluppatori a identificare vel msgid "Urgency" msgstr "Urgenza" +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Usare «fwupdmgr --help» per maggiori informazioni" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Usare «fwupdtool --help» per maggiori informazioni" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "Usa flag quirk nell'installare il firmware" @@ -1410,6 +1889,14 @@ msgstr "Notifica inviata all'utente" msgid "Username" msgstr "Nome utente" +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Valido" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Verifica contenuti ESP…" + #. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') msgid "Variant" msgstr "Variante" @@ -1422,9 +1909,13 @@ msgstr "Fornitore" msgid "Verifying…" msgstr "Verifica…" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "Attenzione: controlli SSL ignorati, per fare ciò automaticamente in futuro, esportare DISABLE_SSL_STRICT nel proprio ambiente" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versione" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "Attenzione:" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1446,6 +1937,10 @@ msgstr "Scrive il firmware dal file nel dispositivo" msgid "Write firmware from file into one partition" msgstr "Scrive il firmware dal file in una partizione" +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Scrittura file:" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Scrittura…" @@ -1454,19 +1949,19 @@ msgstr "Scrittura…" msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." msgstr "La propria distribuzione potrebbe non aver verificato la compatibilità degli aggiornamenti firmware col proprio sistema o col dispositivo collegato." +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Utilizzando questo firmware, l'hardware potrebbe danneggiarsi; inoltre, l'installazione di questa versione potrebbe annullare la garanzia con %s." + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "predefinito" + #. TRANSLATORS: program name msgid "fwupd TPM event log utility" msgstr "Strumento registro eventi TPM fwupd" -#. TRANSLATORS: message letting the user know no device upgrade available due -#. to missing on LVFS -#. * %1 is the device name -#, c-format -msgid "• %s has no available firmware updates" -msgstr "• %s non ha alcun aggiornamento firmware disponibile" - -#. TRANSLATORS: message letting the user know no device upgrade available -#. * %1 is the device name -#, c-format -msgid "• %s has the latest available firmware version" -msgstr "• %s ha la versione più recente del firmware" +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Plugin fwupd" diff --git a/po/ko.po b/po/ko.po index 00bcf5199..898a2e09b 100644 --- a/po/ko.po +++ b/po/ko.po @@ -22,6 +22,12 @@ msgid "%.0f minute remaining" msgid_plural "%.0f minutes remaining" msgstr[0] "%.0f분 남음" +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s CPU 마이크로코드 업데이트" + #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format @@ -309,6 +315,18 @@ msgstr "명령을 찾을 수 없습니다" msgid "Continue with update?" msgstr "업데이트를 계속 진행하시겠습니까?" +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "펌웨어 파일을 변환합니다" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "생성됨" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "매우 높음" + #. TRANSLATORS: Device supports some form of checksum verification msgid "Cryptographic hash verification is available" msgstr "암호화 해시 검사 사용 가능" @@ -448,6 +466,7 @@ msgstr[0] "보고서를 업로드하지 않고 다음에 업데이트할 때에 msgid "Do not write to the history database" msgstr "과거 기록 데이터베이스에 기록하지 않기" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "완료!" @@ -492,6 +511,8 @@ msgstr "지원하는 시스템의 펌웨어 업데이트 지원 활성화" msgid "Enable this remote?" msgstr "이 원격 설정을 활성화하시겠습니까?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "활성화 여부" @@ -530,10 +551,6 @@ msgstr "엔진을 불러온 후 나가기" msgid "Failed to connect to daemon" msgstr "데몬에 연결할 수 없음" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "서버 제한으로 파일을 다운로드할 수 없음" - #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" msgstr "대기 중인 장치를 가져올 수 없음" @@ -566,22 +583,6 @@ msgstr "다시 시작할 수 없음" msgid "Failed to set splash mode" msgstr "스플래시 모드를 설정할 수 없음" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "파일 가져오는 중" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "펌웨어 가져오는 중" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "메타데이터 가져오는 중" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "서명 가져오는 중" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "파일 이름" @@ -626,6 +627,7 @@ msgstr "이 머신에서 펌웨어 업데이트를 지원하지 않습니다." msgid "Firmware updates are supported on this machine." msgstr "이 머신에서 펌웨어 업데이트를 지원합니다." +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "플래그" @@ -633,6 +635,10 @@ msgstr "플래그" msgid "Force the action ignoring all warnings" msgstr "모든 경고를 무시하고 작업 강제 진행" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "감지 장치" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -662,10 +668,6 @@ msgstr "펌웨어 파일 세부 정보를 가져옵니다" msgid "Gets the configured remotes" msgstr "원격 설정 정보를 가져옵니다" -#. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "허용된 펌웨어 목록을 가져옵니다." - #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "연결한 하드웨어의 업데이트 목록을 가져옵니다" @@ -682,6 +684,10 @@ msgstr "최근 업데이트 결과를 가져옵니다" msgid "Hardware is waiting to be replugged" msgstr "하드웨어를 다시 연결해야 함" +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "높음" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "대기 중…" @@ -784,9 +790,13 @@ msgstr "사용 가능한 펌웨어 종류를 표시합니다" msgid "Loading…" msgstr "불러오는 중…" -#. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "지정한 플러그인을 수동으로 허용 목록에 추가" +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "낮음" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "중간" #. TRANSLATORS: remote URI msgid "Metadata Signature" @@ -809,10 +819,6 @@ msgstr "최소 버전" msgid "Mismatched daemon and client, use %s instead" msgstr "데몬과 클라이언트가 일치하지 않음, %s을(를) 대신 사용함" -#. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "데몬 설정값을 변경합니다." - #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "지정한 원격 정보를 수정합니다" @@ -839,6 +845,7 @@ msgstr "설치 후 종료 필요함" msgid "New version" msgstr "새 버전" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "동작을 지정하지 않았습니다!" @@ -876,14 +883,14 @@ msgstr "원격 저장소 없음" msgid "No updates were applied" msgstr "적용된 업데이트 없음" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "확인" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "단일 PCR 값만 표시" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "플러그인 경고 무시" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "기본 ESP 경로 재정의" @@ -967,8 +974,8 @@ msgid "Refresh metadata from remote server" msgstr "원격 서버의 메타데이터를 새로 고칩니다" #. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "장치에 현재 펌웨어를 다시 설치합니다." +msgid "Reinstall firmware on a device" +msgstr "장치에 펌웨어를 다시 설치합니다" #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second is a version number @@ -998,10 +1005,6 @@ msgstr "보고서 URI" msgid "Reported to remote server" msgstr "원격 서버에 보고됨" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "AC 전원 필요함" - #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" msgstr "부트로더 모드 진입 필요함" @@ -1061,13 +1064,10 @@ msgstr "일련 번호" msgid "Set the debugging flag during update" msgstr "업데이트 중 디버깅 플래그 설정" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "허용된 펌웨어 목록 설정" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "허용된 펌웨어 목록을 설정합니다." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "개발사와 펌웨어 업데이트 기록 공유하기" @@ -1324,10 +1324,6 @@ msgstr "%s 업데이트 중..." msgid "Upgrade available for %s from %s to %s" msgstr "%s 업그레이드 사용 가능: %s에서 %s(으)로" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "업로드 메시지:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "이번 한 번만 보고서를 업로드하고 다음에 업데이트할 때 묻기" @@ -1344,6 +1340,10 @@ msgstr[0] "이번 한 번 보고서를 업로드하고 다음에 업데이트할 msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "펌웨어 보고서를 업로드하면 하드웨어 제작사에서 실제 장치에 업데이트를 적용했을 때 성공 및 실패 여부를 빠르게 알 수 있습니다." +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "중요도" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "펌웨어를 설치할 때 quirk 플래그 사용" @@ -1368,9 +1368,9 @@ msgstr "제조사" msgid "Verifying…" msgstr "검증 중…" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "경고: SSL 엄격 검사를 건너뜁니다. 이 옵션을 자동으로 지정하려면 DISABLE_SSL_STRICT 환경 변수를 지정하십시오" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "버전" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1399,3 +1399,7 @@ msgstr "쓰는 중…" #. TRANSLATORS: show the user a warning msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." msgstr "배포판 개발사에서 펌웨어 업데이트와 시스템 및 연결된 장치간의 호환성을 검증한다는 보장이 없습니다." + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM 이벤트 로그 유틸리티" diff --git a/po/lt.po b/po/lt.po index aeb628607..6fabbd50f 100644 --- a/po/lt.po +++ b/po/lt.po @@ -4,6 +4,7 @@ # # Translators: # Moo, 2019 +# Moo, 2020 msgid "" msgstr "" "Project-Id-Version: fwupd\n" @@ -270,6 +271,7 @@ msgstr "Netikrinti ar yra istorijos apie kurią nepranešta" msgid "Do not write to the history database" msgstr "Nerašyti į istorijos duomenų bazę" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Atlikta!" @@ -310,6 +312,8 @@ msgstr "Įjungti programinės aparatinės įrangos atnaujinimo palaikymą palaik msgid "Enable this remote?" msgstr "Įjungti šią nuotolinę saugyklą?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Įjungta" @@ -345,10 +349,6 @@ msgstr "Išeiti, įkėlus modulį" msgid "Failed to connect to daemon" msgstr "Nepavyko prisijungti prie tarnybos" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Nepavyko atsisiųsti dėl serverio apribojimo" - #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" msgstr "Nepavyko gauti laukiančių įrenginių" @@ -373,22 +373,6 @@ msgstr "Nepavyko paleisti iš naujo" msgid "Failed to set splash mode" msgstr "Nepavyko nustatyti prisistatymo lango veikseną" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Gaunamas failas" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Gaunama programinė aparatinė įranga" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Gaunami metaduomenys" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Gaunamas parašas" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "Failo pavadinimas" @@ -423,6 +407,7 @@ msgstr "Šiame kompiuteryje programinės aparatinės įrangos atnaujinimai yra n msgid "Firmware updates are supported on this machine." msgstr "Šiame kompiuteryje yra prieinami programinės aparatinės įrangos atnaujinimai." +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Vėliavėlės" @@ -430,6 +415,10 @@ msgstr "Vėliavėlės" msgid "Force the action ignoring all warnings" msgstr "Priverstinai atlikti veiksmą nepaisant visų įspėjimų" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Rastas" + #. TRANSLATORS: command description msgid "Get all devices and possible releases" msgstr "Gauti visus įrenginius ir galimas laidas" @@ -450,10 +439,6 @@ msgstr "Gauna išsamesnę informaciją apie programinės aparatinės įrangos fa msgid "Gets the configured remotes" msgstr "Gauna sukonfigūruotas nuotolines saugyklas" -#. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Gauna patvirtintos programinės aparatinės įrangos sąrašą." - #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Gauna atnaujinimų sąrašą prijungtai aparatinei įrangai" @@ -527,10 +512,6 @@ msgstr "Išvardyti prieinamus programinės aparatinės įrangos atnaujinimus" msgid "Loading…" msgstr "Įkeliama…" -#. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Rankiniu būdu įtraukti tam tikrus įskiepius į baltąjį sąrašą" - #. TRANSLATORS: remote URI msgid "Metadata URI" msgstr "Metaduomenų URI" @@ -546,10 +527,14 @@ msgstr "Modifikuoja nurodytą nuotolinę saugyklą" msgid "Modify a configured remote" msgstr "Modifikuoti sukonfigūruotą nuotolinę saugyklą" +msgid "Modify daemon configuration" +msgstr "Modifikuoti tarnybos konfigūraciją" + #. TRANSLATORS: command description msgid "Monitor the daemon for events" msgstr "Stebėti tarnybą, ar yra įvykių" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Nenurodytas joks veiksmas!" @@ -569,9 +554,9 @@ msgstr "Šiuo metu nėra įjungtos jokios nuotolinės saugyklos, taigi, nėra pr msgid "No updates were applied" msgstr "Nebuvo pritaikyti jokie atnaujinimai" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Nustelbti įskiepio įspėjimą" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Gerai" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -694,13 +679,10 @@ msgstr "Suplanuojama…" msgid "Set the debugging flag during update" msgstr "Atnaujinimo metu nustatyti derinimo vėliavėlę" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Nustato patvirtintą programinės aparatinės įrangos sąrašą" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Nustato patvirtintos programinės aparatinės įrangos sąrašą." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Bendrinti programinės aparatinės įrangos istoriją su plėtotojais" @@ -841,10 +823,6 @@ msgstr "Atnaujinama %s iš %s į %s... " msgid "Updating %s…" msgstr "Atnaujinama %s…" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Išsiuntimo žinutė:" - #. TRANSLATORS: ask the user to upload msgid "Upload report now?" msgstr "Išsiųsti ataskaitą dabar?" @@ -861,6 +839,10 @@ msgstr "Naudotojo vardas" msgid "Verifying…" msgstr "Patikrinama…" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versija" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Laukiama…" diff --git a/po/make-images b/po/make-images index 8394e16eb..b1097bc25 100755 --- a/po/make-images +++ b/po/make-images @@ -66,8 +66,8 @@ class Rasterizer: font_desc = "Sans %fpx" % (height / 32,) fd = Pango.FontDescription(font_desc) - fo = cairo.FontOptions() - fo.set_antialias(cairo.ANTIALIAS_SUBPIXEL) + font_option = cairo.FontOptions() + font_option.set_antialias(cairo.ANTIALIAS_SUBPIXEL) l = Pango.Language.from_string(language) img = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1) @@ -77,7 +77,7 @@ class Rasterizer: pctx.set_font_description(fd) pctx.set_language(l) fs = pctx.load_fontset(fd, l) - PangoCairo.context_set_font_options(pctx, fo) + PangoCairo.context_set_font_options(pctx, font_option) attrs = Pango.AttrList() length = len(bytes(string, "utf8")) @@ -112,7 +112,7 @@ class Rasterizer: cctx = cairo.Context(img) layout = PangoCairo.create_layout(cctx) pctx = layout.get_context() - PangoCairo.context_set_font_options(pctx, fo) + PangoCairo.context_set_font_options(pctx, font_option) cctx.set_source_rgb(1, 1, 1) cctx.move_to(x, y) diff --git a/po/nl.po b/po/nl.po index 72283fbcd..ee7788bec 100644 --- a/po/nl.po +++ b/po/nl.po @@ -93,6 +93,7 @@ msgstr "Apparaat gewijzigd:" msgid "Device removed:" msgstr "Apparaat verwijderd:" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Afgerond!" @@ -128,6 +129,10 @@ msgstr "Firmware Update Daemon" msgid "Firmware Utility" msgstr "Firmware-hulpmiddel" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Gevonden" + #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Alle apparaten verkrijgen die firmware-updates ondersteunen" @@ -179,9 +184,9 @@ msgstr "De achtergrondservice controleren op gebeurtenissen" msgid "No hardware detected with firmware update capability" msgstr "Geen hardware aangetroffen die in staat is firmware bij te werken" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Pluginwaarschuwing negeren" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Oké" #. TRANSLATORS: command description msgid "Read firmware from device into a file" diff --git a/po/oc.po b/po/oc.po index 60c675d23..763fd72f8 100644 --- a/po/oc.po +++ b/po/oc.po @@ -50,6 +50,7 @@ msgstr "Opcions de desbugatge" msgid "Description" msgstr "Descripcion" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Acabat !" @@ -77,6 +78,10 @@ msgstr "Fracàs de l'analisi dels paramètres" msgid "Firmware Update D-Bus Service" msgstr "Servici D-Bus de mesa a jorn dels micrologicials" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Trobat" + #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Obténer la lista dels periferics que supòrtan las mesas a jorn de micrologicial" @@ -93,6 +98,10 @@ msgstr "Installar un fichièr de micrologicial sus aqueste material" msgid "No hardware detected with firmware update capability" msgstr "Cap de material amb de capacitats de mesa a jorn del micrologicial es pas estat detectat" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "D'acòrdi" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second is a version number #. * e.g. "1.2.3" @@ -118,3 +127,7 @@ msgstr "Mòstra d'informacions de desbugatge complementàrias" #, c-format msgid "Updating %s from %s to %s... " msgstr "Mesa a jorn de %s de %s en %s " + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" diff --git a/po/pl.po b/po/pl.po index ed60f79c8..19e8a6a28 100644 --- a/po/pl.po +++ b/po/pl.po @@ -30,6 +30,12 @@ msgstr[3] "Pozostało %.0f minut" msgid "%s CPU Microcode Update" msgstr "Aktualizacja mikrokodu procesora %s" +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Aktualizacja konfiguracji urządzenia %s" + #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format @@ -93,6 +99,11 @@ msgstr "Aktualizacja urządzenia %s" msgid "%s and all connected devices may not be usable while updating." msgstr "%s i wszystkie podłączone urządzenia nie będą mogły być używane podczas aktualizacji." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Tryb serwisowy %s" + #. TRANSLATORS: warn the user before updating, %1 is a device name #, c-format msgid "%s must remain connected for the duration of the update to avoid damage." @@ -103,6 +114,21 @@ msgstr "Urządzenie %s musi być podłączone podczas trwania aktualizacji, aby msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." msgstr "Urządzenie %s musi być podłączone do prądu podczas trwania aktualizacji, aby uniknąć uszkodzenia." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "Obejście %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Wersja %s" + #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -156,6 +182,10 @@ msgstr[1] "%u sekundy" msgstr[2] "%u sekund" msgstr[3] "%u sekund" +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(przestarzałe)" + #. TRANSLATORS: command description msgid "Activate devices" msgstr "Aktywuje urządzenia" @@ -200,6 +230,10 @@ msgstr "Umożliwia instalowanie poprzednich wersji oprogramowania sprzętowego" msgid "Allow reinstalling existing firmware versions" msgstr "Umożliwia ponowne instalowanie istniejących wersji oprogramowania sprzętowego" +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Umożliwia przełączanie gałęzi oprogramowania sprzętowego" + #. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Ukończenie aktualizacji wymaga ponownego uruchomienia." @@ -216,6 +250,18 @@ msgstr "Odpowiada tak na wszystkie pytania" msgid "Apply firmware updates" msgstr "Zastosowuje aktualizacje oprogramowania sprzętowego" +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Zastosowuje aktualizację nawet, jeśli nie jest zalecana" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Zastosowuje pliki aktualizacji" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Zastosowywanie aktualizacji…" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "Approved firmware:" @@ -225,6 +271,10 @@ msgstr[1] "Zatwierdzone oprogramowanie sprzętowe:" msgstr[2] "Zatwierdzone oprogramowanie sprzętowe:" msgstr[3] "Zatwierdzone oprogramowanie sprzętowe:" +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Zapytać ponownie następnym razem?" + #. TRANSLATORS: command description msgid "Attach to firmware mode" msgstr "Podłącza do trybu oprogramowania sprzętowego" @@ -281,10 +331,38 @@ msgstr "Wymagane jest uwierzytelnienie, aby zaktualizować przechowywane sumy ko msgid "Automatic Reporting" msgstr "Automatyczne zgłaszanie" +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Automatycznie wysyłać za każdym razem?" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Dowiązuje nowy sterownik jądra" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Zablokowane pliki oprogramowania sprzętowego:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blokowanie oprogramowania sprzętowego:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blokuje instalację podanego oprogramowania sprzętowego" + #. TRANSLATORS: firmware version of bootloader msgid "Bootloader Version" msgstr "Wersja programu startowego" +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Gałąź" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Buduje plik oprogramowania sprzętowego" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Buduje oprogramowanie sprzętowe za pomocą piaskownicy" @@ -297,6 +375,14 @@ msgstr "Anuluj" msgid "Cancelled" msgstr "Anulowano" +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Nie można zastosować, ponieważ aktualizacja bazy dbx została już zastosowana." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Nie można stosować aktualizacji na nośnikach typu Live" + #. TRANSLATORS: this is when a device is hotplugged #. TRANSLATORS: this is when the daemon state changes msgid "Changed" @@ -310,6 +396,11 @@ msgstr "Sprawdza, czy kryptograficzna suma kontrolna pasuje do oprogramowania sp msgid "Checksum" msgstr "Suma kontrolna" +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Wybór gałęzi:" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Wybór urządzenia:" @@ -322,6 +413,10 @@ msgstr "Wybór typu oprogramowania sprzętowego:" msgid "Choose a release:" msgstr "Wybór wydania:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Proszę wybrać wolumin:" + #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" msgstr "Usuwa wszystkie zaplanowane aktualizacje w trybie offline" @@ -426,10 +521,18 @@ msgstr "Usunięto urządzenie:" msgid "Device stages updates" msgstr "Urządzenie przygotowuje aktualizacje" +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Urządzenie obsługuje przełączanie na inną gałąź oprogramowania sprzętowego" + #. TRANSLATORS: Device update needs to be separately activated msgid "Device update needs activation" msgstr "Aktualizacja urządzenia wymaga aktywacji" +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Urządzenie wykona kopię zapasową oprogramowania sprzętowego przez instalacją" + #. TRANSLATORS: Device will not return after update completes msgid "Device will not re-appear after update completes" msgstr "Urządzenie nie pojawi się z powrotem po ukończeniu aktualizacji" @@ -442,6 +545,20 @@ msgstr "Pomyślnie zaktualizowane urządzenia:" msgid "Devices that were not updated correctly:" msgstr "Urządzenia, które nie zostały poprawnie zaktualizowane:" +#. TRANSLATORS: message letting the user know no device upgrade available due +#. to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Urządzenia bez dostępnych aktualizacji oprogramowania sprzętowego: " + +#. TRANSLATORS: message letting the user know no device upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "Urządzenia z najnowszą dostępną wersją oprogramowania sprzętowego:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Wyłączone" + msgid "Disabled fwupdate debugging" msgstr "Wyłączono debugowanie fwupdate" @@ -495,6 +612,11 @@ msgstr[3] "Bez wysyłania zgłoszeń i wyłączenie pytania o wysyłanie zgło msgid "Do not write to the history database" msgstr "Bez zapisywania do bazy danych historii" +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Czy rozumiesz konsekwencje zmiany gałęzi oprogramowania sprzętowego?" + +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Gotowe." @@ -539,6 +661,8 @@ msgstr "Włącza obsługę aktualizacji oprogramowania sprzętowego na obsługiw msgid "Enable this remote?" msgstr "Włączyć to repozytorium?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Włączone" @@ -557,6 +681,14 @@ msgstr "Włączenie tej funkcjonalności jest wykonywane na własne ryzyko, co o msgid "Enabling this remote is done at your own risk." msgstr "Włączenie tego repozytorium wykonywane jest na własne ryzyko." +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Zaszyfrowane" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Zaszyfrowana pamięć RAM" + #. TRANSLATORS: command description msgid "Erase all firmware update history" msgstr "Usuwa całą historię aktualizacji oprogramowania sprzętowego" @@ -573,13 +705,25 @@ msgstr "Kończy działanie po małym opóźnieniu" msgid "Exit after the engine has loaded" msgstr "Kończy działanie po wczytaniu mechanizmu" +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Rozpakowuje zamknięte oprogramowanie sprzętowe do obrazów" + +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Nie powiodło się" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Zastosowanie aktualizacji się nie powiodło" + #. TRANSLATORS: we could not talk to the fwupd daemon msgid "Failed to connect to daemon" msgstr "Połączenie z usługą się nie powiodło" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Pobranie się nie powiodło z powodu ograniczenia serwera" +#. TRANSLATORS: could not parse file +msgid "Failed to extract local dbx " +msgstr "Rozpakowanie lokalnej bazy dbx się nie powiodło " #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" @@ -589,10 +733,19 @@ msgstr "Uzyskanie oczekujących urządzeń się nie powiodło" msgid "Failed to install firmware update" msgstr "Zainstalowanie aktualizacji oprogramowania sprzętowego się nie powiodło" +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Wczytanie lokalnej bazy dbx się nie powiodło" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Wczytanie poprawek się nie powiodło" +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Wczytanie systemowej bazy dbx się nie powiodło" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Przetworzenie parametrów się nie powiodło" @@ -605,6 +758,10 @@ msgstr "Przetworzenie pliku się nie powiodło" msgid "Failed to parse flags for --filter" msgstr "Przetworzenie flag dla opcji --filter się nie powiodło" +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Przetworzenie lokalnej bazy dbx się nie powiodło" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Ponowne uruchomienie się nie powiodło" @@ -613,21 +770,10 @@ msgstr "Ponowne uruchomienie się nie powiodło" msgid "Failed to set splash mode" msgstr "Ustawienie trybu ekranu wczytywania się nie powiodło" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Pobieranie pliku" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Pobieranie oprogramowania sprzętowego" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Pobieranie metadanych" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Pobieranie podpisu" +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Sprawdzenie poprawności zawartości ESP się nie powiodło" #. TRANSLATORS: filename of the local file msgid "Filename" @@ -637,6 +783,10 @@ msgstr "Nazwa pliku" msgid "Filename Signature" msgstr "Podpis nazwy pliku" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Wymagana jest nazwa pliku" + #. TRANSLATORS: command line option msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" msgstr "Filtruje za pomocą zestawu flag urządzeń, przedrostek ~ wyklucza, np. „internal,~needs-reboot”" @@ -661,6 +811,22 @@ msgstr "Usługa aktualizacji oprogramowania sprzętowego" msgid "Firmware Utility" msgstr "Narzędzie oprogramowania sprzętowego" +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Zaświadczenie oprogramowania sprzętowego" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "Firmware can not be updated in legacy BIOS mode" +msgstr "Nie można aktualizować oprogramowania sprzętowego w trybie przestarzałego BIOS-u" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Oprogramowanie sprzętowe jest już zablokowane" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Oprogramowanie nie jest już zablokowane" + #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format msgid "Firmware metadata has not been updated for %u day and may not be up to date." @@ -670,19 +836,36 @@ msgstr[1] "Metadane oprogramowania sprzętowego nie zostały zaktualizowane prze msgstr[2] "Metadane oprogramowania sprzętowego nie zostały zaktualizowane przez %u dni i mogą nie być aktualne." msgstr[3] "Metadane oprogramowania sprzętowego nie zostały zaktualizowane przez %u dni i mogą nie być aktualne." +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Aktualizacje oprogramowania sprzętowego" + msgid "Firmware updates are not supported on this machine." msgstr "Aktualizacje oprogramowania sprzętowego nie są obsługiwane na tym komputerze." msgid "Firmware updates are supported on this machine." msgstr "Aktualizacje oprogramowania sprzętowego są obsługiwane na tym komputerze." +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Aktualizacje oprogramowania sprzętowego są wyłączone; wykonanie polecenia „fwupdmgr unlock” je włączy" + +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Flagi" +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Wymusza działanie przez rozluźnienie części testów uruchamiania" + msgid "Force the action ignoring all warnings" msgstr "Wymusza działanie ignorując wszystkie ostrzeżenia" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Odnaleziono" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -715,9 +898,17 @@ msgstr "Uzyskuje informacje o pliku oprogramowania sprzętowego" msgid "Gets the configured remotes" msgstr "Uzyskuje skonfigurowane repozytoria" +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Uzyskuje atrybuty zabezpieczeń komputera" + #. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Uzyskuje listę zatwierdzonego oprogramowania sprzętowego." +msgid "Gets the list of approved firmware" +msgstr "Uzyskuje listę zatwierdzonego oprogramowania sprzętowego" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Uzyskuje listę zablokowanego oprogramowania sprzętowego" #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -739,6 +930,15 @@ msgstr "Sprzęt czeka na ponowne podłączenie" msgid "High" msgstr "Wysoka" +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Identyfikator zabezpieczeń komputera:" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Bezczynne…" @@ -747,10 +947,26 @@ msgstr "Bezczynne…" msgid "Ignore SSL strict checks when downloading files" msgstr "Ignoruje ścisłe testy SSL podczas pobierania plików" +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignoruje niepowodzenia sum kontrolnych oprogramowania sprzętowego" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignoruje niepowodzenia zgodności sprzętu z oprogramowaniem sprzętowym" + +#. TRANSLATORS: command line option +msgid "Ignore requirement of external power source" +msgstr "Ignoruje wymaganie zewnętrznego źródła zasilania" + #. TRANSLATORS: Ignore validation safety checks when flashing this device msgid "Ignore validation safety checks" msgstr "Ignoruje testy bezpieczeństwa i sprawdzanie poprawności" +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ignorowanie ścisłych testów SSL, aby robić to automatycznie w przyszłości, należy wyeksportować zmienną DISABLE_SSL_STRICT w środowisku" + #. TRANSLATORS: length of time the update takes to apply msgid "Install Duration" msgstr "Czas trwania instalacji" @@ -795,10 +1011,57 @@ msgstr "Instalowanie aktualizacji oprogramowania sprzętowego…" msgid "Installing on %s…" msgstr "Instalowanie na urządzeniu %s…" +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "ACM chronione przez Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Bezpiecznik OTP Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Błąd zasad postępowania Intel BootGuard w przypadku błędów" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Uruchamianie zweryfikowane przez Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET jest aktywne" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET jest włączone" + +#. TRANSLATORS: Title: Direct Connect Interface (DCI) allows +#. * debugging of Intel processors using the USB3 port +msgid "Intel DCI debugger" +msgstr "Debuger Intel DCI" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + #. TRANSLATORS: Device cannot be removed easily msgid "Internal device" msgstr "Wewnętrzne urządzenie" +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Nieprawidłowe" + #. TRANSLATORS: Is currently in bootloader mode msgid "Is in bootloader mode" msgstr "Jest w trybie programu startowego" @@ -832,6 +1095,22 @@ msgstr "Linux Vendor Firmware Service (stabilne oprogramowanie sprzętowe)" msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Linux Vendor Firmware Service (testowe oprogramowanie sprzętowe)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Jądro Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Blokada jądra Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Obszar wymiany systemu Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Wyświetla listę wpisów w bazie dbx" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "Wyświetla listę obsługiwanych aktualizacji oprogramowania sprzętowego" @@ -840,17 +1119,39 @@ msgstr "Wyświetla listę obsługiwanych aktualizacji oprogramowania sprzętoweg msgid "List the available firmware types" msgstr "Wyświetla listę dostępnych typów oprogramowania sprzętowego" +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Wyświetla listę plików na ESP" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Wczytywanie…" +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Zablokowane" + #. TRANSLATORS: the release urgency msgid "Low" msgstr "Niska" +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Tryb produkcyjny MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Obejście MEI" + +msgid "MEI version" +msgstr "Wersja MEI" + #. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Ręcznie wstawia podane wtyczki na białą listę" +msgid "Manually enable specific plugins" +msgstr "Ręcznie włącza podane wtyczki" #. TRANSLATORS: the release urgency msgid "Medium" @@ -878,8 +1179,8 @@ msgid "Mismatched daemon and client, use %s instead" msgstr "Niezgodna usługa i klient, proszę użyć %s zamiast tego" #. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Modyfikuje wartość konfiguracji usługi." +msgid "Modifies a daemon configuration value" +msgstr "Modyfikuje wartość konfiguracji usługi" #. TRANSLATORS: command description msgid "Modifies a given remote" @@ -895,6 +1196,10 @@ msgstr "Modyfikacja konfiguracji usługi" msgid "Monitor the daemon for events" msgstr "Monitoruje zdarzenia usługi" +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Montuje MSP" + #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware msgid "Needs a reboot after installation" msgstr "Wymaga ponownego uruchomienia po instalacji" @@ -907,6 +1212,7 @@ msgstr "Wymaga wyłączenia komputera po instalacji" msgid "New version" msgstr "Nowa wersja" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Nie podano działania." @@ -944,14 +1250,22 @@ msgstr "Brak dostępnych repozytoriów" msgid "No updates were applied" msgstr "Nie zastosowano żadnych aktualizacji" +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Nie odnaleziono" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Nieobsługiwane" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Wyświetla tylko jedną wartość PCR" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Zastępuje ostrzeżenie wtyczki" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Zastępuje domyślną ścieżkę ESP" @@ -964,6 +1278,14 @@ msgstr "Zastępuje ostrzeżenia i wymusza działanie" msgid "Parse and show details about a firmware file" msgstr "Przetwarza i wyświetla informacje o pliku oprogramowania sprzętowego" +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Przetwarzanie aktualizacji bazy dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Przetwarzanie systemowej bazy dbx…" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Hasło" @@ -980,6 +1302,14 @@ msgstr "Procent ukończenia" msgid "Please enter a number from 0 to %u: " msgstr "Proszę podać liczbę od 0 do %u: " +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Brak zależności wtyczki" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Ochrona DMA przed uruchomieniem" + #. TRANSLATORS: version number of previous firmware msgid "Previous version" msgstr "Poprzednia wersja" @@ -1035,8 +1365,8 @@ msgid "Refresh metadata from remote server" msgstr "Odświeża metadane ze zdalnego serwera" #. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "Ponownie instaluje obecne oprogramowanie sprzętowe na urządzeniu." +msgid "Reinstall current firmware on the device" +msgstr "Ponownie instaluje obecne oprogramowanie sprzętowe na urządzeniu" #. TRANSLATORS: command description msgid "Reinstall firmware on a device" @@ -1070,9 +1400,13 @@ msgstr "Adres URI zgłoszenia" msgid "Reported to remote server" msgstr "Zgłoszone do zdalnego serwera" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Wymaga zasilania z gniazdka" +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Nie odnaleziono wymaganego systemu plików „efivarfs”" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Nie odnaleziono wymaganego oprogramowania sprzętowego" #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" @@ -1109,6 +1443,22 @@ msgstr "Wykonuje procedurę czyszczenia składania wtyczki podczas używania „ msgid "Run the plugin composite prepare routine when using install-blob" msgstr "Wykonuje procedurę przygotowania składania wtyczki podczas używania „install-blob”" +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Przyrostek uruchamiania" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Region BIOS-u SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Blokada SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Zapis SPI" + #. TRANSLATORS: command line option msgid "Save device state into a JSON file between executions" msgstr "Zapisuje stan urządzenia do pliku JSON między wykonaniami" @@ -1125,6 +1475,10 @@ msgstr "Planowanie…" msgid "Selected device" msgstr "Wybrane urządzenie" +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Wybrany wolumin" + #. TRANSLATORS: serial number of hardware msgid "Serial Number" msgstr "Numer seryjny" @@ -1133,17 +1487,18 @@ msgstr "Numer seryjny" msgid "Set the debugging flag during update" msgstr "Ustawia flagę debugowania podczas aktualizacji" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Ustawienie listy zatwierdzonego oprogramowania sprzętowego" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Ustawia listę zatwierdzonego oprogramowania sprzętowego." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Udostępnia historię oprogramowania sprzętowego programistom" +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Wyświetla wszystkie wyniki" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "Wyświetla wersje klienta i usługi" @@ -1176,6 +1531,10 @@ msgstr "Wyświetla historię aktualizacji oprogramowania sprzętowego" msgid "Show plugin verbose information" msgstr "Wyświetla więcej informacji o wtyczkach" +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Wyświetla obliczoną wersję bazy dbx" + #. TRANSLATORS: command line option msgid "Show the debug log from the last attempted update" msgstr "Wyświetla dziennik debugowania z ostatniej aktualizacji" @@ -1213,6 +1572,10 @@ msgstr "Źródło" msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Podaje identyfikatory dostawcy/produktu urządzenia DFU" +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Podaje plik bazy dbx" + msgid "Specify the number of bytes per USB transfer" msgstr "Podaje liczbę bajtów na przesyłanie USB" @@ -1272,10 +1635,42 @@ msgstr "Pomyślnie sprawdzono poprawność sum kontrolnych urządzenia" msgid "Summary" msgstr "Podsumowanie" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Obsługiwane" + #. TRANSLATORS: Is found in current metadata msgid "Supported on remote server" msgstr "Obsługiwane na zdalnym serwerze" +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Uśpienie do trybu bezczynności" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Uśpienie do pamięci RAM" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Przełącza gałąź oprogramowania sprzętowego na urządzeniu" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Komputer wymaga zewnętrznego źródła zasilania" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Rekonstrukcja PCR0 TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Skażone" + msgid "Target" msgstr "Cel" @@ -1283,6 +1678,23 @@ msgstr "Cel" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "LVFS to wolny serwis działający jako niezależny podmiot prawny niemający związków z systemem $OS_RELEASE:NAME$. Dystrybutor używanego systemu mógł nie zweryfikować żadnych aktualizacji oprogramowania sprzętowego pod kątem zgodności z używanym komputerem lub podłączonymi urządzeniami. Każde oprogramowanie sprzętowe jest dostarczane wyłącznie przez oryginalnego producenta sprzętu." +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "PCR0 TPM różni się od rekonstrukcji." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Usługa wczytała kod firmy trzeciej i nie jest już wspierana przez jej programistów!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Oprogramowanie sprzętowe od firmy %s nie jest dostarczane przez firmę %s, dostawcę sprzętu." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Nie ma zablokowanych plików oprogramowania sprzętowego" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "There is no approved firmware." @@ -1295,6 +1707,18 @@ msgstr "Ten program może działać poprawnie tylko jako root" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "To repozytorium zawiera oprogramowanie sprzętowe nieobjęte embargo, ale nadal testowane przez dostawcę sprzętu. Należy upewnić się, że istnieje możliwość ręcznego zainstalowania poprzedniej wersji oprogramowania sprzętowego w razie niepowodzenia aktualizacji." +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Ten komputer ma problemy HSI uruchamiania." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Ten komputer ma niski poziom zabezpieczeń HSI." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "To narzędzie umożliwia administratorowi zastosowywanie aktualizacji bazy dbx dla UEFI." + #. TRANSLATORS: the user needs to stop playing with stuff msgid "This tool can only be used by the root user" msgstr "To narzędzie może być używane tylko przez użytkownika root" @@ -1303,10 +1727,42 @@ msgstr "To narzędzie może być używane tylko przez użytkownika root" msgid "Type" msgstr "Typ" +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Nie wykryto lub nie skonfigurowano partycji ESP UEFI" + #. TRANSLATORS: program name msgid "UEFI Firmware Utility" msgstr "Narzędzie oprogramowania sprzętowego UEFI" +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled" +msgstr "Aktualizacje kapsułowe UEFI są niedostępne lub wyłączone" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Narzędzie bazy dbx dla UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Zabezpieczone uruchamianie UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Nie można połączyć się z usługą" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Odwiązuje bieżący sterownik" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Odblokowywanie oprogramowania sprzętowego:" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Niezaszyfrowane" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -1320,10 +1776,18 @@ msgstr "Nieznane urządzenie" msgid "Unlock the device to allow access" msgstr "Odblokowanie urządzenia" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Odblokowane" + #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" msgstr "Odblokowuje urządzenie" +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Odmontowuje ESP" + #. TRANSLATORS: command line option msgid "Unset the debugging flag during update" msgstr "Usuwa flagę debugowania podczas aktualizacji" @@ -1333,6 +1797,10 @@ msgstr "Usuwa flagę debugowania podczas aktualizacji" msgid "Unsupported daemon version %s, client version is %s" msgstr "Nieobsługiwana wersja usługi %s, wersja klienta to %s" +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Nieskażone" + #. TRANSLATORS: Device is updatable in this or any other mode msgid "Updatable" msgstr "Można aktualizować" @@ -1381,6 +1849,9 @@ msgstr "Aktualizuje przechowywane metadane obecną zawartością" msgid "Updates all firmware to latest versions available" msgstr "Aktualizuje całe oprogramowanie sprzętowe do najnowszych dostępnych wersji" +msgid "Updating" +msgstr "Aktualizowanie" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" @@ -1399,10 +1870,6 @@ msgstr "Aktualizowanie urządzenia %s…" msgid "Upgrade available for %s from %s to %s" msgstr "Dostępna jest aktualizacja urządzenia %s z wersji %s do %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Komunikat wysyłania:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Wysłanie zgłoszenia tylko tym razem, ale pytanie ponownie przy przyszłych aktualizacjach" @@ -1429,6 +1896,14 @@ msgstr "Wysyłanie zgłoszeń o oprogramowaniu sprzętowym pomaga dostawcom spr msgid "Urgency" msgstr "Pilność" +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Polecenie fwupdmgr --help wyświetli pomoc" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Polecenie fwupdtool --help wyświetli pomoc" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "Używa flag poprawek podczas instalowania oprogramowania sprzętowego" @@ -1441,21 +1916,33 @@ msgstr "Użytkownik został powiadomiony" msgid "Username" msgstr "Nazwa użytkownika" +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Prawidłowe" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Sprawdzanie poprawności zawartości ESP…" + #. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') msgid "Variant" msgstr "Wariant" #. TRANSLATORS: manufacturer of hardware msgid "Vendor" -msgstr "Producent" +msgstr "Dostawca" #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" msgstr "Sprawdzanie poprawności…" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "OSTRZEŻENIE: ignorowanie ścisłych testów SSL, aby robić to automatycznie w przyszłości, należy wyeksportować zmienną DISABLE_SSL_STRICT w środowisku" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Wersja" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "OSTRZEŻENIE:" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1477,6 +1964,10 @@ msgstr "Zapisuje oprogramowanie sprzętowe z pliku na urządzenie" msgid "Write firmware from file into one partition" msgstr "Zapisuje oprogramowanie sprzętowe z pliku na jedną partycję" +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Zapisywanie pliku:" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Zapisywanie…" @@ -1485,19 +1976,19 @@ msgstr "Zapisywanie…" msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." msgstr "Używana dystrybucja mogła nie sprawdzić zgodności aktualizacji oprogramowania sprzętowego z komputerem i podłączonymi urządzeniami." +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Sprzęt może zostać uszkodzony przez użycie tego oprogramowania sprzętowego, a zainstalowanie tego wydania może spowodować utratę gwarancji od firmy %s." + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "domyślna" + #. TRANSLATORS: program name msgid "fwupd TPM event log utility" msgstr "Narzędzie dziennika zdarzeń TPM usługi fwupd" -#. TRANSLATORS: message letting the user know no device upgrade available due -#. to missing on LVFS -#. * %1 is the device name -#, c-format -msgid "• %s has no available firmware updates" -msgstr "• Urządzenie %s nie ma dostępnych aktualizacji oprogramowania sprzętowego" - -#. TRANSLATORS: message letting the user know no device upgrade available -#. * %1 is the device name -#, c-format -msgid "• %s has the latest available firmware version" -msgstr "• Urządzenie %s ma najnowszą dostępną wersję oprogramowania sprzętowego" +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Wtyczki usługi fwupd" diff --git a/po/pt_BR.po b/po/pt_BR.po index 07b4fc1f3..18f347438 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -32,6 +32,12 @@ msgstr[1] "%.0f minutos restantes" msgid "%s CPU Microcode Update" msgstr "Atualização de microcódigo de CPU %s" +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Atualização da configuração de %s " + #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format @@ -95,6 +101,11 @@ msgstr "Atualização de %s" msgid "%s and all connected devices may not be usable while updating." msgstr "%s e todos os dispositivos conectados não podem ser usados durante a atualização." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Modo de fabricação de %s" + #. TRANSLATORS: warn the user before updating, %1 is a device name #, c-format msgid "%s must remain connected for the duration of the update to avoid damage." @@ -105,6 +116,21 @@ msgstr "%s deve permanecer conectado durante a atualização para evitar danos." msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." msgstr "%s deve permanecer conectado a uma fonte de energia durante a atualização para evitar danos." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "substituição de %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "versão %s " + #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -146,6 +172,10 @@ msgid_plural "%u seconds" msgstr[0] "%u segundo" msgstr[1] "%u segundos" +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(obsoleto)" + #. TRANSLATORS: command description msgid "Activate devices" msgstr "Habilita dispositivos" @@ -206,6 +236,18 @@ msgstr "Responde sim para todas as perguntas" msgid "Apply firmware updates" msgstr "Aplica atualizações de firmware" +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Aplica a atualização mesmo quando não aconselhável" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Aplica arquivos de atualização" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Aplicando atualização…" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "Approved firmware:" @@ -213,6 +255,10 @@ msgid_plural "Approved firmware:" msgstr[0] "Firmware aprovado:" msgstr[1] "Firmwares aprovados:" +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Perguntar novamente da próxima vez?" + #. TRANSLATORS: command description msgid "Attach to firmware mode" msgstr "Modo anexar ao firmware" @@ -269,6 +315,18 @@ msgstr "Autenticação é necessária para atualizar as somas de verificação a msgid "Automatic Reporting" msgstr "Relatórios automáticos" +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Enviar automaticamente toda vez?" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Arquivos de firmware bloqueados:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Bloqueando firmware:" + #. TRANSLATORS: firmware version of bootloader msgid "Bootloader Version" msgstr "Versão do gerenciador de boot" @@ -285,6 +343,14 @@ msgstr "Cancelar" msgid "Cancelled" msgstr "Cancelado" +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Não foi possível aplicar, pois a atualização de dbx já foi aplicada." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Não é possível aplicar atualizações em uma mídia live" + #. TRANSLATORS: this is when a device is hotplugged #. TRANSLATORS: this is when the daemon state changes msgid "Changed" @@ -310,6 +376,10 @@ msgstr "Escolha um tipo de firmware:" msgid "Choose a release:" msgstr "Escolha um lançamento:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Escolha um volume:" + #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" msgstr "Limpa quaisquer atualizações agendadas a serem atualizadas desconectadas" @@ -396,7 +466,7 @@ msgstr "Firmware de dispositivo é necessário para ter uma verificação de ver #. TRANSLATORS: Is locked and can be unlocked msgid "Device is locked" -msgstr "O dispositivo está travado" +msgstr "O dispositivo está desbloqueado" #. TRANSLATORS: a version check is required for all firmware msgid "Device is required to install all provided releases" @@ -430,6 +500,20 @@ msgstr "Dispositivos que foram atualizados com sucesso:" msgid "Devices that were not updated correctly:" msgstr "Dispositivos que não foram atualizados com sucesso:" +#. TRANSLATORS: message letting the user know no device upgrade available due +#. to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Dispositivos com nenhuma atualização de firmware disponível:" + +#. TRANSLATORS: message letting the user know no device upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "Dispositivos com a versão mais recente do firmware disponível:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Desabilitado" + msgid "Disabled fwupdate debugging" msgstr "Depuração de fwupdate desabilitada" @@ -479,6 +563,7 @@ msgstr[1] "Não enviar relatórios e nunca solicitar para enviar relatórios em msgid "Do not write to the history database" msgstr "Não escreve no banco de dados de histórico" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Feito!" @@ -523,6 +608,8 @@ msgstr "Habilita suporte a atualização de firmware em sistemas suportados" msgid "Enable this remote?" msgstr "Habilitar esse remoto?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Habilitado" @@ -541,6 +628,14 @@ msgstr "A habilitação dessa funcionalidade é feita por sua conta e risco, o q msgid "Enabling this remote is done at your own risk." msgstr "A habilitação deste remoto é feito a seu próprio custo e risco." +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Criptografado" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM criptografada" + #. TRANSLATORS: command description msgid "Erase all firmware update history" msgstr "Apaga todo histórico de atualização de firmware" @@ -557,13 +652,21 @@ msgstr "Sair após pequeno atraso" msgid "Exit after the engine has loaded" msgstr "Sair após o carregamento do motor" +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Falhou" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Falha ao aplicar a atualização" + #. TRANSLATORS: we could not talk to the fwupd daemon msgid "Failed to connect to daemon" msgstr "Falha ao se conectar ao daemon" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Falha ao baixar em razão de limite do servidor" +#. TRANSLATORS: could not parse file +msgid "Failed to extract local dbx " +msgstr "Falha ao extrair o dbx local" #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" @@ -573,10 +676,19 @@ msgstr "Falha ao obter dispositivos pendentes" msgid "Failed to install firmware update" msgstr "Falha ao instalar a atualização de firmware" +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Falha ao carregar o dbx local" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Falha ao carregar peculiaridades" +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Falha ao carregar o dbx do sistema" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Falha ao interpretar argumentos" @@ -589,6 +701,10 @@ msgstr "Falha ao analisar o arquivo" msgid "Failed to parse flags for --filter" msgstr "Falha ao analisar opções para --filter" +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Falha ao analisar o dbx local" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Falha ao reinicializar" @@ -597,21 +713,10 @@ msgstr "Falha ao reinicializar" msgid "Failed to set splash mode" msgstr "Falha ao definir o modo splash" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Obtendo arquivo" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Obtendo firmware" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Obtendo metadados" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Obtendo assinatura" +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Falha ao validar o conteúdo da ESP" #. TRANSLATORS: filename of the local file msgid "Filename" @@ -621,6 +726,10 @@ msgstr "Nome de arquivo" msgid "Filename Signature" msgstr "Assinatura de nome de arquivo" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Nome de arquivo necessário" + #. TRANSLATORS: command line option msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" msgstr "Filtra com um conjunto de opções de dispositivo usando um prefixo ~ para excluir, p.ex. \"internal,~needs-reboot\"" @@ -645,6 +754,18 @@ msgstr "Daemon de Atualização de Firmware" msgid "Firmware Utility" msgstr "Utilitário de Firmware" +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Atestação de firmware" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "O firmware já está bloqueado" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "O firmware ainda não está bloqueado" + #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format msgid "Firmware metadata has not been updated for %u day and may not be up to date." @@ -652,12 +773,17 @@ msgid_plural "Firmware metadata has not been updated for %u days and may not be msgstr[0] "Os metadados do firmware não foram atualizados a %u dia e podem não estar atualizados." msgstr[1] "Os metadados do firmware não foram atualizados a %u dias e podem não estar atualizados." +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Atualizações de firmware" + msgid "Firmware updates are not supported on this machine." msgstr "Não há suporte a atualizações de firmware nessa máquina." msgid "Firmware updates are supported on this machine." msgstr "Há suporte a atualizações de firmware nesta máquina." +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Opções" @@ -665,6 +791,10 @@ msgstr "Opções" msgid "Force the action ignoring all warnings" msgstr "Força a ação ignorando todos avisos" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Encontrado" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -695,9 +825,9 @@ msgstr "Obtém detalhes sobre um arquivo de firmware" msgid "Gets the configured remotes" msgstr "Obtém os remotos configurados" -#. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Obtém a lista de firmwares aprovados." +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Obtém os atributos de segurança do host" #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -719,6 +849,15 @@ msgstr "O hardware está aguardando para ser reconectado" msgid "High" msgstr "Alta" +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ID de segurança do host:" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Ocioso…" @@ -775,10 +914,57 @@ msgstr "Instalando atualização de firmware…" msgid "Installing on %s…" msgstr "Instalando em %s…" +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "ACM protegido com Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Fusível OTP do Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Política de erro do Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Inicialização verificada com Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET ativo" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Habilitado para Intel CET" + +#. TRANSLATORS: Title: Direct Connect Interface (DCI) allows +#. * debugging of Intel processors using the USB3 port +msgid "Intel DCI debugger" +msgstr "Depurador Intel DCI" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + #. TRANSLATORS: Device cannot be removed easily msgid "Internal device" msgstr "Dispositivo interno" +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Inválido" + #. TRANSLATORS: Is currently in bootloader mode msgid "Is in bootloader mode" msgstr "Está no modo gerenciador de boot" @@ -810,6 +996,22 @@ msgstr "Linux Vendor Firmware Service (firmware estável)" msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Linux Vendor Firmware Service (firmware de teste)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Kernel Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Lockdown do kernel Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Swap do Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Lista entradas no dbx" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "Lista atualizações de firmware suportadas" @@ -822,13 +1024,31 @@ msgstr "Lista os tipos de firmware disponível" msgid "Loading…" msgstr "Carregando…" +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Bloqueado" + #. TRANSLATORS: the release urgency msgid "Low" msgstr "Baixa" +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Modo de fabricação do MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "substituição de MEI" + +msgid "MEI version" +msgstr "versão MEI" + #. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Insere manualmente na lista branca plugins específicos" +msgid "Manually enable specific plugins" +msgstr "Habilita manualmente plugins específicos" #. TRANSLATORS: the release urgency msgid "Medium" @@ -855,10 +1075,6 @@ msgstr "Versão mínima" msgid "Mismatched daemon and client, use %s instead" msgstr "Daemon e cliente incompatíveis, use %s" -#. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Modifica o valor de uma configuração do daemon." - #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Modifica um remoto dado" @@ -885,6 +1101,7 @@ msgstr "Precisa de desligamento após a instalação" msgid "New version" msgstr "Nova versão" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Nenhuma ação especificada!" @@ -922,14 +1139,22 @@ msgstr "Nenhum remoto disponível" msgid "No updates were applied" msgstr "Nenhuma atualização foi aplicada" +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Não encontrado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Não suportado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Mostra apenas valor único de PCR" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Sobrepõe um aviso de plugin" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Substitui o caminho ESP padrão" @@ -942,6 +1167,14 @@ msgstr "Anula avisos e força a ação" msgid "Parse and show details about a firmware file" msgstr "Analisa e mostra detalhes sobre um arquivo de firmware" +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Analisando a atualização de dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Analisando o dbx do sistema…" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Senha" @@ -958,6 +1191,10 @@ msgstr "Percentagem concluída" msgid "Please enter a number from 0 to %u: " msgstr "Por favor, insira um número de 0 a %u: " +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Proteção de DMA pré-inicialização" + #. TRANSLATORS: version number of previous firmware msgid "Previous version" msgstr "Versão anterior" @@ -1012,10 +1249,6 @@ msgstr "Reinicializando…" msgid "Refresh metadata from remote server" msgstr "Renova metadados do servidor remoto" -#. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "Reinstala o atual firmware no dispositivo." - #. TRANSLATORS: command description msgid "Reinstall firmware on a device" msgstr "Reinstala firmware em um dispositivo" @@ -1048,10 +1281,6 @@ msgstr "URI do relatório" msgid "Reported to remote server" msgstr "Relatado ao servidor remoto" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Requer alimentação CA" - #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" msgstr "Requer um gerenciador de boot" @@ -1087,6 +1316,22 @@ msgstr "Executa a rotina de limpeza da composição de plugin ao usar install-bl msgid "Run the plugin composite prepare routine when using install-blob" msgstr "Executa a rotina de preparação da composição de plugin ao usar install-blob" +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Sufixo de tempo de execução" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Região BIOS do SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Bloqueio de SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Escrita de SPI" + #. TRANSLATORS: command line option msgid "Save device state into a JSON file between executions" msgstr "Salva o estado do dispositivo em um arquivo JSON entre execuções" @@ -1103,6 +1348,10 @@ msgstr "Agendando…" msgid "Selected device" msgstr "Dispositivo selecionado" +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volume selecionado" + #. TRANSLATORS: serial number of hardware msgid "Serial Number" msgstr "Número de série" @@ -1111,13 +1360,10 @@ msgstr "Número de série" msgid "Set the debugging flag during update" msgstr "Define a opção de depuração durante atualização" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Define a lista de firmwares aprovados" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Define a lista de firmwares aprovados." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Compartilha histórico de firmware com os desenvolvedores" @@ -1154,6 +1400,10 @@ msgstr "Mostra histórico de atualizações de firmware" msgid "Show plugin verbose information" msgstr "Mostra informação verbosa de plugin" +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Mostra a versão calculada do dbx" + #. TRANSLATORS: command line option msgid "Show the debug log from the last attempted update" msgstr "Mostra o registro log de depuração da tentativa mais recente de atualização" @@ -1191,6 +1441,10 @@ msgstr "Fonte" msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Especifica ID(s) de Fornecedor/Produto de dispositivo DFU" +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Especifica o arquivo de banco de dados dbx" + msgid "Specify the number of bytes per USB transfer" msgstr "Especifica o número de bytes por transferência USB" @@ -1206,7 +1460,7 @@ msgstr "Remoto desabilitado com sucesso" #. TRANSLATORS: success message -- where 'metadata' is information #. * about available firmware on the remote server msgid "Successfully downloaded new metadata: " -msgstr "Baixado com sucesso novos metadados:" +msgstr "Baixados com sucesso novos metadados:" #. TRANSLATORS: success message msgid "Successfully enabled remote" @@ -1248,10 +1502,34 @@ msgstr "Somas de verificação de dispositivo verificadas com sucesso" msgid "Summary" msgstr "Resumo" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Suportado" + #. TRANSLATORS: Is found in current metadata msgid "Supported on remote server" msgstr "Suporte no servidor remoto" +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspensão para inativo" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspensão para RAM" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Reconstrução de PCR0 de TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Contaminado" + msgid "Target" msgstr "Alvo" @@ -1259,6 +1537,14 @@ msgstr "Alvo" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "O LVFS é um serviço livre que opera como uma entidade legal independente e tem nenhuma conexão com $OS_RELEASE:NAME$. Seu distribuidor pode não ter verificado alguma das atualizações de firmware por compatibilidade com seu sistema ou dispositivos conectados. Todo firmware é fornecido apenas pelo fabricante do equipamento original." +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "O TPM PCR0 diverge da reconstrução." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Não há arquivos de firmware bloqueados" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "There is no approved firmware." @@ -1271,6 +1557,18 @@ msgstr "Esse programa só pode funcionar corretamente como root" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "Este remoto contém firmware que não está embargado, mas ainda está sendo testado pelo fornecedor do hardware. Você deve garantir que você tenha uma maneira de fazer downgrade manual do firmware se a atualização do firmware falhar." +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Este sistema possui problemas de tempo de execução de HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Este sistema possui um nível de segurança de HSI baixo." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Esta ferramenta permite que um administrador aplique atualizações de dbx UEFI." + #. TRANSLATORS: the user needs to stop playing with stuff msgid "This tool can only be used by the root user" msgstr "Essa ferramenta só pode ser usada pelo usuário root" @@ -1283,6 +1581,26 @@ msgstr "Tipo" msgid "UEFI Firmware Utility" msgstr "Utilitário de Firmware UEFI" +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Utilitário de dbx UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Secure boot de UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Não foi possível conectar ao serviço" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Desbloqueando firmware:" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Descriptografado" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -1296,6 +1614,10 @@ msgstr "Dispositivo desconhecido" msgid "Unlock the device to allow access" msgstr "Desbloquear o dispositivo para permitir acesso" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Desbloqueado" + #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" msgstr "Desbloqueia o dispositivo para acesso do firmware" @@ -1309,6 +1631,10 @@ msgstr "Desativa a opção de depuração durante atualização" msgid "Unsupported daemon version %s, client version is %s" msgstr "Sem suporte ao daemon na versão %s, a versão do cliente é %s" +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Descontaminado" + #. TRANSLATORS: Device is updatable in this or any other mode msgid "Updatable" msgstr "Atualizável" @@ -1355,7 +1681,10 @@ msgstr "Atualiza os metadados armazenados com o conteúdo atual" #. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" -msgstr "Atualiza todos os firmwares para a última versão disponível" +msgstr "Atualiza todos os firmwares para a versão mais recente disponível" + +msgid "Updating" +msgstr "Atualizando" #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are @@ -1375,10 +1704,6 @@ msgstr "Atualizando %s…" msgid "Upgrade available for %s from %s to %s" msgstr "Atualização disponível para %s de %s para %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Mensagem enviada:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Enviar relatório apenas desta vez, mas solicitar novamente para atualizações futuras" @@ -1401,6 +1726,14 @@ msgstr "O envio de relatórios de firmware ajuda os fornecedores de hardware a i msgid "Urgency" msgstr "Urgência" +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Use fwupdmgr --help para ajuda" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Use fwupdtool --help para ajuda" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "Usa opções de peculiaridades ao instalar firmware" @@ -1413,6 +1746,14 @@ msgstr "O usuário foi notificado" msgid "Username" msgstr "Nome de usuário" +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Válido" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validando conteúdo da ESP…" + #. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') msgid "Variant" msgstr "Variação" @@ -1425,9 +1766,13 @@ msgstr "Fornecedor" msgid "Verifying…" msgstr "Verificando…" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "AVISO: Ignorando verificações estritas de SSL. Para fazer isso automaticamente no futuro, exporte DISABLE_SSL_STRICT em seu ambiente" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versão" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "AVISO:" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1449,6 +1794,10 @@ msgstr "Escreve um firmware do arquivo para o dispositivo" msgid "Write firmware from file into one partition" msgstr "Escreve um firmware do arquivo para uma partição" +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Escrevendo arquivo:" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Escrevendo…" @@ -1461,15 +1810,6 @@ msgstr "Seu distribuidor pode não ter verificado qualquer das atualizações de msgid "fwupd TPM event log utility" msgstr "Utilitário de registro de eventos TPM do fwupd" -#. TRANSLATORS: message letting the user know no device upgrade available due -#. to missing on LVFS -#. * %1 is the device name -#, c-format -msgid "• %s has no available firmware updates" -msgstr "• %s possui nenhuma atualização de firmware disponível" - -#. TRANSLATORS: message letting the user know no device upgrade available -#. * %1 is the device name -#, c-format -msgid "• %s has the latest available firmware version" -msgstr "• %s possui a última versão do firmware disponível" +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Plugins do fwupd" diff --git a/po/ru.po b/po/ru.po index 27ea0794b..8ec7d25d2 100644 --- a/po/ru.po +++ b/po/ru.po @@ -341,6 +341,7 @@ msgstr "Не проверять незарегистрированную ист msgid "Do not write to the history database" msgstr "Не сохранять в базу данных истории" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Готово!" @@ -381,6 +382,8 @@ msgstr "Активировать поддержку обновлений про msgid "Enable this remote?" msgstr "Активировать этот репозиторий?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Активировано" @@ -419,10 +422,6 @@ msgstr "Выйти после загрузки движка" msgid "Failed to connect to daemon" msgstr "Не удалось подключиться к фоновой службе" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Не удалось загрузить из-за ограничения сервера" - #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" msgstr "Не удалось получить ожидающие устройства" @@ -447,22 +446,6 @@ msgstr "Не удалось перезагрузить" msgid "Failed to set splash mode" msgstr "Не удалось установить режим заставки" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Получение файла" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Получение прошивки" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Получение метаданных" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Получение подписи" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "Имя файла" @@ -506,6 +489,7 @@ msgstr "Обновления прошивки не поддерживаются msgid "Firmware updates are supported on this machine." msgstr "Обновления прошивки поддерживаются на этой машине." +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Флаги" @@ -513,6 +497,10 @@ msgstr "Флаги" msgid "Force the action ignoring all warnings" msgstr "Выполнить действие, игнорируя все предупреждения" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Найдено" + #. TRANSLATORS: command description msgid "Get all devices and possible releases" msgstr "Получить все устройства и возможные выпуски" @@ -533,10 +521,6 @@ msgstr "Получить сведения о файле прошивки" msgid "Gets the configured remotes" msgstr "Получить настроенные репозитории" -#. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Получить список одобренных прошивок." - #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Получить список обновлений для подключенного оборудования" @@ -610,10 +594,6 @@ msgstr "Вывести список поддерживаемых обновле msgid "Loading…" msgstr "Загрузка…" -#. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Изменить вручную белый список определённых плагинов" - #. TRANSLATORS: remote URI msgid "Metadata URI" msgstr "URI метаданных" @@ -627,10 +607,6 @@ msgstr "Метаданные можно получить в Linux Vendor Firmwar msgid "Mismatched daemon and client, use %s instead" msgstr "Несовместимые фоновая служба и клиент, используйте %s" -#. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Изменить значение в конфигурации фоновой службы." - #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Модифицировать данный репозиторий" @@ -645,6 +621,7 @@ msgstr "Изменить настройки фоновой службы" msgid "Monitor the daemon for events" msgstr "Следить за событиями в фоновой службе" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Не определено никаких действий." @@ -664,9 +641,9 @@ msgstr "В настоящее время репозитории не актив msgid "No updates were applied" msgstr "Обновления не были применены" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Переопределить предупреждение плагина" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "ОК" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -797,13 +774,10 @@ msgstr "Планировка…" msgid "Set the debugging flag during update" msgstr "Установить флаг отладки во время обновления" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Установить список одобренных прошивок" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Установить список одобренных прошивок." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Поделиться историей прошивки с разработчиками" @@ -964,10 +938,6 @@ msgstr "Обновление %s с %s на %s…" msgid "Updating %s…" msgstr "Обновление устройства %s…" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Загрузить сообщение:" - #. TRANSLATORS: ask the user to upload msgid "Upload report now?" msgstr "Загрузить отчет сейчас?" @@ -984,6 +954,10 @@ msgstr "Имя пользователя" msgid "Verifying…" msgstr "Проверка…" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Версия" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Ожидание…" diff --git a/po/sk.po b/po/sk.po index 3f74db8e0..31b031131 100644 --- a/po/sk.po +++ b/po/sk.po @@ -93,6 +93,7 @@ msgstr "Zmenené zariadenie:" msgid "Device removed:" msgstr "Odstránené zariadenie:" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Hotovo!" @@ -128,6 +129,10 @@ msgstr "Démon aktualizácie firmvéru" msgid "Firmware Utility" msgstr "Nástroj pre firmvéry" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Nájdené" + #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Získa všetky zariadenia, ktoré podporujú aktualizovanie firmvéru" @@ -171,6 +176,10 @@ msgstr "Sleduje démona kvôli udalostiam" msgid "No hardware detected with firmware update capability" msgstr "Nezistil sa žiadny hardvér s možnosťou aktualizácie firmvéru" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + #. TRANSLATORS: command description msgid "Read firmware from device into a file" msgstr "Prečíta firmvér zo zariadenia do súboru" @@ -220,6 +229,10 @@ msgstr "Aktualizuje všetok firmvér na najnovšiu dostupnú verziu" msgid "Updating %s from %s to %s... " msgstr "Aktualizuje sa %s z verzie %s na verziu %s... " +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Verzia" + #. TRANSLATORS: command description msgid "Watch DFU devices being hotplugged" msgstr "Sleduje pripojenie zariadení DFU" diff --git a/po/sr.po b/po/sr.po index 96b5443a3..8e8feb4d3 100644 --- a/po/sr.po +++ b/po/sr.po @@ -159,6 +159,7 @@ msgstr "Не проверавај да ли је потребно поновно msgid "Do not check for unreported history" msgstr "Не проверавај непослати историјат" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Урађено!" @@ -182,6 +183,8 @@ msgstr "Преузимам…" msgid "Dump SMBIOS data from a file" msgstr "Ишчитај SMBIOS податке из датотеке" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Омогућено" @@ -210,22 +213,6 @@ msgstr "Нисам могао да учитам ћефове" msgid "Failed to parse arguments" msgstr "Не могу да обрадим аргументе" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Добављам датотеку" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Добављам фирмвер" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Добављам мета-податке" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Добављам потпис" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "Назив датотеке" @@ -258,6 +245,10 @@ msgstr[0] "Метаподаци фирмвера нису ажурирани %u msgstr[1] "Метаподаци фирмвера нису ажурирани %u дана и можда су застарели." msgstr[2] "Метаподаци фирмвера нису ажурирани %u дана и можда су застарели." +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Нашао" + #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Добави све уређаје који подржавају ажурирање фирмвера" @@ -335,9 +326,9 @@ msgstr "Прати демона за догађајима" msgid "No hardware detected with firmware update capability" msgstr "Нема хардвера којем се може ажурирати фирмвер" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Превазилази упозорења прикључка" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "У реду" #. TRANSLATORS: remote filename base msgid "Password" @@ -487,10 +478,6 @@ msgstr "Ажурира сав фирмвер на последња доступ msgid "Updating %s from %s to %s... " msgstr "Ажурирам %s са %s на %s..." -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Отпремна порука:" - #. TRANSLATORS: ask the user to upload msgid "Upload report now?" msgstr "Отпремити извештај сада?" @@ -503,6 +490,10 @@ msgstr "Корисничко име" msgid "Verifying…" msgstr "Проверавам…" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Издање" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Чекам…" diff --git a/po/sv.po b/po/sv.po index 2ff399a16..772350cb2 100644 --- a/po/sv.po +++ b/po/sv.po @@ -3,10 +3,11 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Anders Jonsson , 2017,2019 +# Anders Jonsson , 2017,2019-2020 # Andreas Henriksson , 2017 # Josef Andersson , 2015,2017-2018 # Josef Andersson , 2015,2017 +# Luna Jernberg , 2020 # Sebastian Rasmussen , 2019-2020 # Sebastian Rasmussen , 2018 msgid "" @@ -27,24 +28,36 @@ msgid_plural "%.0f minutes remaining" msgstr[0] "%.0fminut kvarstår" msgstr[1] "%.0f minuter kvarstår" +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s CPU-mikrokodsuppdatering" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s Konfigurationsuppdatering" + #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s Consumer ME Update" -msgstr "%s ME-uppdatering för konsument" +msgstr "%s Consumer ME-uppdatering" #. TRANSLATORS: the controller is a device that has other devices #. * plugged into it, for example ThunderBolt, FireWire or USB, #. * the first %s is the device name, e.g. 'Intel ThunderBolt` #, c-format msgid "%s Controller Update" -msgstr "%s Kontrolleruppdatering" +msgstr "%s-styrenhetsuppdatering" #. TRANSLATORS: ME stands for Management Engine (with Intel AMT), #. * where the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s Corporate ME Update" -msgstr "%s ME-uppdatering för företag" +msgstr "%s Corporate ME-uppdatering" #. TRANSLATORS: a specific part of hardware, #. * the first %s is the device name, e.g. 'Unifying Receiver` @@ -56,7 +69,7 @@ msgstr "%s Enhetsuppdatering" #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s Embedded Controller Update" -msgstr "%s Uppdatering av inbäddadkontroller" +msgstr "%s inbäddad styrenhetsuppdatering" #. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, #. * the first %s is the device name, e.g. 'ThinkPad P50` @@ -75,7 +88,7 @@ msgstr "%s Systemuppdatering" #. * the first %s is the system name, e.g. 'ThinkPad P50` #, c-format msgid "%s Thunderbolt Controller Update" -msgstr "Thunderbolt-kontrolleruppdatering för %s" +msgstr "%s Thunderbolt-styrenhetsuppdatering" #. TRANSLATORS: this is the fallback where we don't know if the release #. * is updating the system, the device, or a device class, or something else @@ -100,6 +113,16 @@ msgstr "%s måste förbli anslutna under tiden som uppgraderingen pågår för a msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." msgstr "%s måste förbli ansluten till en strömkälla under tiden som uppgraderingen pågår för att undvika skada." +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s version" + #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -141,6 +164,10 @@ msgid_plural "%u seconds" msgstr[0] "%u sekund" msgstr[1] "%u sekunder" +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(föråldrad)" + #. TRANSLATORS: command description msgid "Activate devices" msgstr "Aktivera enheter" @@ -201,6 +228,14 @@ msgstr "Svara ja på alla frågor" msgid "Apply firmware updates" msgstr "Tillämpa uppdateringar för fast programvara" +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Tillämpa uppdateringsfiler" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Tillämpar uppdatering…" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "Approved firmware:" @@ -208,6 +243,10 @@ msgid_plural "Approved firmware:" msgstr[0] "Godkänd fast programvara:" msgstr[1] "Godkänd fast programvara:" +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Fråga igen nästa gång?" + #. TRANSLATORS: command description msgid "Attach to firmware mode" msgstr "Fäst till fast programvaruläge" @@ -264,10 +303,26 @@ msgstr "Autentisering krävs för att uppdatera den lagrade kontrollsumman för msgid "Automatic Reporting" msgstr "Automatisk rapportering" +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Blockerad fast programvaru fil:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blockera fast programvara: " + #. TRANSLATORS: firmware version of bootloader msgid "Bootloader Version" msgstr "Starthanterarversion" +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Gren" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Bygg en fast programvaru fil" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Bygg fast programvara med en sandlåda" @@ -293,6 +348,11 @@ msgstr "Kontrollerar att kryptografisk hash matchar fastprogramvara" msgid "Checksum" msgstr "Kontrollsumma" +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Välj en gren:" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Välj en enhet:" @@ -305,6 +365,10 @@ msgstr "Välj en typ av fastprogravara:" msgid "Choose a release:" msgstr "Välj en utgåva:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Välj en volym:" + #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" msgstr "Rensa eventuella uppdateringar som schemalagts att bli tillämpade frånkopplade" @@ -321,6 +385,18 @@ msgstr "Kommandot hittades inte" msgid "Continue with update?" msgstr "Fortsätta med uppdatering?" +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Konvertera en fast programvaru fil" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Skapad" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Kritisk" + #. TRANSLATORS: Device supports some form of checksum verification msgid "Cryptographic hash verification is available" msgstr "Kryptografiskt hashverifiering är tillgänglig" @@ -413,6 +489,16 @@ msgstr "Enheter som har uppdaterats:" msgid "Devices that were not updated correctly:" msgstr "Enheter som inte uppdaterades korrekt:" +#. TRANSLATORS: message letting the user know no device upgrade available due +#. to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Enheter med inga tillgängliga uppdateringar för fast programvara" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Inaktiverad" + msgid "Disabled fwupdate debugging" msgstr "Inaktiverade fwupdate-felsökning" @@ -462,6 +548,7 @@ msgstr[1] "Skicka inte rapporter, och fråga aldrig om att skicka rapporter vid msgid "Do not write to the history database" msgstr "Skriv inte till historikdatabasen" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Klar!" @@ -506,6 +593,8 @@ msgstr "Aktivera stöd för uppdatering av fast programvara på system som stöd msgid "Enable this remote?" msgstr "Aktivera denna fjärr?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Aktiverad" @@ -524,6 +613,14 @@ msgstr "Att aktivera denna funktionalitet görs på egen risk, vilket betyder at msgid "Enabling this remote is done at your own risk." msgstr "Att aktivera denna fjärr görs på egen risk." +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Krypterad" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Krypterat RAM" + #. TRANSLATORS: command description msgid "Erase all firmware update history" msgstr "Ta bort alla historik för fast programvara" @@ -540,14 +637,18 @@ msgstr "Avsluta efter en kort fördröjning" msgid "Exit after the engine has loaded" msgstr "Avsluta efter att motorn har lästs in" +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Misslyckades" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Misslyckades med att tillämpa uppdatering" + #. TRANSLATORS: we could not talk to the fwupd daemon msgid "Failed to connect to daemon" msgstr "Misslyckades med att ansluta till demon" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Misslyckades med hämtning på grund av servergräns" - #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" msgstr "Misslyckades med att hämta väntande enheter" @@ -556,6 +657,11 @@ msgstr "Misslyckades med att hämta väntande enheter" msgid "Failed to install firmware update" msgstr "Misslyckades med att installera uppdatering av fast programvara" +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Misslyckades med att läsa in lokal dbx" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Misslyckades med att läsa in speciallösning" @@ -580,22 +686,6 @@ msgstr "Misslyckades med att starta om" msgid "Failed to set splash mode" msgstr "Misslyckades med att sätta uppstartsläge" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Hämtar fil" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Hämtar fast programvara" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Hämtar metainformation" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Hämtar signatur" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "Filnamn" @@ -604,6 +694,10 @@ msgstr "Filnamn" msgid "Filename Signature" msgstr "Filnamn Signatur" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Filnamn krävs" + #. TRANSLATORS: command line option msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" msgstr "Filtrera via en uppsättning av enhetsflaggor genom att använda ett ~-prefix för att exkludera, t.e.x ”internal,~needs-reboot”" @@ -628,6 +722,10 @@ msgstr "Uppgraderingsdemon för fast programvara" msgid "Firmware Utility" msgstr "Fast programvaruverktyg" +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "fast programvara är redan blockerad" + #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format msgid "Firmware metadata has not been updated for %u day and may not be up to date." @@ -635,12 +733,17 @@ msgid_plural "Firmware metadata has not been updated for %u days and may not be msgstr[0] "Metadata för fast programvara har inte uppdaterats på %udag och kan vara utdaterad." msgstr[1] "Metadata för fast programvara har inte uppdaterats på %udagar och kan vara utdaterad." +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Fastprogramvaru uppdateringar" + msgid "Firmware updates are not supported on this machine." msgstr "Uppdateringar av fast programvara stöds inte på denna maskin." msgid "Firmware updates are supported on this machine." msgstr "Uppdateringar av fast programvara stöds på denna maskin." +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Flaggor" @@ -648,6 +751,10 @@ msgstr "Flaggor" msgid "Force the action ignoring all warnings" msgstr "Tvinga åtgärden, ignorera alla varningar" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Hittad" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -679,8 +786,12 @@ msgid "Gets the configured remotes" msgstr "Ger de konfigurerade fjärrkällorna" #. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Hämtar listan över godkänd fast programvara." +msgid "Gets the list of approved firmware" +msgstr "Hämtar lista över godkänd fast programvara." + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Hämtar lista över blockerad fast programvara." #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -698,6 +809,15 @@ msgstr "Hämtar resultaten från senaste uppdateringen" msgid "Hardware is waiting to be replugged" msgstr "Hårdvara väntar på att bli utdragen/återinsatt" +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Hög" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Väntar…" @@ -754,10 +874,32 @@ msgstr "Installerar uppdatering för fast programvara…" msgid "Installing on %s…" msgstr "Installerar på %s…" +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET Aktiv" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET Aktiverad" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + #. TRANSLATORS: Device cannot be removed easily msgid "Internal device" msgstr "Intern enhet" +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Ogiltig" + #. TRANSLATORS: Is currently in bootloader mode msgid "Is in bootloader mode" msgstr "Är i starthanterarläge" @@ -789,6 +931,18 @@ msgstr "Linux Vendor Firmware Service (stabil fastprogramvara)" msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Linux Vendor Firmware Service (fastprogramvara för testning)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux kärna" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux swap" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Lista poster i dbx" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "Lista uppdateringar för fast programvara som stöds" @@ -801,9 +955,16 @@ msgstr "Lista tillgänliga fastprogramvutyper" msgid "Loading…" msgstr "Läser in…" -#. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Vitlista manuellt specifika insticksmoduler" +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Låst" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Låg" + +msgid "MEI version" +msgstr "MEI version" #. TRANSLATORS: remote URI msgid "Metadata Signature" @@ -826,10 +987,6 @@ msgstr "Minimiversion" msgid "Mismatched daemon and client, use %s instead" msgstr "Demon och klient stämmer inte, använd %s istället" -#. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Modifierar ett konfigurationsvärde för en demon." - #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Modifierar en given fjärrkälla" @@ -856,6 +1013,7 @@ msgstr "Kräver en nedstängning efter installation" msgid "New version" msgstr "Ny version" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Ingen åtgärd angiven!" @@ -893,14 +1051,22 @@ msgstr "Inga fjärrarkiv tillgängliga" msgid "No updates were applied" msgstr "Inga uppdateringar applicerades" +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Hittades inte" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Stöds inte" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Visa endast enkelt PCR-värde" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Åsidosätt insticksmodulvarning" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Åsidosätt standardsökväg för ESP" @@ -984,8 +1150,12 @@ msgid "Refresh metadata from remote server" msgstr "Uppdatera metadata från fjärrserver" #. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "Återinstallera aktuell fastprogrmvara på enheten." +msgid "Reinstall current firmware on the device" +msgstr "Återinstallera aktuell fast programvara på enheten." + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Installera om fast programvara på en enhet" #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second is a version number @@ -1015,10 +1185,6 @@ msgstr "Rapport-uri" msgid "Reported to remote server" msgstr "Rapporterade till fjärrserver" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Kräver AC-ström" - #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" msgstr "Kräver en starthanterare" @@ -1054,6 +1220,14 @@ msgstr "Kör uppstädningssammansättningsrutinen för insticksmodule när insta msgid "Run the plugin composite prepare routine when using install-blob" msgstr "Kör förberedelsesammansättningsrutinen för insticksmodule när install-blob används" +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI lås" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI skriv" + #. TRANSLATORS: command line option msgid "Save device state into a JSON file between executions" msgstr "Spara enhetstillstånd i en JSON-fil mellan körningar" @@ -1070,6 +1244,10 @@ msgstr "Schemalägger…" msgid "Selected device" msgstr "Vald enhet" +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Vald volym" + #. TRANSLATORS: serial number of hardware msgid "Serial Number" msgstr "Serienummer" @@ -1078,17 +1256,18 @@ msgstr "Serienummer" msgid "Set the debugging flag during update" msgstr "Ställ in felsökningsflaggan under uppdatering" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Sätter listan av godkända fasta programvaror" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Sätter listan över godkänd fast programvara." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Dela fast programvaruhistorik med utvecklarna" +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Visa alla resultat" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "Visa klient- och demon-version" @@ -1121,6 +1300,10 @@ msgstr "Visa historik över fasta programvaruuppdateringar" msgid "Show plugin verbose information" msgstr "Visa utförlig information om insticksmodul" +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Visa den beräknade versionen av dbx" + #. TRANSLATORS: command line option msgid "Show the debug log from the last attempted update" msgstr "Visa felsökningsloggen från det senaste uppdateringsförsöket" @@ -1215,10 +1398,22 @@ msgstr "Verifierade framgångsrikt enhetskontrollsummor" msgid "Summary" msgstr "Sammanfattning" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Stöds" + #. TRANSLATORS: Is found in current metadata msgid "Supported on remote server" msgstr "Stöds på fjärrserver" +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Systemet kräver extern strömkälla" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + msgid "Target" msgstr "Mål" @@ -1226,6 +1421,10 @@ msgstr "Mål" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "LVFS är en fri tjänst som fungerar som en oberoende juridisk person och har ingen koppling till $OS_RELEASE:NAME$. Din distributör kanske inte har bekräftat att någon av de fasta programvaruuppdateringarna är kompatibla med ditt system eller anslutna enheter. All fast programvara tillhandahålls endast av den ursprungliga tillverkaren av utrustning." +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Det finns inga blockerade fast programvaru filer" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "There is no approved firmware." @@ -1238,6 +1437,10 @@ msgstr "Detta program kommer endast fungera korrekt som root" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "Denna fjärrkälla innehåller fast programvara som inte är under embargo, men fortfarande testas av hårdvarutillverkare. Du bör säkerställa att du har ett sätt att manuellt nedgradera den fasta programvaran om uppdateringen misslyckas." +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Detta system har en låg HSI-säkerhetsnivå." + #. TRANSLATORS: the user needs to stop playing with stuff msgid "This tool can only be used by the root user" msgstr "Detta verktyg kan endast användas av administratörsanvändaren" @@ -1250,6 +1453,22 @@ msgstr "Typ" msgid "UEFI Firmware Utility" msgstr "Verktyg för fast UEFI-programvara" +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx-verktyg" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Misslyckades med att ansluta till tjänst" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Avblockera fast programvara:" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Okrypterad" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -1263,6 +1482,10 @@ msgstr "Okänd enhet" msgid "Unlock the device to allow access" msgstr "Lås upp enheten för att tillåta åtkomst" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Upplåst" + #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" msgstr "Låser upp enheten för fast programvaruåtkomst" @@ -1324,6 +1547,9 @@ msgstr "Uppdatera lagrad metadata med aktuellt innehåll" msgid "Updates all firmware to latest versions available" msgstr "Uppdaterar all fast programvara till de senast tillgängliga versionerna" +msgid "Updating" +msgstr "Uppdaterar" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" @@ -1342,10 +1568,6 @@ msgstr "Uppdaterar %s…" msgid "Upgrade available for %s from %s to %s" msgstr "Uppgradering tillgänglig för %s från %s till %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Uppladdningsmeddelande:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Skicka rapport endast denna gång, men fråga igen vid framtida uppdateringar" @@ -1364,6 +1586,14 @@ msgstr[1] "Skicka rapporter och skicka dem automatiskt efter att framtida uppdat msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "Att skicka upp rapporter för fastprogramvara hjälper hårdvaruförsäljare att snabbt identifiera trasiga och fungerande uppdateringar på riktiga enheter." +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Använd fwupdmgr --help för hjälp" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Använd fwupdtool --help för hjälp" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "Använd specialflaggor vid installation av fastprogramvara" @@ -1376,6 +1606,10 @@ msgstr "Användare har aviserats" msgid "Username" msgstr "Användarnamn" +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Giltig" + #. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') msgid "Variant" msgstr "Variant" @@ -1388,9 +1622,13 @@ msgstr "Tillverkare" msgid "Verifying…" msgstr "Verifierar…" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "VARNING: Ignorerar strikta SSL-kontroller, för att göra detta automatiskt i framtiden, expotera DISABLE_SSL_STRICT i din miljö" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "VARNING:" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1412,6 +1650,10 @@ msgstr "Skriv fast programvara från fil till enhet" msgid "Write firmware from file into one partition" msgstr "Skriv fast programvara från fil till partition" +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Skriver fil:" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Skriver…" @@ -1419,3 +1661,7 @@ msgstr "Skriver…" #. TRANSLATORS: show the user a warning msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." msgstr "Din distributör kanske inte har verifierat någon av uppdateringarna av fastprogramvara för kompatibilitet med ditt system eller anslutna enheter." + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "standard" diff --git a/po/tr.po b/po/tr.po index d74be8f4b..fb6d78b0c 100644 --- a/po/tr.po +++ b/po/tr.po @@ -5,7 +5,7 @@ # Translators: # Emin Tufan , 2020 # Muhammet Kara , 2016 -# Sabri Ünal , 2019 +# Sabri Ünal , 2019-2020 # Sabri Ünal , 2020 # Serdar Sağlam , 2020 msgid "" @@ -284,6 +284,10 @@ msgstr "Komut bulunamadı" msgid "Continue with update?" msgstr "Güncellemeye devam edilsin mi?" +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Kritik" + #. TRANSLATORS: version number of current firmware msgid "Current version" msgstr "Var olan sürüm" @@ -367,6 +371,7 @@ msgstr "Cihaz güvenlik kontrolleri yapma" msgid "Do not write to the history database" msgstr "Geçmişi veritabanına yazma" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Tamamlandı!" @@ -407,6 +412,8 @@ msgstr "Desteklenen sistemlerde ürün yazılımı güncelleme desteğini etkinl msgid "Enable this remote?" msgstr "Bu uzaktan kumanda etkinleştirilsin mi?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Etkinleştirildi" @@ -434,10 +441,6 @@ msgstr "Küçük bir gecikme sonrası çık" msgid "Failed to connect to daemon" msgstr "Artalan sürecine bağlanamadı" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Sunucu sınırı nedeniyle indirilemedi" - #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" msgstr "Bekleyen aygıtlar alınamadı" @@ -466,22 +469,6 @@ msgstr "Yeniden başlatılamadı" msgid "Failed to set splash mode" msgstr "Sıçrama kipi ayarlanamadı" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Dosya alınıyor" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Ürün yazılımı alınıyor" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Üstveri alınıyor" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "İmza alınıyor" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "Dosya adı" @@ -516,6 +503,7 @@ msgstr "Ürün yazılımı güncellemeleri bu makinede desteklenmiyor." msgid "Firmware updates are supported on this machine." msgstr "Ürün yazılımı güncellemeleri bu makinede destekleniyor." +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Bayraklar" @@ -523,6 +511,10 @@ msgstr "Bayraklar" msgid "Force the action ignoring all warnings" msgstr "Eylemi tüm uyarıları görmezden gelmeye zorla" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Bulundu" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -545,10 +537,6 @@ msgstr "Etkinleştirilmiş tüm eklentileri sisteme kaydettir" msgid "Gets details about a firmware file" msgstr "Ürün yazılımı dosyasıyla ilgili ayrıntıları al" -#. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Onaylı ürün yazılımı listesini getirir." - #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Bağlı donanım için güncelleme listesini al" @@ -565,6 +553,10 @@ msgstr "Son güncellemeden sonuçları alır" msgid "Hardware is waiting to be replugged" msgstr "Donanım yeniden takılmayı bekliyor" +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Yüksek" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Boşta…" @@ -648,9 +640,13 @@ msgstr "Kullanılabilir ürün yazılımı türlerini listele" msgid "Loading…" msgstr "Yükleniyor…" -#. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "El ile beyaz listeye özel eklentiler" +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Düşük" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Orta" #. TRANSLATORS: remote URI msgid "Metadata Signature" @@ -673,10 +669,6 @@ msgstr "Asgari Sürüm" msgid "Mismatched daemon and client, use %s instead" msgstr "Artalan süreci ile istemci uyuşmadı, bunun yerine %s kullanın" -#. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Artalan süreci yapılandırma değerini değiştir." - msgid "Modify a configured remote" msgstr "Uzak yapılandırmayı değiştir" @@ -691,6 +683,7 @@ msgstr "Olaylar için artalan sürecini gözetle" msgid "New version" msgstr "Yeni sürüm" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Eylem belirtilmedi!" @@ -722,14 +715,14 @@ msgstr "Uzaktan kumanda bulunamadı" msgid "No updates were applied" msgstr "Güncelleme uygulanmadı" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Tamam" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Yalnızca tek PCR değerini göster" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Eklenti uyarısını geçersiz yap" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Öntanımlı ESP yolunu geçersiz kıl" @@ -818,10 +811,6 @@ msgstr "Rapor URI" msgid "Reported to remote server" msgstr "Uzak sunucuya bildirildi" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "AC güç gerekli" - #. TRANSLATORS: metadata is downloaded from the Internet msgid "Requires internet connection" msgstr "İnternet bağlantısı gerektirir" @@ -858,13 +847,10 @@ msgstr "Seri Numarası" msgid "Set the debugging flag during update" msgstr "Güncelleme sırasında hata ayıklama bayrağını ayarla" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Onaylı ürün yazılımı listesini ayarlar" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Onaylı ürün yazılımı listesini ayarlar." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Ürün yazılım geçmişini geliştiricilerle paylaş" @@ -1073,17 +1059,13 @@ msgstr "Tüm ürün yazılımlarını mevcut en son sürümlere güncelle" msgid "Updating %s…" msgstr "Güncelleniyor %s…" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "İleti yükle:" - #. TRANSLATORS: ask the user to upload msgid "Upload report now?" msgstr "Rapor şimdi karşıya yüklensin mi?" #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" -msgstr "ürün yazılımı kurulurken quirk bayraklarını kullan" +msgstr "Ürün yazılımı kurulurken quirk bayraklarını kullan" #. TRANSLATORS: User has been notified msgid "User has been notified" @@ -1105,6 +1087,10 @@ msgstr "Üretici" msgid "Verifying…" msgstr "Doğrulanıyor…" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Sürüm" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Bekliyor…" @@ -1136,16 +1122,3 @@ msgstr "Hizmet sağlayıcınız, sisteminizle veya bağlı aygıtlarla uyumluluk #. TRANSLATORS: program name msgid "fwupd TPM event log utility" msgstr "fwupd TPM olay günlüğü yardımcı aracı" - -#. TRANSLATORS: message letting the user know no device upgrade available due -#. to missing on LVFS -#. * %1 is the device name -#, c-format -msgid "• %s has no available firmware updates" -msgstr "• %s için mevcut ürün yazılımı güncellemesi yok" - -#. TRANSLATORS: message letting the user know no device upgrade available -#. * %1 is the device name -#, c-format -msgid "• %s has the latest available firmware version" -msgstr "• %s en son ürün yazılımı sürümüne sahip" diff --git a/po/uk.po b/po/uk.po index de9227402..2c02c358a 100644 --- a/po/uk.po +++ b/po/uk.po @@ -30,6 +30,12 @@ msgstr[3] "Лишилася %.0f хвилина" msgid "%s CPU Microcode Update" msgstr "Оновлення мікропрограми процесора %s" +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Оновлення налаштувань %s" + #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format @@ -93,6 +99,11 @@ msgstr "Оновлення для %s" msgid "%s and all connected devices may not be usable while updating." msgstr "%s і усі з'єднані пристрою можуть бути недоступними протягом оновлення." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Режим виробництва %s" + #. TRANSLATORS: warn the user before updating, %1 is a device name #, c-format msgid "%s must remain connected for the duration of the update to avoid damage." @@ -103,6 +114,21 @@ msgstr "Для уникнення пошкоджень %s має лишатис msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." msgstr "Для уникнення пошкоджень %s має лишатися з'єднаним із джерелом живлення протягом оновлення." +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "Перевизначення %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s, версія %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Версія %s" + #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -156,6 +182,10 @@ msgstr[1] "%u секунди" msgstr[2] "%u секунд" msgstr[3] "%u секунда" +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(є застарілим)" + #. TRANSLATORS: command description msgid "Activate devices" msgstr "Задіяти пристрої" @@ -200,6 +230,10 @@ msgstr "Дозволити зниження версій мікропрогра msgid "Allow reinstalling existing firmware versions" msgstr "Дозволити перевстановлення наявних версій мікропрограми" +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Дозволити перемикання гілок мікропрограми" + #. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Для завершення оновлення слід перезавантажити систему." @@ -216,6 +250,18 @@ msgstr "Відповідати «так» на усі питання" msgid "Apply firmware updates" msgstr "Застосувати оновлення мікропрограми" +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Застосувати оновлення, навіть якщо воно не є рекомендованим" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Застосувати файли оновлень" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Застосовуємо оновлення…" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "Approved firmware:" @@ -225,6 +271,10 @@ msgstr[1] "Схвалені мікропрограми:" msgstr[2] "Схвалені мікропрограми:" msgstr[3] "Схвалена мікропрограма:" +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Питати знову наступного разу?" + #. TRANSLATORS: command description msgid "Attach to firmware mode" msgstr "Долучитися до режиму мікропрограми" @@ -281,10 +331,38 @@ msgstr "Щоб отримати доступ до оновлення збере msgid "Automatic Reporting" msgstr "Автоматичне звітування" +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Автоматично вивантажувати кожного разу?" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Пов'язати новий драйвер ядра" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Заблоковані файли мікропрограм:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Блокуємо мікропрограму:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Блокує встановлення певної мікропрограми" + #. TRANSLATORS: firmware version of bootloader msgid "Bootloader Version" msgstr "Версія завантажувача" +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Гілка" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Зібрати файл мікропрограми" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Зібрати мікропрограму за допомогою пісочниці" @@ -297,6 +375,14 @@ msgstr "Скасувати" msgid "Cancelled" msgstr "Скасовано" +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Не вдалося застосувати, оскільки оновлення dbx вже застосовано." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Не можна застосовувати оновлення до портативного носія" + #. TRANSLATORS: this is when a device is hotplugged #. TRANSLATORS: this is when the daemon state changes msgid "Changed" @@ -310,6 +396,11 @@ msgstr "Перевірити відповідність криптографіч msgid "Checksum" msgstr "Контрольна сума" +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Виберіть гілку:" + #. TRANSLATORS: get interactive prompt msgid "Choose a device:" msgstr "Виберіть пристрій:" @@ -322,6 +413,10 @@ msgstr "Виберіть тип мікропрограми:" msgid "Choose a release:" msgstr "Виберіть випуск:" +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Виберіть том:" + #. TRANSLATORS: command description msgid "Clears any updates scheduled to be updated offline" msgstr "Спорожняє список усіх оновлень, які заплановано для автономного режиму" @@ -426,10 +521,18 @@ msgstr "Вилучено пристрій:" msgid "Device stages updates" msgstr "Пристрій із покроковим оновленням" +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "У пристрої передбачено підтримку перемикання на іншу гілку мікропрограми" + #. TRANSLATORS: Device update needs to be separately activated msgid "Device update needs activation" msgstr "Оновлення пристрою потребує активації" +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Пристрій створить резервну копію мікропрограми перед встановленням" + #. TRANSLATORS: Device will not return after update completes msgid "Device will not re-appear after update completes" msgstr "Пристрій не з'явиться у списку пристроїв після завершення оновлення" @@ -442,6 +545,20 @@ msgstr "Пристрої, для яких вдалося успішно онов msgid "Devices that were not updated correctly:" msgstr "Пристрої, для яких не вдалося оновити дані належним чином:" +#. TRANSLATORS: message letting the user know no device upgrade available due +#. to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Пристрої, для яких немає оновлень мікропрограми:" + +#. TRANSLATORS: message letting the user know no device upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "Пристрої із найсвіжішою доступною версією мікропрограми:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Вимкнено" + msgid "Disabled fwupdate debugging" msgstr "Вимкнено діагностику fwupdate" @@ -495,6 +612,11 @@ msgstr[3] "Не вивантажувати звіти цього разу і н msgid "Do not write to the history database" msgstr "Не записувати дані до бази даних журналу" +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Чи розумієте ви наслідки зміни гілки мікропрограми?" + +#. TRANSLATORS: success #. success msgid "Done!" msgstr "Виконано!" @@ -539,6 +661,8 @@ msgstr "Увімкнути підтримку оновлення мікропр msgid "Enable this remote?" msgstr "Увімкнути це віддалене сховище?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Увімкнено" @@ -557,6 +681,14 @@ msgstr "Наслідки вмикання цієї можливості покл msgid "Enabling this remote is done at your own risk." msgstr "Вмикання цього віддаленого сховища виконано під вашу відповідальність." +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Зашировано" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Шифрована пам'ять" + #. TRANSLATORS: command description msgid "Erase all firmware update history" msgstr "Витерти увесь журнал оновлень мікропрограми" @@ -573,13 +705,25 @@ msgstr "Завершити роботу з невеличкою затримко msgid "Exit after the engine has loaded" msgstr "Завершити роботу після завантаження рушія" +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Видобувати бінарну мікропрограму до образів" + +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Failed" +msgstr "Помилка" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Не вдалося застосувати оновлення" + #. TRANSLATORS: we could not talk to the fwupd daemon msgid "Failed to connect to daemon" msgstr "Не вдалося встановити з'єднання із фоновою службою" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "Не вдалося отримати через обмеження сервера" +#. TRANSLATORS: could not parse file +msgid "Failed to extract local dbx " +msgstr "Не вдалося видобути локальний dbx" #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" @@ -589,10 +733,19 @@ msgstr "Не вдалося отримати список пристроїв у msgid "Failed to install firmware update" msgstr "Не вдалося встановити оновлення мікропрограми" +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Не вдалося завантажити локальний dbx" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Не вдалося завантажити коригування" +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Не вдалося завантажити системний dbx" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "Не вдалося обробити аргументи" @@ -605,6 +758,10 @@ msgstr "Не вдалося обробити файл" msgid "Failed to parse flags for --filter" msgstr "Не вдалося обробити прапорці до --filter" +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Не вдалося обробити локальний dbx" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Не вдалося перезавантажити" @@ -613,21 +770,10 @@ msgstr "Не вдалося перезавантажити" msgid "Failed to set splash mode" msgstr "Не вдалося встановити режим вітання" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "Отримуємо файл" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "Отримуємо мікропрограму" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "Отримуємо метадані" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "Отримуємо підпис" +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Не вдалося встановити чинність даних ESP" #. TRANSLATORS: filename of the local file msgid "Filename" @@ -637,6 +783,10 @@ msgstr "Назва файла" msgid "Filename Signature" msgstr "Підпис назви файла" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Слід вказати назву файла" + #. TRANSLATORS: command line option msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" msgstr "Фільтрувати за набором прапорців пристроїв за допомогою префікса виключення ~, наприклад «internal,~needs-reboot»" @@ -661,6 +811,22 @@ msgstr "Служба оновлення мікропрограми" msgid "Firmware Utility" msgstr "Засіб роботи з мікропрограмами" +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Засвідчення мікропрограми" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "Firmware can not be updated in legacy BIOS mode" +msgstr "У режимі сумісності із застарілим BIOS не можна оновити мікропрограму" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Мікропрограму вже заблоковано" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Мікропрограму ще не заблоковано" + #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format msgid "Firmware metadata has not been updated for %u day and may not be up to date." @@ -670,19 +836,36 @@ msgstr[1] "Метадані мікропрограми не оновлювали msgstr[2] "Метадані мікропрограми не оновлювалися %u днів, можливо, вони вже не є актуальними." msgstr[3] "Метадані мікропрограми не оновлювалися %u днів, можливо, вони вже не є актуальними." +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Оновлення мікропрограми" + msgid "Firmware updates are not supported on this machine." msgstr "На цьому комп'ютері не передбачено підтримки оновлення мікропрограми." msgid "Firmware updates are supported on this machine." msgstr "На цьому комп'ютері передбачено підтримку оновлень мікропрограми." +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Оновлення мікропрограми вимкнено; віддайте команду 'fwupdmgr unlock', щоб їх увімкнути" + +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "Прапорці" +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Виконати дію примусово шляхом послаблення деяких перевірок під час виконання" + msgid "Force the action ignoring all warnings" msgstr "Виконати дію примусово, ігноруючи усі попередження" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Знайдено" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -715,9 +898,17 @@ msgstr "Отримати параметри файла мікропрограм msgid "Gets the configured remotes" msgstr "Отримує налаштовані віддалені пристрої" +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Отримує атрибути захисту основної системи" + #. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "Отримує список схвалених мікропрограм." +msgid "Gets the list of approved firmware" +msgstr "Отримує список схвалених мікропрограм" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Отримує список заблокованих мікропрограм" #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -739,6 +930,15 @@ msgstr "Обладнання очікує на повторне з'єднанн msgid "High" msgstr "Високий" +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Ід. захисту основної системи:" + +#. TRANSLATORS: Title: +#. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + #. TRANSLATORS: daemon is inactive msgid "Idle…" msgstr "Бездіяльність…" @@ -747,10 +947,26 @@ msgstr "Бездіяльність…" msgid "Ignore SSL strict checks when downloading files" msgstr "Ігнорувати результати строгої перевірки SSL під час отримання файлів" +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ігнорувати помилки при перевірці контрольної суми мікропрограми" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ігнорувати помилки, пов'язані із невідповідністю обладнання мікропрограмі" + +#. TRANSLATORS: command line option +msgid "Ignore requirement of external power source" +msgstr "Ігнорувати вимогу щодо наявності зовнішнього джерела живлення" + #. TRANSLATORS: Ignore validation safety checks when flashing this device msgid "Ignore validation safety checks" msgstr "Ігнорувати перевірки безпечності" +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ігноруємо строгі перевірки SSL. Щоб зробити ігнорування у майбутньому автоматичним, експортуйте до вашого середовища змінну DISABLE_SSL_STRICT" + #. TRANSLATORS: length of time the update takes to apply msgid "Install Duration" msgstr "Тривалість встановлення" @@ -795,10 +1011,57 @@ msgstr "Встановлюємо оновлення мікропрограми msgid "Installing on %s…" msgstr "Встановлюємо на %s…" +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard захищено ACM" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "OTP FUSE Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Правила обробки помилок Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Перевірене завантаження Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Активна Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Увімкнено Intel CET" + +#. TRANSLATORS: Title: Direct Connect Interface (DCI) allows +#. * debugging of Intel processors using the USB3 port +msgid "Intel DCI debugger" +msgstr "Діагностика DCI Intel" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + #. TRANSLATORS: Device cannot be removed easily msgid "Internal device" msgstr "Внутрішній пристрій" +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Некоректна" + #. TRANSLATORS: Is currently in bootloader mode msgid "Is in bootloader mode" msgstr "Перебуває у режимі завантажувача" @@ -832,6 +1095,22 @@ msgstr "Служба надання мікропрограм для Linux від msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Служба надання мікропрограм для Linux від виробника (тестова мікропрограма)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Ядро Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Блокування ядра Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Рез. пам'ять Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Вивести список записів у dbx" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "Показати список підтримуваних оновлень мікропрограми" @@ -840,17 +1119,39 @@ msgstr "Показати список підтримуваних оновлен msgid "List the available firmware types" msgstr "Вивести список доступних типів мікропрограм" +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Виводить список файлів у ESP" + #. TRANSLATORS: parsing the firmware information msgid "Loading…" msgstr "Завантаження…" +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Заблоковано" + #. TRANSLATORS: the release urgency msgid "Low" msgstr "Низький" +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Режим виробництва MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Перевизначення MEI" + +msgid "MEI version" +msgstr "Версія MEI" + #. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "Вручну додати певні додатки до «білого» списку" +msgid "Manually enable specific plugins" +msgstr "Увімкнути певні додатки вручну" #. TRANSLATORS: the release urgency msgid "Medium" @@ -878,8 +1179,8 @@ msgid "Mismatched daemon and client, use %s instead" msgstr "Невідповідність фонової служби і клієнта, скористайтеся краще %s" #. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "Змінює значення налаштування фонової служби." +msgid "Modifies a daemon configuration value" +msgstr "Змінює значення налаштувань фонової служби" #. TRANSLATORS: command description msgid "Modifies a given remote" @@ -895,6 +1196,10 @@ msgstr "Зміна налаштувань фонової служби" msgid "Monitor the daemon for events" msgstr "Стежити за подіями у фоновій службі" +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Монтує ESP" + #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware msgid "Needs a reboot after installation" msgstr "Потребує перезавантаження після встановлення" @@ -907,6 +1212,7 @@ msgstr "Потребує вимикання після встановлення" msgid "New version" msgstr "Нова версія" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "Не вказано дії!" @@ -944,14 +1250,22 @@ msgstr "Немає доступних віддалених сховищ" msgid "No updates were applied" msgstr "Не застосовано жодного оновлення" +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Не знайдено" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Немає підтримки" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Гаразд" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Показувати лише одне значення PCR" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "Перевизначити попередження для додатка" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Перевизначити типовий шлях до ESP" @@ -964,6 +1278,14 @@ msgstr "Не зважати на попередження і примусово msgid "Parse and show details about a firmware file" msgstr "Обробити і показати параметри файла мікропрограми" +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Обробляємо оновлення dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Обробляємо системний dbx…" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Пароль" @@ -980,6 +1302,14 @@ msgstr "Відсоток виконання" msgid "Please enter a number from 0 to %u: " msgstr "Будь ласка, введіть число від 0 до %u: " +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Не встановлено залежності додатка" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Захист DMA до завантаження" + #. TRANSLATORS: version number of previous firmware msgid "Previous version" msgstr "Попередня версія" @@ -1035,8 +1365,8 @@ msgid "Refresh metadata from remote server" msgstr "Оновити метадані з віддаленого сервера" #. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "Повторно встановити поточну мікропрограму на пристрій." +msgid "Reinstall current firmware on the device" +msgstr "Повторно встановити мікропрограму на пристрій" #. TRANSLATORS: command description msgid "Reinstall firmware on a device" @@ -1070,9 +1400,13 @@ msgstr "Адреса звіту" msgid "Reported to remote server" msgstr "Повідомлено на віддаленому сервері" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "Потребує сталого живлення" +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Не знайдено відповідної файлової системи efivarfs" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Не знайдено відповідного обладнання" #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" @@ -1109,6 +1443,22 @@ msgstr "Запустити процедуру чищення композиці msgid "Run the plugin composite prepare routine when using install-blob" msgstr "Запустити процедуру приготування композиції додатків при використанні install-blob" +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Динамічний суфікс" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Регіон BIOS SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Блокування SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Запис SPI" + #. TRANSLATORS: command line option msgid "Save device state into a JSON file between executions" msgstr "Зберігати стан пристрою до файла JSON між виконаннями" @@ -1125,6 +1475,10 @@ msgstr "Плануємо…" msgid "Selected device" msgstr "Вибрано пристрій" +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Вибраний том" + #. TRANSLATORS: serial number of hardware msgid "Serial Number" msgstr "Серійний номер" @@ -1133,17 +1487,18 @@ msgstr "Серійний номер" msgid "Set the debugging flag during update" msgstr "Встановити під час оновлення прапорець діагностики" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Встановлення списку схвалених мікропрограм" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "Встановлює список схвалених мікропрограм." - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Поділитися журналом оновлень із розробниками" +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Показати усі результати" + #. TRANSLATORS: command line option msgid "Show client and daemon versions" msgstr "Вивести дані щодо версій клієнат і фонової служби" @@ -1176,6 +1531,10 @@ msgstr "Показати журнал оновлень мікропрограм msgid "Show plugin verbose information" msgstr "Показати докладні відомості щодо додатків" +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Вивести обчислену версію dbx" + #. TRANSLATORS: command line option msgid "Show the debug log from the last attempted update" msgstr "Показати діагностичний журнал щодо останньої спроби оновлення" @@ -1213,6 +1572,10 @@ msgstr "Джерело" msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Вказати ідентифікатори виробника/продукту пристрою DFU" +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Вказати файл бази даних dbx" + msgid "Specify the number of bytes per USB transfer" msgstr "Вказати кількість байтів на один пакет передавання даних USB" @@ -1272,10 +1635,42 @@ msgstr "Успішно перевірено контрольні суми при msgid "Summary" msgstr "Резюме" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Є підтримка" + #. TRANSLATORS: Is found in current metadata msgid "Supported on remote server" msgstr "Підтримуваний на віддаленому сервері" +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Присипляння бездіяльності" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Присипляння до пам'яті" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Перемкнути гілку мікропрограми на пристрої" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Система потребує зовнішнього джерела живлення" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Відновлення PCR0 TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM 2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Нечисте" + msgid "Target" msgstr "Ціль" @@ -1283,6 +1678,23 @@ msgstr "Ціль" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "LVFS є безкоштовною службою, яка працює як незалежна юридична одиниця і не має зв'язку з $OS_RELEASE:NAME$. Розробники вашої операційної системи, можливо, не перевіряли жодні з цих оновлень на сумісність із системою або з'єднаними із комп'ютером пристроями. Усі мікропрограми надаються лише самими виробниками обладнання." +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "PCR0 TPM відрізняється від реконструкції." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Ця фонова служба завантажила сторонній код — її підтримка більше не здійснюється розробниками основної гілки програми!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Мікропрограма з %s не постачається %s, постачальником обладнання." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Заблокованих файлів мікропрограм немає" + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "There is no approved firmware." @@ -1295,6 +1707,18 @@ msgstr "Програма зможе працювати належними чин msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "У цьому сховищі міститься мікропрограма, встановлювати яку не заборонено, але яка усе ще перебуває у процесі тестування виробником обладнання. Вам слід переконатися, що ви зможете встановити стабільну версію мікропрограми, якщо процедура оновлення зазнає невдачі." +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Ця система має вади у роботі HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Ця система має низький рівень захисту HSI." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "За допомогою цієї програми адміністратор може застосувати оновлення до dbx UEFI." + #. TRANSLATORS: the user needs to stop playing with stuff msgid "This tool can only be used by the root user" msgstr "Цим інструментом може користуватися лише root" @@ -1303,10 +1727,46 @@ msgstr "Цим інструментом може користуватися ли msgid "Type" msgstr "Тип" +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Розділ ESP UEFI не виявлено або не налаштовано" + #. TRANSLATORS: program name msgid "UEFI Firmware Utility" msgstr "Засіб роботи із мікропрограмами UEFI" +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled" +msgstr "Капсульні оновлення UEFI є недоступними або їх не увімкнено" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Засіб роботи з dbx UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI secure boot" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Не вдалося встановити з'єднання зі службою" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Відв'язати поточний драйвер" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Розблокуємо мікропрограму:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Розблоковує встановлення певної мікропрограми" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Розшифровано" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -1320,10 +1780,18 @@ msgstr "Невідомий пристрій" msgid "Unlock the device to allow access" msgstr "Розблокування пристрою для отримання доступу" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Розблоковано" + #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" msgstr "Розблоковує пристрій для доступу до мікропрограми" +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Демонтує ESP" + #. TRANSLATORS: command line option msgid "Unset the debugging flag during update" msgstr "Зняти під час оновлення прапорець діагностики" @@ -1333,6 +1801,10 @@ msgstr "Зняти під час оновлення прапорець діаг msgid "Unsupported daemon version %s, client version is %s" msgstr "Непідтримувана версія фонової служби %s, версія клієнта — %s" +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Очищене" + #. TRANSLATORS: Device is updatable in this or any other mode msgid "Updatable" msgstr "Придатний до оновлення" @@ -1381,6 +1853,9 @@ msgstr "Оновити збережені метадані поточними д msgid "Updates all firmware to latest versions available" msgstr "Оновлює усі мікропрограми до найновіших доступних версій" +msgid "Updating" +msgstr "Оновлення" + #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second and third are #. * version numbers e.g. "1.2.3" @@ -1399,10 +1874,6 @@ msgstr "Оновлюємо %s…" msgid "Upgrade available for %s from %s to %s" msgstr "Виявлено оновлення для %s з %s до %s" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "Повідомлення про вивантаження:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "Вивантажити звіт цього разу, але запитати знову, якщо надалі з'являться оновлення" @@ -1429,6 +1900,14 @@ msgstr "Вивантаження звітів щодо мікропрограм msgid "Urgency" msgstr "Рівень важливості" +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Скористайтеся fwupdmgr --help, щоб дізнатися більше" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "Скористайтеся fwupdtool --help, щоб дізнатися більше" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "Використовувати варіативні прапорці при встановленні мікропрограми" @@ -1441,6 +1920,14 @@ msgstr "Сповіщено користувача" msgid "Username" msgstr "Користувач" +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Коректна" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Перевіряємо дані ESP…" + #. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') msgid "Variant" msgstr "Варіант" @@ -1453,9 +1940,13 @@ msgstr "Виробник" msgid "Verifying…" msgstr "Перевіряємо…" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "ПОПЕРЕДЖЕННЯ: ігноруємо строгі перевірки SSL. Щоб зробити ігнорування у майбутньому автоматичним, експортуйте до вашого середовища змінну DISABLE_SSL_STRICT" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Версія" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "УВАГА:" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" @@ -1477,6 +1968,10 @@ msgstr "Записати мікропрограму з файла на прис msgid "Write firmware from file into one partition" msgstr "Записати мікропрограму з файла на один розділ" +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Записуємо файл:" + #. TRANSLATORS: writing to the flash chips msgid "Writing…" msgstr "Записуємо…" @@ -1485,19 +1980,19 @@ msgstr "Записуємо…" msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." msgstr "Виробник вашого дистрибутива може не перевіряти усі оновлення мікропрограми на сумісність із вашою системою або з’єднаними із нею пристроями." +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Ваше обладнання може бути пошкоджено через використання цієї мікропрограми. Встановлення цього випуску може призвести до відмови %s від гарантійних зобов'язань." + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "типова" + #. TRANSLATORS: program name msgid "fwupd TPM event log utility" msgstr "Допоміжна програма для роботи із записами подій журналу TPM fwupd" -#. TRANSLATORS: message letting the user know no device upgrade available due -#. to missing on LVFS -#. * %1 is the device name -#, c-format -msgid "• %s has no available firmware updates" -msgstr "• %s не має доступних оновлень мікропрограми" - -#. TRANSLATORS: message letting the user know no device upgrade available -#. * %1 is the device name -#, c-format -msgid "• %s has the latest available firmware version" -msgstr "• Для %s встановлено найсвіжішу доступну версію мікропрограми" +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Додатки fwupd" diff --git a/po/zh_CN.po b/po/zh_CN.po index e2bd88fcf..850e8a711 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -3,7 +3,7 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Boyuan Yang <073plan@gmail.com>, 2017 +# Boyuan Yang <073plan@gmail.com>, 2017,2020 # Dingzhong Chen , 2020 # Dingzhong Chen , 2016-2019 # Mingcong Bai , 2017-2018 @@ -452,6 +452,7 @@ msgstr[0] "不要上传报告,并且以后更新时也不再询问" msgid "Do not write to the history database" msgstr "不要写入历史数据库" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "完成!" @@ -496,6 +497,8 @@ msgstr "在所支持的系统上启用固件更新的支持" msgid "Enable this remote?" msgstr "要启用此远程源吗?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "已启用" @@ -534,10 +537,6 @@ msgstr "在引擎加载后退出" msgid "Failed to connect to daemon" msgstr "连接到守护进程失败" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "由于服务器限制而下载失败" - #. TRANSLATORS: we could not get the devices to update offline msgid "Failed to get pending devices" msgstr "获取待处理的设备失败" @@ -570,22 +569,6 @@ msgstr "重启失败" msgid "Failed to set splash mode" msgstr "设定启动屏幕模式失败" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "正在获取文件" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "正在获取固件" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "正在获取元信息" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "正在获取签名" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "文件名" @@ -624,12 +607,17 @@ msgid "Firmware metadata has not been updated for %u day and may not be up to da msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." msgstr[0] "固件元数据已有 %u 天未更新,数据可能不是最新的。" +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "固件更新" + msgid "Firmware updates are not supported on this machine." msgstr "此机器不支持固件更新。" msgid "Firmware updates are supported on this machine." msgstr "此机器支持固件更新。" +#. TRANSLATORS: description of plugin state, e.g. disabled #. TRANSLATORS: release properties msgid "Flags" msgstr "标志" @@ -637,6 +625,10 @@ msgstr "标志" msgid "Force the action ignoring all warnings" msgstr "强制操作忽略所有警告" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "找到" + #. TRANSLATORS: global ID common to all similar hardware msgid "GUID" msgid_plural "GUIDs" @@ -666,10 +658,6 @@ msgstr "获取有关某固件文件的详细信息" msgid "Gets the configured remotes" msgstr "获取已配置的远程源" -#. TRANSLATORS: firmware approved by the admin -msgid "Gets the list of approved firmware." -msgstr "获取批准固件的列表。" - #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "获取已连接硬件的可用更新列表" @@ -776,6 +764,10 @@ msgstr "Linux 供应商固件服务(稳定固件)" msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Linux 供应商固件服务(测试固件)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux 内核" + #. TRANSLATORS: command line option msgid "List supported firmware updates" msgstr "列出所支持固件的更新" @@ -788,10 +780,6 @@ msgstr "列出可用的固件类型" msgid "Loading…" msgstr "正在加载…" -#. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "将指定插件手动加入白名单" - #. TRANSLATORS: remote URI msgid "Metadata Signature" msgstr "元数据签名" @@ -813,10 +801,6 @@ msgstr "最小版本" msgid "Mismatched daemon and client, use %s instead" msgstr "守护进程与客户端不匹配,使用 %s 来代替" -#. TRANSLATORS: sets something in daemon.conf -msgid "Modifies a daemon configuration value." -msgstr "修改守护进程配置值。" - #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "修改给定的远程源" @@ -843,6 +827,7 @@ msgstr "安装完后需要关机" msgid "New version" msgstr "新版本" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "未指定操作!" @@ -880,14 +865,14 @@ msgstr "无可用远程源" msgid "No updates were applied" msgstr "没有应用任何更新" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "确定" + #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "只显示单 PCR 值" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "忽略插件警告" - #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "覆盖默认的 ESP 路径" @@ -970,10 +955,6 @@ msgstr "正在重启……" msgid "Refresh metadata from remote server" msgstr "刷新来自远程服务器的元数据" -#. TRANSLATORS: command description -msgid "Reinstall current firmware on the device." -msgstr "在设备上重新安装当前的固件。" - #. TRANSLATORS: the first replacement is a display name #. * e.g. "ColorHugALS" and the second is a version number #. * e.g. "1.2.3" @@ -1002,10 +983,6 @@ msgstr "报告 URI" msgid "Reported to remote server" msgstr "已报告到远程服务器" -#. TRANSLATORS: Must be plugged in to an outlet -msgid "Requires AC power" -msgstr "需要交流电源" - #. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user msgid "Requires a bootloader" msgstr "需要引导程序" @@ -1065,13 +1042,10 @@ msgstr "序列号" msgid "Set the debugging flag during update" msgstr "更新期间设定调试标志" +#. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "设定批准固件的列表" -#. TRANSLATORS: firmware approved by the admin -msgid "Sets the list of approved firmware." -msgstr "设定批准固件的列表。" - #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "与开发者分享固件历史" @@ -1236,6 +1210,10 @@ msgstr "类型" msgid "UEFI Firmware Utility" msgstr "固件 UEFI 实用工具" +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "无法连接到服务" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -1328,10 +1306,6 @@ msgstr "正在更新 %s……" msgid "Upgrade available for %s from %s to %s" msgstr "%s 有从版本 %s 到 %s 的升级" -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "上传消息:" - msgid "Upload report just this one time, but prompt again for future updates" msgid_plural "Upload reports just this one time, but prompt again for future updates" msgstr[0] "仅这次上传报告,但以后更新时再次提示" @@ -1348,6 +1322,14 @@ msgstr[0] "此次上传报告,并且以后完成更新后自动上传报告" msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "上传固件报告可帮助硬件供应商尽快确定真实设备上更新的成功及失败案例。" +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdmgr --help for help" +msgstr "使用 fwupdmgr --help 获取帮助信息" + +#. TRANSLATORS: error message explaining command to run to how to get help +msgid "Use fwupdtool --help for help" +msgstr "使用 fwupdtool --help 获取帮助信息" + #. TRANSLATORS: command line option msgid "Use quirk flags when installing firmware" msgstr "安装固件时使用 quirk 标志" @@ -1372,9 +1354,9 @@ msgstr "供应商" msgid "Verifying…" msgstr "正在验证…" -#. TRANSLATORS: try to help -msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" -msgstr "警告:现忽略 SSL 严格检查,要在以后自动忽略请在你的环境变量里导出 DISABLE_SSL_STRICT" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "版本" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" diff --git a/po/zh_TW.po b/po/zh_TW.po index 77fe91090..17a2ed119 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -198,6 +198,7 @@ msgstr "不要檢查是否有尚未報告的歷史" msgid "Do not write to the history database" msgstr "不要寫入歷史資料庫中" +#. TRANSLATORS: success #. success msgid "Done!" msgstr "完成!" @@ -233,6 +234,8 @@ msgstr "對支援的系統啟用韌體更新支援" msgid "Enable this remote?" msgstr "啟用此遠端站點?" +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "啟用" @@ -267,10 +270,6 @@ msgstr "經過一段短暫延遲後便離開" msgid "Exit after the engine has loaded" msgstr "引擎載入後離開" -#. TRANSLATORS: the server is rate-limiting downloads -msgid "Failed to download due to server limit" -msgstr "因伺服器限制而下載失敗" - #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "無法載入奇技淫巧" @@ -279,22 +278,6 @@ msgstr "無法載入奇技淫巧" msgid "Failed to parse arguments" msgstr "無法解析引數" -#. TRANSLATORS: downloading unknown file -msgid "Fetching file" -msgstr "擷取檔案中" - -#. TRANSLATORS: downloading new firmware file -msgid "Fetching firmware" -msgstr "擷取韌體中" - -#. TRANSLATORS: downloading new metadata file -msgid "Fetching metadata" -msgstr "擷取中介資料中" - -#. TRANSLATORS: downloading new signing file -msgid "Fetching signature" -msgstr "擷取簽章中" - #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "檔名" @@ -331,6 +314,10 @@ msgstr "此機器沒有韌體更新支援。" msgid "Firmware updates are supported on this machine." msgstr "此機器有韌體更新支援。" +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "找到" + #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "取得所有支援韌體更新的裝置" @@ -416,10 +403,6 @@ msgstr "列出支援的韌體更新" msgid "Loading…" msgstr "載入中…" -#. TRANSLATORS: command line option -msgid "Manually whitelist specific plugins" -msgstr "手動白名單指定插件" - #. TRANSLATORS: remote URI msgid "Metadata URI" msgstr "中介資料 URI" @@ -439,6 +422,7 @@ msgstr "修改設定的遠端站點" msgid "Monitor the daemon for events" msgstr "監控幕後程式是否有活動" +#. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" msgstr "未指定動作!" @@ -454,9 +438,9 @@ msgstr "找不到插件" msgid "No remotes are currently enabled so no metadata is available." msgstr "目前沒有啟用的遠端站點,因而沒有中介資料可用。" -#. TRANSLATORS: command line option -msgid "Override plugin warning" -msgstr "凌駕插件警告" +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "確定" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -654,10 +638,6 @@ msgstr "將所有韌體更新至可用的最新版本" msgid "Updating %s from %s to %s... " msgstr "正將 %s 從 %s 版升級至 %s 版... " -#. TRANSLATORS: the server sent the user a small message -msgid "Upload message:" -msgstr "上傳訊息:" - #. TRANSLATORS: ask the user to upload msgid "Upload report now?" msgstr "是否立刻上傳報告?" @@ -674,6 +654,10 @@ msgstr "使用者名稱" msgid "Verifying…" msgstr "核驗中…" +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "版本" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "等候中…" diff --git a/policy/org.freedesktop.fwupd.policy.in b/policy/org.freedesktop.fwupd.policy.in index 5e45825df..22cb19f7e 100644 --- a/policy/org.freedesktop.fwupd.policy.in +++ b/policy/org.freedesktop.fwupd.policy.in @@ -33,6 +33,7 @@ no auth_admin_keep + org.freedesktop.fwupd.update-internal-trusted @@ -44,6 +45,7 @@ no auth_admin_keep + org.freedesktop.fwupd.update-internal @@ -66,6 +68,7 @@ no auth_admin_keep + org.freedesktop.fwupd.update-hotplug-trusted @@ -77,6 +80,7 @@ no auth_admin_keep + org.freedesktop.fwupd.update-hotplug @@ -99,6 +103,7 @@ no auth_admin_keep + org.freedesktop.fwupd.modify-remote diff --git a/snap/hooks/install b/snap/hooks/install index 419b84ecd..3302ab62e 100755 --- a/snap/hooks/install +++ b/snap/hooks/install @@ -19,3 +19,5 @@ install_if_missing etc/systemd/system/fwupd-activate.service / systemctl daemon-reload systemctl enable fwupd-activate systemctl start fwupd-activate +#msr module +install_if_missing etc/modules-load.d/fwupd-msr.conf / diff --git a/snap/hooks/remove b/snap/hooks/remove index 15f6fe5e4..350838419 100755 --- a/snap/hooks/remove +++ b/snap/hooks/remove @@ -5,3 +5,5 @@ systemctl stop fwupd-activate systemctl disable fwupd-activate rm /etc/systemd/system/fwupd-activate.service -f systemctl daemon-reload +#msr module +rm /etc/modules-load.d/fwupd-msr.conf -f diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 5c9fbe2d9..5f1044aff 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -177,6 +177,10 @@ parts: -Dman=false, -Dplugin_modem_manager=true, -Dudevdir=$SNAPCRAFT_STAGE/lib/udev, + "-Dgusb:tests=false", + "-Dgusb:docs=false", + "-Dgusb:introspection=false", + "-Dgusb:vapi=false", "-Dlibxmlb:gtkdoc=false", "-Dlibxmlb:introspection=false", "-Dlibjcat:man=false", @@ -199,7 +203,6 @@ parts: - libefivar-dev - libftdi1-dev - libgudev-1.0-dev - - libgusb-dev - libgcab-dev - libglib2.0-dev - libgpgme11-dev @@ -222,7 +225,6 @@ parts: - libefivar1 - libefiboot1 - libelf1 - - libgusb2 - libusb-1.0-0 - libgudev-1.0-0 - libgpgme11 diff --git a/src/fu-agent.c b/src/fu-agent.c index 72a8d17f9..c85b91166 100644 --- a/src/fu-agent.c +++ b/src/fu-agent.c @@ -21,12 +21,14 @@ #include "fu-util-common.h" #include "fwupd-device-private.h" #include "fwupd-enums-private.h" +#include "fwupd-security-attr-private.h" struct FuUtilPrivate { GCancellable *cancellable; GMainLoop *loop; GOptionContext *context; FwupdClient *client; + FwupdInstallFlags flags; }; static gboolean @@ -112,6 +114,37 @@ fu_util_add_updates_json (FuUtilPrivate *priv, JsonBuilder *builder, GError **er return TRUE; } +static gboolean +fu_util_add_security_attributes_json (FuUtilPrivate *priv, JsonBuilder *builder, GError **error) +{ + g_autoptr(GPtrArray) attrs = NULL; + + /* not ready yet */ + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "The HSI specification is not yet complete. " + "To ignore this warning, use --force"); + return FALSE; + } + + /* get attrs from daemon */ + attrs = fwupd_client_get_host_security_attrs (priv->client, NULL, error); + if (attrs == NULL) + return FALSE; + json_builder_set_member_name (builder, "HostSecurityAttributes"); + json_builder_begin_array (builder); + for (guint i = 0; i < attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i); + json_builder_begin_object (builder); + fwupd_security_attr_to_json (attr, builder); + json_builder_end_object (builder); + } + json_builder_end_array (builder); + return TRUE; +} + static gboolean fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -198,6 +231,49 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +static gboolean +fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autofree gchar *data = NULL; + g_autoptr(JsonBuilder) builder = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonNode) json_root = NULL; + + /* check args */ + if (g_strv_length (values) != 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* create header */ + builder = json_builder_new (); + json_builder_begin_object (builder); + if (!fu_util_add_security_attributes_json (priv, builder, error)) + return FALSE; + json_builder_end_object (builder); + + /* export as a string */ + json_root = json_builder_get_root (builder); + json_generator = json_generator_new (); + json_generator_set_pretty (json_generator, TRUE); + json_generator_set_root (json_generator, json_root); + data = json_generator_to_data (json_generator, NULL); + if (data == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to convert to JSON string"); + return FALSE; + } + + /* just print */ + g_print ("%s\n", data); + return TRUE; +} + static void fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) @@ -235,6 +311,7 @@ int main (int argc, char *argv[]) { gboolean ret; + gboolean force = FALSE; gboolean verbose = FALSE; g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1); g_autoptr(GError) error = NULL; @@ -244,6 +321,9 @@ main (int argc, char *argv[]) { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, /* TRANSLATORS: command line option */ _("Show extra debugging information"), NULL }, + { "force", '\0', 0, G_OPTION_ARG_NONE, &force, + /* TRANSLATORS: command line option */ + _("Override warnings and force the action"), NULL }, { NULL} }; @@ -271,6 +351,11 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Gets the list of updates for connected hardware"), fu_util_get_updates); + fu_util_cmd_array_add (cmd_array, + "security", NULL, + /* TRANSLATORS: command description */ + _("Gets the host security attributes"), + fu_util_security); /* sort by command name */ fu_util_cmd_array_sort (cmd_array); @@ -309,6 +394,10 @@ main (int argc, char *argv[]) fu_util_ignore_cb, NULL); } + /* set flags */ + if (force) + priv->flags |= FWUPD_INSTALL_FLAG_FORCE; + /* run the specified command */ ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error); if (!ret) { diff --git a/src/fu-config.c b/src/fu-config.c index d8bc070bc..07114402e 100644 --- a/src/fu-config.c +++ b/src/fu-config.c @@ -27,8 +27,8 @@ struct _FuConfig { GObject parent_instance; GFileMonitor *monitor; - GPtrArray *blacklist_devices; /* (element-type utf-8) */ - GPtrArray *blacklist_plugins; /* (element-type utf-8) */ + GPtrArray *disabled_devices; /* (element-type utf-8) */ + GPtrArray *disabled_plugins; /* (element-type utf-8) */ GPtrArray *approved_firmware; /* (element-type utf-8) */ GPtrArray *blocked_firmware; /* (element-type utf-8) */ guint64 archive_size_max; @@ -66,30 +66,30 @@ fu_config_reload (FuConfig *self, GError **error) G_KEY_FILE_NONE, error)) return FALSE; - /* get blacklisted devices */ - g_ptr_array_set_size (self->blacklist_devices, 0); + /* get disabled devices */ + g_ptr_array_set_size (self->disabled_devices, 0); devices = g_key_file_get_string_list (keyfile, "fwupd", - "BlacklistDevices", + "DisabledDevices", NULL, /* length */ NULL); if (devices != NULL) { for (guint i = 0; devices[i] != NULL; i++) { - g_ptr_array_add (self->blacklist_devices, + g_ptr_array_add (self->disabled_devices, g_strdup (devices[i])); } } - /* get blacklisted plugins */ - g_ptr_array_set_size (self->blacklist_plugins, 0); + /* get disabled plugins */ + g_ptr_array_set_size (self->disabled_plugins, 0); plugins = g_key_file_get_string_list (keyfile, "fwupd", - "BlacklistPlugins", + "DisabledPlugins", NULL, /* length */ NULL); if (plugins != NULL) { for (guint i = 0; plugins[i] != NULL; i++) { - g_ptr_array_add (self->blacklist_plugins, + g_ptr_array_add (self->disabled_plugins, g_strdup (plugins[i])); } } @@ -237,10 +237,10 @@ fu_config_get_idle_timeout (FuConfig *self) } GPtrArray * -fu_config_get_blacklist_devices (FuConfig *self) +fu_config_get_disabled_devices (FuConfig *self) { g_return_val_if_fail (FU_IS_CONFIG (self), NULL); - return self->blacklist_devices; + return self->disabled_devices; } GPtrArray * @@ -258,10 +258,10 @@ fu_config_get_archive_size_max (FuConfig *self) } GPtrArray * -fu_config_get_blacklist_plugins (FuConfig *self) +fu_config_get_disabled_plugins (FuConfig *self) { g_return_val_if_fail (FU_IS_CONFIG (self), NULL); - return self->blacklist_plugins; + return self->disabled_plugins; } GPtrArray * @@ -302,8 +302,8 @@ static void fu_config_init (FuConfig *self) { self->archive_size_max = 512 * 0x100000; - self->blacklist_devices = g_ptr_array_new_with_free_func (g_free); - self->blacklist_plugins = g_ptr_array_new_with_free_func (g_free); + self->disabled_devices = g_ptr_array_new_with_free_func (g_free); + self->disabled_plugins = g_ptr_array_new_with_free_func (g_free); self->approved_firmware = g_ptr_array_new_with_free_func (g_free); self->blocked_firmware = g_ptr_array_new_with_free_func (g_free); } @@ -317,8 +317,8 @@ fu_config_finalize (GObject *obj) g_file_monitor_cancel (self->monitor); g_object_unref (self->monitor); } - g_ptr_array_unref (self->blacklist_devices); - g_ptr_array_unref (self->blacklist_plugins); + g_ptr_array_unref (self->disabled_devices); + g_ptr_array_unref (self->disabled_plugins); g_ptr_array_unref (self->approved_firmware); g_ptr_array_unref (self->blocked_firmware); g_free (self->config_file); diff --git a/src/fu-config.h b/src/fu-config.h index f20854ba8..6c8d6e340 100644 --- a/src/fu-config.h +++ b/src/fu-config.h @@ -23,8 +23,8 @@ gboolean fu_config_set_key_value (FuConfig *self, guint64 fu_config_get_archive_size_max (FuConfig *self); guint fu_config_get_idle_timeout (FuConfig *self); -GPtrArray *fu_config_get_blacklist_devices (FuConfig *self); -GPtrArray *fu_config_get_blacklist_plugins (FuConfig *self); +GPtrArray *fu_config_get_disabled_devices (FuConfig *self); +GPtrArray *fu_config_get_disabled_plugins (FuConfig *self); GPtrArray *fu_config_get_approved_firmware (FuConfig *self); GPtrArray *fu_config_get_blocked_firmware (FuConfig *self); gboolean fu_config_get_update_motd (FuConfig *self); diff --git a/src/fu-debug.c b/src/fu-debug.c index 10337a165..1773efe17 100644 --- a/src/fu-debug.c +++ b/src/fu-debug.c @@ -66,8 +66,7 @@ fu_debug_handler_cb (const gchar *log_domain, gpointer user_data) { FuDebug *self = (FuDebug *) user_data; - g_autofree gchar *tmp = NULL; - g_autoptr(GDateTime) dt = g_date_time_new_now_utc (); + g_autofree gchar *timestamp = NULL; g_autoptr(GString) domain = NULL; /* should ignore */ @@ -76,11 +75,12 @@ fu_debug_handler_cb (const gchar *log_domain, /* time header */ if (!self->no_timestamp) { - tmp = g_strdup_printf ("%02i:%02i:%02i:%04i", - g_date_time_get_hour (dt), - g_date_time_get_minute (dt), - g_date_time_get_second (dt), - g_date_time_get_microsecond (dt) / 1000); + g_autoptr(GDateTime) dt = g_date_time_new_now_utc (); + timestamp = g_strdup_printf ("%02i:%02i:%02i:%04i", + g_date_time_get_hour (dt), + g_date_time_get_minute (dt), + g_date_time_get_second (dt), + g_date_time_get_microsecond (dt) / 1000); } /* pad out domain */ @@ -96,8 +96,8 @@ fu_debug_handler_cb (const gchar *log_domain, /* to file */ if (!self->console) { g_autofree gchar *ascii_message = g_str_to_ascii (message, NULL); - if (tmp != NULL) - g_printerr ("%s ", tmp); + if (timestamp != NULL) + g_printerr ("%s ", timestamp); if (domain != NULL) g_printerr ("%s ", domain->str); g_printerr ("%s\n", ascii_message); @@ -110,16 +110,16 @@ fu_debug_handler_cb (const gchar *log_domain, case G_LOG_LEVEL_CRITICAL: case G_LOG_LEVEL_WARNING: /* critical in red */ - if (tmp != NULL) - g_printerr ("%c[%dm%s ", 0x1B, 32, tmp); + if (timestamp != NULL) + g_printerr ("%c[%dm%s ", 0x1B, 32, timestamp); if (domain != NULL) g_printerr ("%s ", domain->str); g_printerr ("%c[%dm%s\n%c[%dm", 0x1B, 31, message, 0x1B, 0); break; default: /* debug in blue */ - if (tmp != NULL) - g_printerr ("%c[%dm%s ", 0x1B, 32, tmp); + if (timestamp != NULL) + g_printerr ("%c[%dm%s ", 0x1B, 32, timestamp); if (domain != NULL) g_printerr ("%s ", domain->str); g_printerr ("%c[%dm%s\n%c[%dm", 0x1B, 34, message, 0x1B, 0); diff --git a/src/fu-device-list.c b/src/fu-device-list.c index cdc942792..6f24ed8eb 100644 --- a/src/fu-device-list.c +++ b/src/fu-device-list.c @@ -38,8 +38,6 @@ struct _FuDeviceList GObject parent_instance; GPtrArray *devices; /* of FuDeviceItem */ GRWLock devices_mutex; - GMainLoop *replug_loop; /* block waiting for replug */ - guint replug_id; /* timeout the loop */ }; enum { @@ -81,6 +79,60 @@ fu_device_list_emit_device_changed (FuDeviceList *self, FuDevice *device) g_signal_emit (self, signals[SIGNAL_CHANGED], 0, device); } +/* we cannot use fu_device_get_children() as this will not find "parent-only" + * logical relationships added using fu_device_add_parent_guid() */ +static GPtrArray * +fu_device_list_get_children (FuDeviceList *self, FuDevice *device) +{ + GPtrArray *devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_rw_lock_reader_lock (&self->devices_mutex); + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item = g_ptr_array_index (self->devices, i); + if (device == fu_device_get_parent (item->device)) + g_ptr_array_add (devices, g_object_ref (item->device)); + } + g_rw_lock_reader_unlock (&self->devices_mutex); + return devices; +} + +static void +fu_device_list_depsolve_order_full (FuDeviceList *self, FuDevice *device, guint depth) +{ + g_autoptr(GPtrArray) children = NULL; + + /* ourself */ + fu_device_set_order (device, depth); + + /* optional children */ + children = fu_device_list_get_children (self, device); + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index (children, i); + if (fu_device_has_flag (child, FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST)) { + fu_device_list_depsolve_order_full (self, child, depth + 1); + } else { + fu_device_list_depsolve_order_full (self, child, depth - 1); + } + } +} + +/** + * fu_device_list_depsolve_order: + * @self: A #FuDeviceList + * @device: A #FuDevice + * + * Sets the device order using the logical parent->child relationships -- by default + * the child is updated first, unless the device has set flag + * %FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST. + * + * Since: 1.5.0 + **/ +void +fu_device_list_depsolve_order (FuDeviceList *self, FuDevice *device) +{ + g_autoptr(FuDevice) root = fu_device_get_root (device); + fu_device_list_depsolve_order_full (self, root, 0); +} + /** * fu_device_list_get_all: * @self: A #FuDeviceList @@ -588,11 +640,9 @@ fu_device_list_replace (FuDeviceList *self, FuDeviceItem *item, FuDevice *device fu_device_list_emit_device_changed (self, device); /* we were waiting for this... */ - if (fu_device_has_flag (item->device_old, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) && - g_main_loop_is_running (self->replug_loop)) { - g_debug ("quitting replug loop"); + if (fu_device_has_flag (item->device_old, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) { + g_debug ("device came back, clearing flag"); fu_device_remove_flag (item->device_old, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - g_main_loop_quit (self->replug_loop); } } @@ -628,20 +678,13 @@ fu_device_list_add (FuDeviceList *self, FuDevice *device) /* is the device waiting to be replugged? */ item = fu_device_list_find_by_id (self, fu_device_get_id (device), NULL); - if (item != NULL && item->remove_id != 0) { + if (item != NULL) { g_debug ("found existing device %s, reusing item", fu_device_get_id (item->device)); fu_device_list_replace (self, item, device); return; } - /* verify the device does not already exist */ - if (item != NULL) { - g_debug ("device %s already exists, ignoring", - fu_device_get_id (item->device)); - return; - } - /* verify a device with same connection does not already exist */ item = fu_device_list_find_by_connection (self, fu_device_get_physical_id (device), @@ -681,26 +724,32 @@ fu_device_list_add (FuDeviceList *self, FuDevice *device) fu_device_get_protocol (device)) == 0) { if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NO_GUID_MATCHING)) { if (fu_device_get_priority (device) < fu_device_get_priority (item->device)) { - g_debug ("ignoring device %s [%s] as better device %s [%s] already exists", + g_debug ("ignoring device %s [%s:%s] as better device %s [%s:%s] already exists", fu_device_get_id (device), fu_device_get_plugin (device), + g_type_name (fu_device_get_specialized_gtype (device)), fu_device_get_id (item->device), - fu_device_get_plugin (item->device)); + fu_device_get_plugin (item->device), + g_type_name (fu_device_get_specialized_gtype (item->device))); return; } if (fu_device_get_priority (device) == fu_device_get_priority (item->device)) { - g_warning ("ignoring device %s [%s] existing device %s [%s] already exists", + g_warning ("ignoring device %s [%s:%s] existing device %s [%s:%s] already exists", fu_device_get_id (device), fu_device_get_plugin (device), + g_type_name (fu_device_get_specialized_gtype (device)), fu_device_get_id (item->device), - fu_device_get_plugin (item->device)); + fu_device_get_plugin (item->device), + g_type_name (fu_device_get_specialized_gtype (item->device))); return; } - g_debug ("removing device %s [%s] as better device %s [%s] added", + g_debug ("removing device %s [%s:%s] as better device %s [%s:%s] added", fu_device_get_id (item->device), fu_device_get_plugin (item->device), + g_type_name (fu_device_get_specialized_gtype (item->device)), fu_device_get_id (device), - fu_device_get_plugin (device)); + fu_device_get_plugin (device), + g_type_name (fu_device_get_specialized_gtype (device))); fu_device_list_remove (self, item->device); } else { g_debug ("not adding matching %s for device add, use " @@ -749,18 +798,19 @@ fu_device_list_get_by_guid (FuDeviceList *self, const gchar *guid, GError **erro return NULL; } -static gboolean -fu_device_list_replug_cb (gpointer user_data) +/* count devices that are disconnected and are waiting to be replugged */ +static guint +fu_device_list_devices_wait_removed (FuDeviceList *self) { - FuDeviceList *self = FU_DEVICE_LIST (user_data); - - /* no longer valid */ - self->replug_id = 0; - - /* quit loop */ - g_debug ("device did not replug"); - g_main_loop_quit (self->replug_loop); - return FALSE; + guint cnt = 0; + g_rw_lock_reader_lock (&self->devices_mutex); + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item = g_ptr_array_index (self->devices, i); + if (item->remove_id != 0) + cnt++; + } + g_rw_lock_reader_unlock (&self->devices_mutex); + return cnt; } /** @@ -783,11 +833,13 @@ fu_device_list_wait_for_replug (FuDeviceList *self, FuDevice *device, GError **e { FuDeviceItem *item; guint remove_delay; + guint wait_removed; + guint wait_removed_old = 0; + g_autoptr(GTimer) timer = g_timer_new (); g_return_val_if_fail (FU_IS_DEVICE_LIST (self), FALSE); g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - g_return_val_if_fail (self->replug_id == 0, FALSE); /* not found */ item = fu_device_list_find_by_device (self, device); @@ -826,14 +878,20 @@ fu_device_list_wait_for_replug (FuDeviceList *self, FuDevice *device, GError **e } /* time to unplug and then re-plug */ - self->replug_id = g_timeout_add (remove_delay, fu_device_list_replug_cb, self); - g_main_loop_run (self->replug_loop); - - /* cancel timeout if still pending */ - if (self->replug_id != 0) { - g_source_remove (self->replug_id); - self->replug_id = 0; - } + do { + /* count how many devices are in the remove waiting state */ + wait_removed = fu_device_list_devices_wait_removed (self); + if (wait_removed != wait_removed_old) { + g_debug ("devices in wait_removed: %u -> %u", + wait_removed_old, wait_removed); + wait_removed_old = wait_removed; + } + g_usleep (1000); + g_main_context_iteration (NULL, FALSE); + if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) && + wait_removed == 0) + break; + } while (g_timer_elapsed (timer, NULL) * 1000.f < remove_delay); /* device was not added back to the device list */ if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) { @@ -947,7 +1005,6 @@ static void fu_device_list_init (FuDeviceList *self) { self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_device_list_item_free); - self->replug_loop = g_main_loop_new (NULL, FALSE); g_rw_lock_init (&self->devices_mutex); } @@ -956,11 +1013,8 @@ fu_device_list_finalize (GObject *obj) { FuDeviceList *self = FU_DEVICE_LIST (obj); - if (self->replug_id != 0) - g_source_remove (self->replug_id); - g_ptr_array_unref (self->devices); - g_main_loop_unref (self->replug_loop); g_rw_lock_clear (&self->devices_mutex); + g_ptr_array_unref (self->devices); G_OBJECT_CLASS (fu_device_list_parent_class)->finalize (obj); } diff --git a/src/fu-device-list.h b/src/fu-device-list.h index 783b9d765..eaf711a6b 100644 --- a/src/fu-device-list.h +++ b/src/fu-device-list.h @@ -31,3 +31,5 @@ FuDevice *fu_device_list_get_by_guid (FuDeviceList *self, gboolean fu_device_list_wait_for_replug (FuDeviceList *self, FuDevice *device, GError **error); +void fu_device_list_depsolve_order (FuDeviceList *self, + FuDevice *device); diff --git a/src/fu-engine.c b/src/fu-engine.c index 8c7316565..2eef588a5 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -50,11 +50,14 @@ #include "fu-plugin-private.h" #include "fu-quirks.h" #include "fu-remote-list.h" +#include "fu-security-attr.h" +#include "fu-security-attrs-private.h" #include "fu-smbios-private.h" #include "fu-udev-device-private.h" #include "fu-usb-device-private.h" #include "fu-dfu-firmware.h" +#include "fu-fmap-firmware.h" #include "fu-ihex-firmware.h" #include "fu-srec-firmware.h" @@ -66,6 +69,7 @@ #endif static void fu_engine_finalize (GObject *obj); +static void fu_engine_ensure_security_attrs (FuEngine *self); struct _FuEngine { @@ -98,12 +102,14 @@ struct _FuEngine FuQuirks *quirks; GHashTable *runtime_versions; GHashTable *compile_versions; - GHashTable *approved_firmware; + GHashTable *approved_firmware; /* (nullable) */ GHashTable *blocked_firmware; /* (nullable) */ GHashTable *firmware_gtypes; gchar *host_machine_id; JcatContext *jcat_context; gboolean loaded; + gchar *host_security_id; + FuSecurityAttrs *host_security_attrs; }; enum { @@ -131,13 +137,16 @@ fu_engine_emit_changed (FuEngine *self) fu_config_get_update_motd (self->config)) { g_autoptr(GError) error_local = NULL; if (!fu_engine_update_motd (self, &error_local)) - g_debug ("%s", error_local->message); + g_debug ("failed to update MOTD: %s", + error_local->message); } } static void fu_engine_emit_device_changed (FuEngine *self, FuDevice *device) { + /* invalidate host security attributes */ + g_clear_pointer (&self->host_security_id, g_free); g_signal_emit (self, signals[SIGNAL_DEVICE_CHANGED], 0, device); } @@ -348,6 +357,9 @@ fu_engine_set_release_from_appstream (FuEngine *self, tmp = xb_node_query_text (component, "summary", NULL); if (tmp != NULL) fwupd_release_set_summary (rel, tmp); + tmp = xb_node_query_text (component, "branch", NULL); + if (tmp != NULL) + fwupd_release_set_branch (rel, tmp); tmp = xb_node_query_text (component, "developer_name", NULL); if (tmp != NULL) fwupd_release_set_vendor (rel, tmp); @@ -519,9 +531,9 @@ fu_engine_modify_config (FuEngine *self, const gchar *key, const gchar *value, G { const gchar *keys[] = { "ArchiveSizeMax", - "BlacklistDevices", - "BlacklistPlugins", + "DisabledDevices", "BlockedFirmware", + "DisabledPlugins", "IdleTimeout", "VerboseDomains", "UpdateMotd", @@ -565,7 +577,17 @@ fu_engine_modify_remote (FuEngine *self, const gchar *value, GError **error) { - const gchar *keys[] = { "Enabled", "MetadataURI", "FirmwareBaseURI", "ReportURI", "AutomaticReports", NULL }; + const gchar *keys[] = { + "ApprovalRequired", + "AutomaticReports", + "AutomaticSecurityReports", + "Enabled", + "FirmwareBaseURI", + "MetadataURI", + "ReportURI", + "SecurityReportURI", + NULL, + }; /* check keys are valid */ if (!g_strv_contains (keys, key)) { @@ -776,6 +798,115 @@ fu_engine_get_component_by_guids (FuEngine *self, FuDevice *device) return NULL; } +static XbNode * +fu_engine_verify_from_local_metadata (FuEngine *self, + FuDevice *device, + GError **error) +{ + g_autofree gchar *fn = NULL; + g_autofree gchar *localstatedir = NULL; + g_autofree gchar *xpath = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + g_autoptr(XbNode) release = NULL; + g_autoptr(XbSilo) silo = NULL; + + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + fn = g_strdup_printf ("%s/verify/%s.xml", + localstatedir, + fu_device_get_id (device)); + file = g_file_new_for_path (fn); + if (!g_file_query_exists (file, NULL)) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to find %s", fn); + return NULL; + } + + if (!xb_builder_source_load_file (source, file, + XB_BUILDER_SOURCE_FLAG_NONE, + NULL, error)) + return NULL; + xb_builder_import_source (builder, source); + silo = xb_builder_compile (builder, + XB_BUILDER_COMPILE_FLAG_NONE, + NULL, error); + if (silo == NULL) + return NULL; + xpath = g_strdup_printf ("component/releases/release[@version='%s']", + fu_device_get_version (device)); + release = xb_silo_query_first (silo, xpath, error); + if (release == NULL) + return NULL; + + /* silo has to have same lifecycle as node */ + g_object_set_data_full (G_OBJECT (release), "XbSilo", + g_steal_pointer (&silo), + (GDestroyNotify) g_object_unref); + return g_steal_pointer (&release); +} + +static XbNode * +fu_engine_verify_from_system_metadata (FuEngine *self, + FuDevice *device, + GError **error) +{ + FwupdVersionFormat fmt = fu_device_get_version_format (device); + GPtrArray *guids = fu_device_get_guids (device); + g_autoptr(XbQuery) query = NULL; + + /* prepare query with bound GUID parameter */ + query = xb_query_new_full (self->silo, + "components/component/" + "provides/firmware[@type='flashed'][text()=?]/" + "../../releases/release", + XB_QUERY_FLAG_OPTIMIZE | + XB_QUERY_FLAG_USE_INDEXES, + error); + if (query == NULL) + return NULL; + + /* use prepared query for each GUID */ + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index (guids, i); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) releases = NULL; + + /* bind GUID and then query */ + if (!xb_query_bind_str (query, 0, guid, error)) { + g_prefix_error (error, "failed to bind string: "); + return NULL; + } + releases = xb_silo_query_full (self->silo, query, &error_local); + if (releases == NULL) { + if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) || + g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_debug ("could not find %s: %s", + guid, error_local->message); + continue; + } + g_propagate_error (error, g_steal_pointer (&error_local)); + return NULL; + } + for (guint j = 0; j < releases->len; j++) { + XbNode *rel = g_ptr_array_index (releases, j); + const gchar *rel_ver = xb_node_get_attr (rel, "version"); + g_autofree gchar *tmp_ver = fu_common_version_parse_from_format (rel_ver, fmt); + if (fu_common_vercmp_full (tmp_ver, fu_device_get_version (device), fmt) == 0) + return g_object_ref (rel); + } + } + + /* not found */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to find release"); + return NULL; +} + /** * fu_engine_verify: * @self: A #FuEngine @@ -791,15 +922,11 @@ fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) { FuPlugin *plugin; GPtrArray *checksums; - const gchar *version; - g_autofree gchar *fn = NULL; - g_autofree gchar *localstatedir = NULL; g_autoptr(FuDevice) device = NULL; - g_autoptr(GFile) file = NULL; + g_autoptr(GError) error_local = NULL; g_autoptr(GString) xpath_csum = g_string_new (NULL); g_autoptr(XbNode) csum = NULL; g_autoptr(XbNode) release = NULL; - g_autoptr(XbSilo) silo = xb_silo_new (); g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); g_return_val_if_fail (device_id != NULL, FALSE); @@ -824,71 +951,34 @@ fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) return FALSE; } - /* find component in metadata */ - version = fu_device_get_version (device); - localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); - fn = g_strdup_printf ("%s/verify/%s.xml", localstatedir, device_id); - file = g_file_new_for_path (fn); - if (g_file_query_exists (file, NULL)) { - g_autofree gchar *xpath = NULL; - g_autoptr(XbBuilder) builder = xb_builder_new (); - g_autoptr(XbBuilderSource) source = xb_builder_source_new (); - if (!xb_builder_source_load_file (source, file, - XB_BUILDER_SOURCE_FLAG_NONE, - NULL, error)) + /* find component in local metadata */ + release = fu_engine_verify_from_local_metadata (self, device, &error_local); + if (release == NULL) { + if (!g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && + !g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_propagate_error (error, g_steal_pointer (&error_local)); return FALSE; - xb_builder_import_source (builder, source); - silo = xb_builder_compile (builder, - XB_BUILDER_COMPILE_FLAG_NONE, - NULL, error); - if (silo == NULL) - return FALSE; - xpath = g_strdup_printf ("component/releases/release[@version='%s']", version); - release = xb_silo_query_first (silo, xpath, NULL); + } } /* try again with the system metadata */ if (release == NULL) { - GPtrArray *guids = fu_device_get_guids (device); - FwupdVersionFormat fmt = fu_device_get_version_format (device); - for (guint i = 0; i < guids->len; i++) { - const gchar *guid = g_ptr_array_index (guids, i); - g_autofree gchar *xpath2 = NULL; - g_autoptr(GError) error_local = NULL; - g_autoptr(GPtrArray) releases = NULL; - xpath2 = g_strdup_printf ("components/component/" - "provides/firmware[@type='flashed'][text()='%s']/" - "../../releases/release", - guid); - releases = xb_silo_query (self->silo, xpath2, 0, &error_local); - if (releases == NULL) { - if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) || - g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { - g_debug ("could not find %s: %s", - guid, error_local->message); - continue; - } - g_propagate_error (error, g_steal_pointer (&error_local)); + g_autoptr(GError) error_system = NULL; + release = fu_engine_verify_from_system_metadata (self, device, &error_system); + if (release == NULL) { + if (!g_error_matches (error_system, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && + !g_error_matches (error_system, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_propagate_error (error, g_steal_pointer (&error_system)); return FALSE; } - for (guint j = 0; j < releases->len; j++) { - XbNode *rel = g_ptr_array_index (releases, j); - const gchar *rel_ver = xb_node_get_attr (rel, "version"); - g_autofree gchar *tmp_ver = fu_common_version_parse_from_format (rel_ver, fmt); - if (fu_common_vercmp_full (tmp_ver, version, fmt) == 0) { - release = g_object_ref (rel); - break; - } - } - if (release != NULL) - break; } } if (release == NULL) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, - "No release found for version %s", version); + "No release found for version %s", + fu_device_get_version (device)); return FALSE; } @@ -898,7 +988,8 @@ fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, - "No device checksums for %s", version); + "No device checksums for %s", + fu_device_get_version (device)); return FALSE; } @@ -929,7 +1020,7 @@ fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "No stored checksums for %s", - version); + fu_device_get_version (device)); return FALSE; } for (guint i = 0; i < csums->len; i++) { @@ -946,7 +1037,7 @@ fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) FWUPD_ERROR_NOT_FOUND, "For %s %s expected %s, got %s", fu_device_get_name (device), - version, + fu_device_get_version (device), checksums_metadata->str, checksums_device->str); return FALSE; @@ -1244,7 +1335,7 @@ fu_engine_check_requirement_id (FuEngine *self, XbNode *req, GError **error) return FALSE; } - g_debug ("requirement %s %s %s on %s passed", + g_debug ("requirement %s %s %s -> %s passed", xb_node_get_attr (req, "version"), xb_node_get_attr (req, "compare"), version, xb_node_get_text (req)); @@ -1349,6 +1440,21 @@ fu_engine_check_requirement (FuEngine *self, return FALSE; } +gboolean +fu_engine_check_trust (FuInstallTask *task, GError **error) +{ +#ifndef HAVE_POLKIT + if ((fu_install_task_get_trust_flags (task) & FWUPD_TRUST_FLAG_PAYLOAD) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "archive signature missing or not trusted"); + return FALSE; + } +#endif + return TRUE; +} + gboolean fu_engine_check_requirements (FuEngine *self, FuEngineRequest *request, @@ -1369,19 +1475,18 @@ fu_engine_check_requirements (FuEngine *self, /* do engine checks */ reqs = xb_node_query (fu_install_task_get_component (task), "requires/*", 0, &error_local); - if (reqs == NULL) { - if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - return TRUE; - if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) - return TRUE; - g_propagate_error (error, g_steal_pointer (&error_local)); - return FALSE; - } - for (guint i = 0; i < reqs->len; i++) { - XbNode *req = g_ptr_array_index (reqs, i); - if (!fu_engine_check_requirement (self, request, req, device, error)) + if (reqs != NULL) { + for (guint i = 0; i < reqs->len; i++) { + XbNode *req = g_ptr_array_index (reqs, i); + if (!fu_engine_check_requirement (self, request, req, device, error)) + return FALSE; + } + } else if (!g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && + !g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_propagate_error (error, g_steal_pointer (&error_local)); return FALSE; } + return TRUE; } @@ -1406,14 +1511,135 @@ fu_engine_get_boot_time (void) return NULL; } -static GHashTable * -fu_engine_get_report_metadata (FuEngine *self) +static gboolean +fu_engine_get_report_metadata_os_release (GHashTable *hash, GError **error) { - GHashTable *hash; + g_autoptr(GHashTable) os_release = NULL; + struct { + const gchar *key; + const gchar *val; + } distro_kv[] = { + { "ID", "DistroId" }, + { "VERSION_ID", "DistroVersion" }, + { "VARIANT_ID", "DistroVariant" }, + { NULL, NULL } + }; + + /* get all required os-release keys */ + os_release = fwupd_get_os_release (error); + if (os_release == NULL) + return FALSE; + for (guint i = 0; distro_kv[i].key != NULL; i++) { + const gchar *tmp = g_hash_table_lookup (os_release, distro_kv[i].key); + if (tmp != NULL) { + g_hash_table_insert (hash, + g_strdup (distro_kv[i].val), + g_strdup (tmp)); + } + } + return TRUE; +} + +static gboolean +fu_engine_get_report_metadata_kernel_cmdline (GHashTable *hash, GError **error) +{ + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + const gchar *ignore[] = { + "", + "auto", + "boot", + "BOOT_IMAGE", + "console", + "cryptdevice", + "cryptkey", + "earlycon", + "earlyprintk", + "ether", + "initrd", + "ip", + "LANG", + "loglevel", + "luks.key", + "luks.name", + "luks.options", + "luks.uuid", + "mount.usr", + "mount.usrflags", + "mount.usrfstype", + "netdev", + "netroot", + "nfsaddrs", + "nfs.nfs4_unique_id", + "nfsroot", + "noplymouth", + "ostree", + "quiet", + "rd.dm.uuid", + "rd.luks.allow-discards", + "rd.luks.key", + "rd.luks.name", + "rd.luks.options", + "rd.luks.uuid", + "rd.lvm.lv", + "rd.lvm.vg", + "rd.md.uuid", + "rd.systemd.mask", + "rd.systemd.wants", + "resume", + "resumeflags", + "rhgb", + "ro", + "root", + "rootflags", + "roothash", + "rw", + "showopts", + "splash", + "swap", + "systemd.mask", + "systemd.unit", + "systemd.verity_root_data", + "systemd.verity_root_hash", + "systemd.wants", + "verbose", + "vt.handoff", + "zfs", + NULL, /* last entry */ + }; + + /* get a PII-safe kernel command line */ + if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, error)) + return FALSE; + if (bufsz > 0) { + g_auto(GStrv) tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1); + g_autoptr(GString) cmdline_safe = g_string_new (NULL); + for (guint i = 0; tokens[i] != NULL; i++) { + g_auto(GStrv) kv = g_strsplit (tokens[i], "=", 2); + if (g_strv_contains (ignore, kv[0])) + continue; + if (cmdline_safe->len > 0) + g_string_append (cmdline_safe, " "); + g_string_append (cmdline_safe, tokens[i]); + } + if (cmdline_safe->len > 0) { + g_hash_table_insert (hash, + g_strdup ("KernelCmdline"), + g_strdup (cmdline_safe->str)); + } + } + return TRUE; +} + +GHashTable * +fu_engine_get_report_metadata (FuEngine *self, GError **error) +{ + const gchar *tmp; gchar *btime; #ifdef HAVE_UTSNAME_H struct utsname name_tmp; #endif + g_autoptr(GHashTable) hash = NULL; g_autoptr(GList) compile_keys = g_hash_table_get_keys (self->compile_versions); g_autoptr(GList) runtime_keys = g_hash_table_get_keys (self->runtime_versions); @@ -1433,6 +1659,24 @@ fu_engine_get_report_metadata (FuEngine *self) g_strdup_printf ("RuntimeVersion(%s)", id), g_strdup (version)); } + if (!fu_engine_get_report_metadata_os_release (hash, error)) + return NULL; + if (!fu_engine_get_report_metadata_kernel_cmdline (hash, error)) + return NULL; + + /* DMI data */ + tmp = fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_PRODUCT_NAME); + if (tmp != NULL) + g_hash_table_insert (hash, g_strdup ("HostProduct"), g_strdup (tmp)); + tmp = fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_FAMILY); + if (tmp != NULL) + g_hash_table_insert (hash, g_strdup ("HostFamily"), g_strdup (tmp)); + tmp = fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_PRODUCT_SKU); + if (tmp != NULL) + g_hash_table_insert (hash, g_strdup ("HostSku"), g_strdup (tmp)); + tmp = fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_MANUFACTURER); + if (tmp != NULL) + g_hash_table_insert (hash, g_strdup ("HostVendor"), g_strdup (tmp)); /* kernel version is often important for debugging failures */ #ifdef HAVE_UTSNAME_H @@ -1441,6 +1685,9 @@ fu_engine_get_report_metadata (FuEngine *self) g_hash_table_insert (hash, g_strdup ("CpuArchitecture"), g_strdup (name_tmp.machine)); + g_hash_table_insert (hash, + g_strdup ("KernelVersion"), + g_strdup (name_tmp.release)); } #endif @@ -1449,7 +1696,7 @@ fu_engine_get_report_metadata (FuEngine *self) if (btime != NULL) g_hash_table_insert (hash, g_strdup ("BootTime"), btime); - return hash; + return g_steal_pointer (&hash); } /** @@ -1528,7 +1775,7 @@ fu_engine_install_tasks (FuEngine *self, g_autoptr(GPtrArray) devices_new = NULL; /* do not allow auto-shutdown during this time */ - locker = fu_idle_locker_new (self->idle, "performing update"); + locker = fu_idle_locker_new (self->idle, "update"); g_assert (locker != NULL); /* notify the plugins about the composite action */ @@ -1593,54 +1840,50 @@ fu_engine_install_tasks (FuEngine *self, } static FwupdRelease * -fu_engine_create_release_metadata (FuEngine *self, FuPlugin *plugin, GError **error) +fu_engine_create_release_metadata (FuEngine *self, + FuDevice *device, + FuPlugin *plugin, + GError **error) { GPtrArray *metadata_sources; - const gchar *tmp; g_autoptr(FwupdRelease) release = fwupd_release_new (); + g_autoptr(GHashTable) metadata_device = NULL; g_autoptr(GHashTable) metadata_hash = NULL; - g_autoptr(GHashTable) os_release = NULL; - - /* add release data from os-release */ - os_release = fwupd_get_os_release (error); - if (os_release == NULL) - return NULL; /* build the version metadata */ - metadata_hash = fu_engine_get_report_metadata (self); + metadata_hash = fu_engine_get_report_metadata (self, error); + if (metadata_hash == NULL) + return NULL; fwupd_release_add_metadata (release, metadata_hash); - fwupd_release_add_metadata (release, fu_plugin_get_report_metadata (plugin)); + if (fu_plugin_get_report_metadata (plugin) != NULL) + fwupd_release_add_metadata (release, fu_plugin_get_report_metadata (plugin)); + metadata_device = fu_device_report_metadata_pre (device); + if (metadata_device != NULL) + fwupd_release_add_metadata (release, metadata_device); /* allow other plugins to contribute metadata too */ metadata_sources = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_METADATA_SOURCE); - for (guint i = 0; i < metadata_sources->len; i++) { - FuPlugin *plugin_tmp; - const gchar *plugin_name = g_ptr_array_index (metadata_sources, i); - g_autoptr(GError) error_local = NULL; + if (metadata_sources != NULL) { + for (guint i = 0; i < metadata_sources->len; i++) { + FuPlugin *plugin_tmp; + const gchar *plugin_name = g_ptr_array_index (metadata_sources, i); + g_autoptr(GError) error_local = NULL; - plugin_tmp = fu_plugin_list_find_by_name (self->plugin_list, - plugin_name, - &error_local); - if (plugin_tmp == NULL) { - g_warning ("could not add metadata for %s: %s", - plugin_name, - error_local->message); - continue; + plugin_tmp = fu_plugin_list_find_by_name (self->plugin_list, + plugin_name, + &error_local); + if (plugin_tmp == NULL) { + g_warning ("could not add metadata for %s: %s", + plugin_name, + error_local->message); + continue; + } + if (fu_plugin_get_report_metadata (plugin_tmp) != NULL) { + fwupd_release_add_metadata (release, + fu_plugin_get_report_metadata (plugin_tmp)); + } } - fwupd_release_add_metadata (release, - fu_plugin_get_report_metadata (plugin_tmp)); } - - /* add details from os-release as metadata */ - tmp = g_hash_table_lookup (os_release, "ID"); - if (tmp != NULL) - fwupd_release_add_metadata_item (release, "DistroId", tmp); - tmp = g_hash_table_lookup (os_release, "VERSION_ID"); - if (tmp != NULL) - fwupd_release_add_metadata_item (release, "DistroVersion", tmp); - tmp = g_hash_table_lookup (os_release, "VARIANT_ID"); - if (tmp != NULL) - fwupd_release_add_metadata_item (release, "DistroVariant", tmp); return g_steal_pointer (&release); } @@ -1867,7 +2110,7 @@ fu_engine_install_release (FuEngine *self, /* add device to database */ if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) { g_autoptr(FwupdRelease) release_tmp = NULL; - release_tmp = fu_engine_create_release_metadata (self, plugin, error); + release_tmp = fu_engine_create_release_metadata (self, device, plugin, error); if (release_tmp == NULL) return FALSE; tmp = xb_node_query_text (component, @@ -1941,20 +2184,6 @@ fu_engine_install_release (FuEngine *self, str = g_strdup_printf ("device version not updated on success, %s != %s", version_rel, fu_device_get_version (device)); fu_device_set_update_error (device, str); - if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 && - !fu_history_modify_device (self->history, device, error)) - return FALSE; - /* success */ - return TRUE; - } - - /* ensure the new version matched what we expected */ - if (version_rel != NULL && - g_strcmp0 (fu_device_get_version (device), version_rel) != 0) { - g_warning ("new device version '%s' was is not '%s', fixing up", - fu_device_get_version (device), version_rel); - fu_device_set_version_format (device, fu_device_get_version_format (device)); - fu_device_set_version (device, version_rel); } /* success */ @@ -2115,7 +2344,7 @@ fu_engine_install (FuEngine *self, if (!fu_plugin_runner_update_prepare (plugin, flags, device, error)) return FALSE; } - release_tmp = fu_engine_create_release_metadata (self, plugin, error); + release_tmp = fu_engine_create_release_metadata (self, device, plugin, error); if (release_tmp == NULL) return FALSE; fwupd_release_set_version (release_tmp, version_rel); @@ -2173,8 +2402,18 @@ fu_engine_get_plugins (FuEngine *self) return fu_plugin_list_get_all (self->plugin_list); } -static FuDevice * -fu_engine_get_device_by_id (FuEngine *self, const gchar *device_id, GError **error) +/** + * fu_engine_get_device: + * @self: A #FuEngine + * @device_id: A device ID + * @error: A #GError, or %NULL + * + * Gets a specific device. + * + * Returns: (transfer full): a device, or %NULL if not found + **/ +FuDevice * +fu_engine_get_device (FuEngine *self, const gchar *device_id, GError **error) { g_autoptr(FuDevice) device1 = NULL; g_autoptr(FuDevice) device2 = NULL; @@ -2261,7 +2500,7 @@ fu_engine_update_prepare (FuEngine *self, g_autoptr(FuDevice) device = NULL; /* the device and plugin both may have changed */ - device = fu_engine_get_device_by_id (self, device_id, error); + device = fu_engine_get_device (self, device_id, error); if (device == NULL) return FALSE; @@ -2269,7 +2508,7 @@ fu_engine_update_prepare (FuEngine *self, fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); str = fu_device_to_string (device); - g_debug ("performing prepare on %s", str); + g_debug ("prepare -> %s", str); if (!fu_engine_device_prepare (self, device, flags, error)) return FALSE; for (guint j = 0; j < plugins->len; j++) { @@ -2299,11 +2538,11 @@ fu_engine_update_cleanup (FuEngine *self, g_autoptr(FuDevice) device = NULL; /* the device and plugin both may have changed */ - device = fu_engine_get_device_by_id (self, device_id, error); + device = fu_engine_get_device (self, device_id, error); if (device == NULL) return FALSE; str = fu_device_to_string (device); - g_debug ("performing cleanup on %s", str); + g_debug ("cleanup -> %s", str); if (!fu_engine_device_cleanup (self, device, flags, error)) return FALSE; for (guint j = 0; j < plugins->len; j++) { @@ -2330,11 +2569,11 @@ fu_engine_update_detach (FuEngine *self, const gchar *device_id, GError **error) g_autoptr(FuDevice) device = NULL; /* the device and plugin both may have changed */ - device = fu_engine_get_device_by_id (self, device_id, error); + device = fu_engine_get_device (self, device_id, error); if (device == NULL) return FALSE; str = fu_device_to_string (device); - g_debug ("performing detach on %s", str); + g_debug ("detach -> %s", str); plugin = fu_plugin_list_find_by_name (self->plugin_list, fu_device_get_plugin (device), error); @@ -2353,13 +2592,13 @@ fu_engine_update_attach (FuEngine *self, const gchar *device_id, GError **error) g_autoptr(FuDevice) device = NULL; /* the device and plugin both may have changed */ - device = fu_engine_get_device_by_id (self, device_id, error); + device = fu_engine_get_device (self, device_id, error); if (device == NULL) { g_prefix_error (error, "failed to get device after update: "); return FALSE; } str = fu_device_to_string (device); - g_debug ("performing attach on %s", str); + g_debug ("attach -> %s", str); plugin = fu_plugin_list_find_by_name (self->plugin_list, fu_device_get_plugin (device), error); @@ -2387,7 +2626,7 @@ fu_engine_activate (FuEngine *self, const gchar *device_id, GError **error) if (device == NULL) return FALSE; str = fu_device_to_string (device); - g_debug ("performing activate on %s", str); + g_debug ("activate -> %s", str); plugin = fu_plugin_list_find_by_name (self->plugin_list, fu_device_get_plugin (device), error); @@ -2412,13 +2651,13 @@ fu_engine_update_reload (FuEngine *self, const gchar *device_id, GError **error) g_autoptr(FuDevice) device = NULL; /* the device and plugin both may have changed */ - device = fu_engine_get_device_by_id (self, device_id, error); + device = fu_engine_get_device (self, device_id, error); if (device == NULL) { g_prefix_error (error, "failed to get device after update: "); return FALSE; } str = fu_device_to_string (device); - g_debug ("performing reload on %s", str); + g_debug ("reload -> %s", str); plugin = fu_plugin_list_find_by_name (self->plugin_list, fu_device_get_plugin (device), error); @@ -2454,14 +2693,14 @@ fu_engine_update (FuEngine *self, return FALSE; /* the device and plugin both may have changed */ - device = fu_engine_get_device_by_id (self, device_id, error); + device = fu_engine_get_device (self, device_id, error); if (device == NULL) { g_prefix_error (error, "failed to get device after detach: "); return FALSE; } device_pending = fu_history_get_device_by_id (self->history, device_id, NULL); str = fu_device_to_string (device); - g_debug ("performing update on %s", str); + g_debug ("update -> %s", str); plugin = fu_plugin_list_find_by_name (self->plugin_list, fu_device_get_plugin (device), error); @@ -2516,34 +2755,20 @@ fu_engine_update (FuEngine *self, } GBytes * -fu_engine_firmware_read (FuEngine *self, +fu_engine_firmware_dump (FuEngine *self, FuDevice *device, FwupdInstallFlags flags, GError **error) { g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(FuFirmware) firmware = NULL; - /* open, detach, read, attach, serialize */ + /* open, read, close */ locker = fu_device_locker_new (device, error); if (locker == NULL) { g_prefix_error (error, "failed to open device for firmware read: "); return NULL; } - if (!fu_device_detach (device, error)) - return NULL; - firmware = fu_device_read_firmware (device, error); - if (firmware == NULL) { - g_autoptr(GError) error_local = NULL; - if (!fu_device_attach (device, &error_local)) { - g_warning ("failed to attach after read image failure: %s", - error_local->message); - } - return NULL; - } - if (!fu_device_attach (device, error)) - return NULL; - return fu_firmware_write (firmware, error); + return fu_device_dump_firmware (device, error); } gboolean @@ -2601,7 +2826,7 @@ fu_engine_install_blob (FuEngine *self, return FALSE; /* the device and plugin both may have changed */ - device_tmp = fu_engine_get_device_by_id (self, device_id, error); + device_tmp = fu_engine_get_device (self, device_id, error); if (device_tmp == NULL) return FALSE; if (!fu_device_has_flag (device_tmp, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED)) @@ -2758,7 +2983,8 @@ fu_engine_create_metadata (FuEngine *self, XbBuilder *builder, /* build source for file */ source = fu_engine_create_metadata_builder_source (self, fn, &error_local); if (source == NULL) { - g_warning ("%s", error_local->message); + g_warning ("failed to create builder source: %s", + error_local->message); continue; } @@ -2837,8 +3063,10 @@ fu_engine_md_refresh_device_name (FuEngine *self, FuDevice *device, XbNode *comp /* copy 1:1 */ name = xb_node_query_text (component, "name", NULL); - if (name != NULL) + if (name != NULL) { fu_device_set_name (device, name); + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_MD_SET_NAME); + } } static const gchar * @@ -2881,8 +3109,10 @@ fu_engine_md_refresh_device_name_category (FuEngine *self, FuDevice *device, XbN if (name != NULL) break; } - if (name != NULL) + if (name != NULL) { fu_device_set_name (device, name); + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_MD_SET_NAME_CATEGORY); + } } static void @@ -2938,6 +3168,9 @@ fu_engine_md_refresh_device_verfmt (FuEngine *self, FuDevice *device, XbNode *co fu_device_set_version_bootloader (device, version); } } + + /* do not try to do this again */ + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_MD_SET_VERFMT); } void @@ -2985,7 +3218,7 @@ fu_engine_load_metadata_store (FuEngine *self, FuEngineLoadFlags flags, GError * g_clear_object (&self->silo); /* verbose profiling */ - if (g_getenv ("FWUPD_VERBOSE") != NULL) { + if (g_getenv ("FWUPD_XMLB_VERBOSE") != NULL) { xb_builder_set_profile_flags (builder, XB_SILO_PROFILE_FLAG_XPATH | XB_SILO_PROFILE_FLAG_DEBUG); @@ -3002,16 +3235,11 @@ fu_engine_load_metadata_store (FuEngine *self, FuEngineLoadFlags flags, GError * g_autoptr(XbBuilderSource) source = xb_builder_source_new (); FwupdRemote *remote = g_ptr_array_index (remotes, i); - if (!fwupd_remote_get_enabled (remote)) { - g_debug ("remote %s not enabled, so skipping", - fwupd_remote_get_id (remote)); + if (!fwupd_remote_get_enabled (remote)) continue; - } path = fwupd_remote_get_filename_cache (remote); - if (!g_file_test (path, G_FILE_TEST_EXISTS)) { - g_debug ("no %s, so skipping", path); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) continue; - } /* generate all metadata on demand */ if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) { @@ -3108,6 +3336,9 @@ fu_engine_remote_list_changed_cb (FuRemoteList *remote_list, FuEngine *self) /* set device properties from the metadata */ fu_engine_md_refresh_devices (self); + /* invalidate host security attributes */ + g_clear_pointer (&self->host_security_id, g_free); + /* make the UI update */ fu_engine_emit_changed (self); } @@ -3270,12 +3501,12 @@ fu_engine_update_metadata_bytes (FuEngine *self, const gchar *remote_id, /* verify file */ keyring_kind = fwupd_remote_get_keyring_kind (remote); if (keyring_kind != FWUPD_KEYRING_KIND_NONE) { - JcatResult *jcat_result; g_autoptr(GError) error_local = NULL; g_autoptr(GInputStream) istream = NULL; g_autoptr(GPtrArray) results = NULL; g_autoptr(JcatFile) jcat_file = jcat_file_new (); g_autoptr(JcatItem) jcat_item = NULL; + g_autoptr(JcatResult) jcat_result = NULL; g_autoptr(JcatResult) jcat_result_old = NULL; /* load Jcat file */ @@ -3334,7 +3565,14 @@ fu_engine_update_metadata_bytes (FuEngine *self, const gchar *remote_id, } if (!fu_engine_load_metadata_store (self, FU_ENGINE_LOAD_FLAG_NONE, error)) return FALSE; + + /* refresh SUPPORTED flag on devices */ fu_engine_md_refresh_devices (self); + + /* invalidate host security attributes */ + g_clear_pointer (&self->host_security_id, g_free); + + /* make the UI update */ fu_engine_emit_changed (self); return TRUE; } @@ -3634,7 +3872,8 @@ fu_engine_get_details (FuEngine *self, FuEngineRequest *request, gint fd, GError fwupd_release_set_remote_id (rel, remote_id); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED); } - fu_engine_md_refresh_device_from_component (self, dev, component); + if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_MD_SET_VERFMT)) + fu_engine_md_refresh_device_verfmt (self, dev, component); /* if this matched a device on the system, ensure all the * requirements passed before setting UPDATABLE */ @@ -3644,6 +3883,7 @@ fu_engine_get_details (FuEngine *self, FuEngineRequest *request, gint fd, GError if (!fu_engine_check_requirements (self, request, task, FWUPD_INSTALL_FLAG_OFFLINE | FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | + FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH | FWUPD_INSTALL_FLAG_ALLOW_OLDER, &error_req)) { g_debug ("%s failed requirement checks: %s", @@ -3716,27 +3956,6 @@ fu_engine_get_devices (FuEngine *self, GError **error) return g_steal_pointer (&devices); } -/** - * fu_engine_get_device: - * @self: A #FuEngine - * @device_id: A device ID - * @error: A #GError, or %NULL - * - * Gets a specific device. - * - * Returns: (transfer full): a device, or %NULL if not found - **/ -FuDevice * -fu_engine_get_device (FuEngine *self, const gchar *device_id, GError **error) -{ - FuDevice *device; - - device = fu_device_list_get_by_id (self->device_list, device_id, error); - if (device == NULL) - return NULL; - return device; -} - /** * fu_engine_get_devices_by_guid: * @self: A #FuEngine @@ -3775,6 +3994,27 @@ fu_engine_get_devices_by_guid (FuEngine *self, const gchar *guid, GError **error return g_steal_pointer (&devices); } +static void +fu_engine_get_history_set_hsi_attrs (FuEngine *self, FuDevice *device) +{ + g_autoptr(GPtrArray) vals = NULL; + + /* ensure up to date */ + fu_engine_ensure_security_attrs (self); + + /* add attributes */ + vals = fu_security_attrs_get_all (self->host_security_attrs); + for (guint i = 0; i < vals->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index (vals, i); + const gchar *tmp; + tmp = fwupd_security_attr_result_to_string (fwupd_security_attr_get_result (attr)); + fu_device_set_metadata (device, fwupd_security_attr_get_appstream_id (attr), tmp); + } + + /* computed value */ + fu_device_set_metadata (device, "HSI", self->host_security_id); +} + /** * fu_engine_get_history: * @self: A #FuEngine @@ -3803,6 +4043,13 @@ fu_engine_get_history (FuEngine *self, GError **error) return NULL; } + /* if this is the system firmware device, add the HSI attrs */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index (devices, i); + if (fu_device_has_instance_id (dev, "main-system-firmware")) + fu_engine_get_history_set_hsi_attrs (self, dev); + } + /* try to set the remote ID for each device */ for (guint i = 0; i < devices->len; i++) { FuDevice *dev = g_ptr_array_index (devices, i); @@ -3830,6 +4077,19 @@ fu_engine_get_history (FuEngine *self, GError **error) return g_steal_pointer (&devices); } +#if !GLIB_CHECK_VERSION(2,62,0) +static GPtrArray * +g_ptr_array_copy (GPtrArray *array, GCopyFunc func, gpointer user_data) +{ + GPtrArray *new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + for (guint i = 0; i < array->len; i++) { + GObject *obj = g_ptr_array_index (array, i); + g_ptr_array_add (new, g_object_ref (obj)); + } + return new; +} +#endif + /** * fu_engine_get_remotes: * @self: A #FuEngine @@ -3855,7 +4115,9 @@ fu_engine_get_remotes (FuEngine *self, GError **error) "No remotes configured"); return NULL; } - return g_ptr_array_ref (remotes); + + /* deep copy so the remote list can be kept up to date */ + return g_ptr_array_copy (remotes, (GCopyFunc) g_object_ref, NULL); } /** @@ -3896,6 +4158,14 @@ fu_engine_sort_releases_cb (gconstpointer a, gconstpointer b, gpointer user_data FuDevice *device = FU_DEVICE (user_data); FwupdRelease *rel_a = FWUPD_RELEASE (*((FwupdRelease **) a)); FwupdRelease *rel_b = FWUPD_RELEASE (*((FwupdRelease **) b)); + gint rc; + + /* first by branch */ + rc = g_strcmp0 (fwupd_release_get_branch (rel_b), fwupd_release_get_branch (rel_a)); + if (rc != 0) + return rc; + + /* then by version */ return fu_common_vercmp_full (fwupd_release_get_version (rel_b), fwupd_release_get_version (rel_a), fu_device_get_version_format (device)); @@ -3905,6 +4175,8 @@ static gboolean fu_engine_check_release_is_approved (FuEngine *self, FwupdRelease *rel) { GPtrArray *csums = fwupd_release_get_checksums (rel); + if (self->approved_firmware == NULL) + return FALSE; for (guint i = 0; i < csums->len; i++) { const gchar *csum = g_ptr_array_index (csums, i); g_debug ("checking %s against approved list", csum); @@ -3922,7 +4194,6 @@ fu_engine_check_release_is_blocked (FuEngine *self, FwupdRelease *rel) return FALSE; for (guint i = 0; i < csums->len; i++) { const gchar *csum = g_ptr_array_index (csums, i); - g_debug ("checking %s against blocked list", csum); if (g_hash_table_lookup (self->blocked_firmware, csum) != NULL) return TRUE; } @@ -3937,6 +4208,7 @@ fu_engine_add_releases_for_device_component (FuEngine *self, GPtrArray *releases, GError **error) { + FwupdFeatureFlags feature_flags; FwupdVersionFormat fmt = fu_device_get_version_format (device); g_autoptr(GError) error_local = NULL; g_autoptr(FuInstallTask) task = fu_install_task_new (device, component); @@ -3944,6 +4216,7 @@ fu_engine_add_releases_for_device_component (FuEngine *self, if (!fu_engine_check_requirements (self, request, task, FWUPD_INSTALL_FLAG_OFFLINE | + FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH | FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | FWUPD_INSTALL_FLAG_ALLOW_OLDER, error)) @@ -3959,6 +4232,7 @@ fu_engine_add_releases_for_device_component (FuEngine *self, g_propagate_error (error, g_steal_pointer (&error_local)); return FALSE; } + feature_flags = fu_engine_request_get_feature_flags (request); for (guint i = 0; i < releases_tmp->len; i++) { XbNode *release = g_ptr_array_index (releases_tmp, i); const gchar *remote_id; @@ -3992,6 +4266,18 @@ fu_engine_add_releases_for_device_component (FuEngine *self, if (checksums->len == 0) continue; + /* different branch */ + if (g_strcmp0 (fwupd_release_get_branch (rel), + fu_device_get_branch (device)) != 0) { + if ((feature_flags & FWUPD_FEATURE_FLAG_SWITCH_BRANCH) == 0) { + g_debug ("client does not understand branches, skipping %s:%s", + fwupd_release_get_branch (rel), + fwupd_release_get_version (rel)); + continue; + } + fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH); + } + /* test for upgrade or downgrade */ vercmp = fu_common_vercmp_full (fwupd_release_get_version (rel), fu_device_get_version (device), @@ -4013,7 +4299,7 @@ fu_engine_add_releases_for_device_component (FuEngine *self, if (fu_engine_check_release_is_blocked (self, rel)) fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL); - /* check if remote is whitelisting firmware */ + /* check if remote is filtering firmware */ remote_id = fwupd_release_get_remote_id (rel); if (remote_id != NULL) { FwupdRemote *remote = fu_engine_get_remote_by_id (self, remote_id, NULL); @@ -4043,6 +4329,14 @@ fu_engine_add_releases_for_device_component (FuEngine *self, return TRUE; } +static const gchar * +fu_engine_get_branch_fallback (const gchar *nullable_branch) +{ + if (nullable_branch == NULL) + return "default"; + return nullable_branch; +} + GPtrArray * fu_engine_get_releases_for_device (FuEngine *self, FuEngineRequest *request, @@ -4054,6 +4348,7 @@ fu_engine_get_releases_for_device (FuEngine *self, const gchar *version; g_autoptr(GError) error_all = NULL; g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) branches = NULL; g_autoptr(GPtrArray) components = NULL; g_autoptr(GString) xpath = g_string_new (NULL); @@ -4120,6 +4415,21 @@ fu_engine_get_releases_for_device (FuEngine *self, } } + /* are there multiple branches available */ + branches = g_ptr_array_new_with_free_func (g_free); + g_ptr_array_add (branches, + g_strdup (fu_engine_get_branch_fallback (fu_device_get_branch (device)))); + for (guint i = 0; i < releases->len; i++) { + FwupdRelease *rel_tmp = FWUPD_RELEASE (g_ptr_array_index (releases, i)); + const gchar *branch_tmp = fu_engine_get_branch_fallback (fwupd_release_get_branch (rel_tmp)); + if (g_ptr_array_find_with_equal_func (branches, branch_tmp, + g_str_equal, NULL)) + continue; + g_ptr_array_add (branches, g_strdup (branch_tmp)); + } + if (branches->len > 1) + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES); + /* return the compound error */ if (releases->len == 0) { if (error_all != NULL) { @@ -4249,6 +4559,16 @@ fu_engine_get_downgrades (FuEngine *self, fu_device_get_version_lowest (device)); continue; } + + /* different branch */ + if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH)) { + g_debug ("ignoring release %s as branch %s, and device is %s", + fwupd_release_get_version (rel_tmp), + fwupd_release_get_branch (rel_tmp), + fu_device_get_branch (device)); + continue; + } + g_ptr_array_add (releases, g_object_ref (rel_tmp)); } if (error_str->len > 2) @@ -4278,10 +4598,12 @@ GPtrArray * fu_engine_get_approved_firmware (FuEngine *self) { GPtrArray *checksums = g_ptr_array_new_with_free_func (g_free); - g_autoptr(GList) keys = g_hash_table_get_keys (self->approved_firmware); - for (GList *l = keys; l != NULL; l = l->next) { - const gchar *csum = l->data; - g_ptr_array_add (checksums, g_strdup (csum)); + if (self->approved_firmware != NULL) { + g_autoptr(GList) keys = g_hash_table_get_keys (self->approved_firmware); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *csum = l->data; + g_ptr_array_add (checksums, g_strdup (csum)); + } } return checksums; } @@ -4289,6 +4611,12 @@ fu_engine_get_approved_firmware (FuEngine *self) void fu_engine_add_approved_firmware (FuEngine *self, const gchar *checksum) { + if (self->approved_firmware == NULL) { + self->approved_firmware = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + } g_hash_table_add (self->approved_firmware, g_strdup (checksum)); } @@ -4424,7 +4752,7 @@ fu_engine_get_upgrades (FuEngine *self, !fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) { g_string_append_printf (error_str, "%s=same, ", fwupd_release_get_version (rel_tmp)); - g_debug ("ignoring %s as the same as %s", + g_debug ("ignoring %s == %s", fwupd_release_get_version (rel_tmp), fu_device_get_version (device)); continue; @@ -4434,7 +4762,7 @@ fu_engine_get_upgrades (FuEngine *self, if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) { g_string_append_printf (error_str, "%s=older, ", fwupd_release_get_version (rel_tmp)); - g_debug ("ignoring %s as older than %s", + g_debug ("ignoring %s < %s", fwupd_release_get_version (rel_tmp), fu_device_get_version (device)); continue; @@ -4450,6 +4778,15 @@ fu_engine_get_upgrades (FuEngine *self, continue; } + /* different branch */ + if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH)) { + g_debug ("ignoring release %s as branch %s, and device is %s", + fwupd_release_get_version (rel_tmp), + fwupd_release_get_branch (rel_tmp), + fu_device_get_branch (device)); + continue; + } + g_ptr_array_add (releases, g_object_ref (rel_tmp)); } if (error_str->len > 2) @@ -4570,7 +4907,12 @@ fu_engine_plugins_setup (FuEngine *self) g_autoptr(GError) error = NULL; FuPlugin *plugin = g_ptr_array_index (plugins, i); if (!fu_plugin_runner_startup (plugin, &error)) { - fu_plugin_set_enabled (plugin, FALSE); + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED); + if (g_error_matches (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED)) { + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_NO_HARDWARE); + } g_message ("disabling plugin because: %s", error->message); } } @@ -4609,7 +4951,7 @@ fu_engine_plugins_coldplug (FuEngine *self, gboolean is_recoldplug) g_message ("failed recoldplug: %s", error->message); } else { if (!fu_plugin_runner_coldplug (plugin, &error)) { - fu_plugin_set_enabled (plugin, FALSE); + fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED); g_message ("disabling plugin because: %s", error->message); } @@ -4627,7 +4969,7 @@ fu_engine_plugins_coldplug (FuEngine *self, gboolean is_recoldplug) /* print what we do have */ for (guint i = 0; i < plugins->len; i++) { FuPlugin *plugin = g_ptr_array_index (plugins, i); - if (!fu_plugin_get_enabled (plugin)) + if (fu_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED)) continue; g_string_append_printf (str, "%s, ", fu_plugin_get_name (plugin)); } @@ -4811,7 +5153,7 @@ fu_engine_device_inherit_history (FuEngine *self, FuDevice *device) void fu_engine_add_device (FuEngine *self, FuDevice *device) { - GPtrArray *blacklisted_devices; + GPtrArray *disabled_devices; GPtrArray *device_guids; g_autoptr(XbNode) component = NULL; @@ -4824,14 +5166,14 @@ fu_engine_add_device (FuEngine *self, FuDevice *device) return; } - /* is this GUID blacklisted */ - blacklisted_devices = fu_config_get_blacklist_devices (self->config); - for (guint i = 0; i < blacklisted_devices->len; i++) { - const gchar *blacklisted_guid = g_ptr_array_index (blacklisted_devices, i); + /* is this GUID disabled */ + disabled_devices = fu_config_get_disabled_devices (self->config); + for (guint i = 0; i < disabled_devices->len; i++) { + const gchar *disabled_guid = g_ptr_array_index (disabled_devices, i); for (guint j = 0; j < device_guids->len; j++) { const gchar *device_guid = g_ptr_array_index (device_guids, j); - if (g_strcmp0 (blacklisted_guid, device_guid) == 0) { - g_debug ("%s [%s] is blacklisted [%s], ignoring from %s", + if (g_strcmp0 (disabled_guid, device_guid) == 0) { + g_debug ("%s [%s] is disabled [%s], ignoring from %s", fu_device_get_name (device), fu_device_get_id (device), device_guid, @@ -4919,6 +5261,9 @@ fu_engine_add_device (FuEngine *self, FuDevice *device) /* create new device */ fu_device_list_add (self->device_list, device); + /* fix order */ + fu_device_list_depsolve_order (self->device_list, device); + /* fixup the name and format as needed from cached metadata */ if (component != NULL) fu_engine_md_refresh_device_from_component (self, device, component); @@ -4947,12 +5292,26 @@ fu_engine_plugin_rules_changed_cb (FuPlugin *plugin, gpointer user_data) { FuEngine *self = FU_ENGINE (user_data); GPtrArray *rules = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_INHIBITS_IDLE); + if (rules == NULL) + return; for (guint j = 0; j < rules->len; j++) { const gchar *tmp = g_ptr_array_index (rules, j); fu_idle_inhibit (self->idle, tmp); } } +static void +fu_engine_plugin_security_changed_cb (FuPlugin *plugin, gpointer user_data) +{ + FuEngine *self = FU_ENGINE (user_data); + + /* invalidate host security attributes */ + g_clear_pointer (&self->host_security_id, g_free); + + /* make UI refresh */ + fu_engine_emit_changed (self); +} + static void fu_engine_plugin_device_removed_cb (FuPlugin *plugin, FuDevice *device, @@ -4967,7 +5326,9 @@ fu_engine_plugin_device_removed_cb (FuPlugin *plugin, fu_device_get_id (device), &error); if (device_tmp == NULL) { - g_debug ("%s", error->message); + g_debug ("failed to find device %s: %s", + fu_device_get_id (device), + error->message); return; } @@ -4976,7 +5337,9 @@ fu_engine_plugin_device_removed_cb (FuPlugin *plugin, fu_device_get_plugin (device), &error); if (plugin_old == NULL) { - g_debug ("%s", error->message); + g_debug ("failed to find plugin %s: %s", + fu_device_get_plugin (device), + error->message); return; } @@ -4997,7 +5360,7 @@ static gboolean fu_engine_recoldplug_delay_cb (gpointer user_data) { FuEngine *self = (FuEngine *) user_data; - g_debug ("performing a recoldplug"); + g_debug ("recoldplugging"); fu_engine_plugins_coldplug (self, TRUE); self->coldplug_id = 0; return FALSE; @@ -5033,13 +5396,9 @@ fu_engine_udev_device_add (FuEngine *self, GUdevDevice *udev_device) const gchar *plugin_name = g_ptr_array_index (possible_plugins, i); g_autoptr(GError) error = NULL; - plugin = fu_plugin_list_find_by_name (self->plugin_list, - plugin_name, &error); - if (plugin == NULL) { - g_debug ("failed to find specified plugin %s: %s", - plugin_name, error->message); + plugin = fu_plugin_list_find_by_name (self->plugin_list, plugin_name, NULL); + if (plugin == NULL) continue; - } if (!fu_plugin_runner_udev_device_added (plugin, device, &error)) { if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) { @@ -5177,8 +5536,9 @@ fu_engine_enumerate_udev (FuEngine *self) const gchar *subsystem = g_ptr_array_index (self->udev_subsystems, i); GList *devices = g_udev_client_query_by_subsystem (self->gudev_client, subsystem); - g_debug ("%u devices with subsystem %s", - g_list_length (devices), subsystem); + if (g_list_length (devices) > 0) + g_debug ("%u devices with subsystem %s", + g_list_length (devices), subsystem); for (GList *l = devices; l != NULL; l = l->next) { GUdevDevice *udev_device = l->data; fu_engine_udev_device_add (self, udev_device); @@ -5242,11 +5602,11 @@ fu_engine_add_plugin (FuEngine *self, FuPlugin *plugin) } static gboolean -fu_engine_is_plugin_name_blacklisted (FuEngine *self, const gchar *name) +fu_engine_is_plugin_name_disabled (FuEngine *self, const gchar *name) { - GPtrArray *blacklist = fu_config_get_blacklist_plugins (self->config); - for (guint i = 0; i < blacklist->len; i++) { - const gchar *name_tmp = g_ptr_array_index (blacklist, i); + GPtrArray *disabled = fu_config_get_disabled_plugins (self->config); + for (guint i = 0; i < disabled->len; i++) { + const gchar *name_tmp = g_ptr_array_index (disabled, i); if (g_strcmp0 (name_tmp, name) == 0) return TRUE; } @@ -5254,7 +5614,7 @@ fu_engine_is_plugin_name_blacklisted (FuEngine *self, const gchar *name) } static gboolean -fu_engine_is_plugin_name_whitelisted (FuEngine *self, const gchar *name) +fu_engine_is_plugin_name_enabled (FuEngine *self, const gchar *name) { if (self->plugin_filter->len == 0) return TRUE; @@ -5315,6 +5675,224 @@ fu_engine_get_host_machine_id (FuEngine *self) return self->host_machine_id; } +static void +fu_engine_ensure_security_attrs_tainted (FuEngine *self) +{ + gboolean disabled_plugins = FALSE; + GPtrArray *disabled = fu_config_get_disabled_plugins (self->config); + g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS); + fwupd_security_attr_set_plugin (attr, "core"); + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + + fu_security_attrs_append (self->host_security_attrs, attr); + for (guint i = 0; i < disabled->len; i++) { + const gchar *name_tmp = g_ptr_array_index (disabled, i); + if (g_strcmp0 (name_tmp, "test") != 0 && + g_strcmp0 (name_tmp, "invalid") != 0) { + disabled_plugins = TRUE; + break; + } + } + if (self->tainted) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_TAINTED); + return; + } + if (self->plugin_filter->len > 0 || disabled_plugins) { + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED); +} + +static void +fu_engine_ensure_security_attrs_supported (FuEngine *self) +{ + FwupdRelease *rel_current = NULL; + FwupdRelease *rel_newest = NULL; + g_autoptr(FwupdSecurityAttr) attr_a = NULL; + g_autoptr(FwupdSecurityAttr) attr_u = NULL; + guint64 now = (guint64) g_get_real_time () / G_USEC_PER_SEC; + g_autoptr(FuDevice) device = NULL; + g_autoptr(GPtrArray) releases = NULL; + + attr_u = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_FWUPD_UPDATES); + fwupd_security_attr_set_plugin (attr_u, "core"); + fwupd_security_attr_add_flag (attr_u, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES); + fu_security_attrs_append (self->host_security_attrs, attr_u); + attr_a = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_FWUPD_ATTESTATION); + fwupd_security_attr_set_plugin (attr_a, "core"); + fwupd_security_attr_add_flag (attr_a, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION); + fu_security_attrs_append (self->host_security_attrs, attr_a); + /* get device */ + device = fu_device_list_get_by_guid (self->device_list, + /* main-system-firmware */ + "230c8b18-8d9b-53ec-838b-6cfc0383493a", + NULL); + + /* find out if there is firmware less than 12 months old */ + if (device == NULL) { + fwupd_security_attr_set_result (attr_u, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + } else { + g_autoptr(FuEngineRequest) request = fu_engine_request_new (); + fu_engine_request_set_feature_flags (request, ~0); + releases = fu_engine_get_releases_for_device (self, request, device, NULL); + if (releases == NULL) { + fwupd_security_attr_set_result (attr_u, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + } else { + /* check the age */ + for (guint i = 0; i < releases->len; i++) { + FwupdRelease *rel_tmp = g_ptr_array_index (releases, i); + if (rel_newest == NULL || + fwupd_release_get_created (rel_tmp) > fwupd_release_get_created (rel_newest)) + rel_newest = rel_tmp; + } + g_debug ("newest release is %" G_GUINT64_FORMAT " months old", + (now - fwupd_release_get_created (rel_newest)) / (60 * 60 * 24 * 30)); + fwupd_security_attr_set_result (attr_u, FWUPD_SECURITY_ATTR_RESULT_SUPPORTED); + if (now - fwupd_release_get_created (rel_newest) < 60 * 60 * 24 * 30 * 12) { + fwupd_security_attr_add_flag (attr_u, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr_a, FWUPD_SECURITY_ATTR_RESULT_SUPPORTED); + } + } + } + + /* do we have attestation checksums */ + if (releases != NULL) { + for (guint i = 0; i < releases->len; i++) { + FwupdRelease *rel_tmp = g_ptr_array_index (releases, i); + if (fu_common_vercmp_full (fu_device_get_version (device), + fwupd_release_get_version (rel_tmp), + fu_device_get_version_format (device)) == 0) { + rel_current = rel_tmp; + break; + } + } + } + if (rel_current == NULL) { + fwupd_security_attr_set_result (attr_a, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + } else { + g_autoptr(GError) error_local = NULL; + if (!fu_engine_verify (self, fu_device_get_id (device), &error_local)) { + fwupd_security_attr_set_result (attr_a, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + g_debug ("Failed to find attestation checksums: %s", error_local->message); + } else { + fwupd_security_attr_add_flag (attr_a, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result (attr_a, FWUPD_SECURITY_ATTR_RESULT_SUPPORTED); + } + } +} + +static gchar * +fu_engine_attrs_calculate_hsi_for_chassis (FuEngine *self) +{ + guint val; + g_autoptr(GError) error = NULL; + + /* get chassis type from SMBIOS data */ + val = fu_smbios_get_integer (self->smbios, + FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, + 0x05, &error); + if (val == G_MAXUINT) { + g_warning ("failed to get chassis type: %s", error->message); + return g_strdup ("HSI-INVALID:chassis"); + } + + /* verify HSI makes sense for this chassis type */ + switch (val) { + case FU_SMBIOS_CHASSIS_KIND_DESKTOP: + case FU_SMBIOS_CHASSIS_KIND_LOW_PROFILE_DESKTOP: + case FU_SMBIOS_CHASSIS_KIND_MINI_TOWER: + case FU_SMBIOS_CHASSIS_KIND_TOWER: + case FU_SMBIOS_CHASSIS_KIND_PORTABLE: + case FU_SMBIOS_CHASSIS_KIND_LAPTOP: + case FU_SMBIOS_CHASSIS_KIND_NOTEBOOK: + case FU_SMBIOS_CHASSIS_KIND_ALL_IN_ONE: + case FU_SMBIOS_CHASSIS_KIND_SUB_NOTEBOOK: + case FU_SMBIOS_CHASSIS_KIND_LUNCH_BOX: + case FU_SMBIOS_CHASSIS_KIND_MAIN_SERVER: + case FU_SMBIOS_CHASSIS_KIND_TABLET: + case FU_SMBIOS_CHASSIS_KIND_CONVERTIBLE: + case FU_SMBIOS_CHASSIS_KIND_DETACHABLE: + case FU_SMBIOS_CHASSIS_KIND_IOT_GATEWAY: + case FU_SMBIOS_CHASSIS_KIND_EMBEDDED_PC: + case FU_SMBIOS_CHASSIS_KIND_MINI_PC: + case FU_SMBIOS_CHASSIS_KIND_STICK_PC: + return fu_security_attrs_calculate_hsi (self->host_security_attrs, + FU_SECURITY_ATTRS_FLAG_ADD_VERSION); + default: + break; + } + + /* failed */ + return g_strdup_printf ("HSI-INVALID:chassis[0x%02x]", val); +} + + +static void +fu_engine_ensure_security_attrs (FuEngine *self) +{ + GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list); + g_autoptr(GPtrArray) items = NULL; + + /* already valid */ + if (self->host_security_id != NULL) + return; + + /* clear old values */ + fu_security_attrs_remove_all (self->host_security_attrs); + + /* built in */ + fu_engine_ensure_security_attrs_tainted (self); + fu_engine_ensure_security_attrs_supported (self); + + /* call into plugins */ + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); + fu_plugin_runner_add_security_attrs (plugin_tmp, self->host_security_attrs); + } + + /* set the fallback names for clients without native translations */ + items = fu_security_attrs_get_all (self->host_security_attrs); + for (guint i = 0; i < items->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index (items, i); + if (fwupd_security_attr_get_name (attr) == NULL) { + g_autofree gchar *name_tmp = fu_security_attr_get_name (attr); + if (name_tmp == NULL) { + g_warning ("failed to get fallback for %s", + fwupd_security_attr_get_appstream_id (attr)); + continue; + } + fwupd_security_attr_set_name (attr, name_tmp); + } + } + + /* set the obsoletes flag for each attr */ + fu_security_attrs_depsolve (self->host_security_attrs); + + /* distil into one simple string */ + g_free (self->host_security_id); + self->host_security_id = fu_engine_attrs_calculate_hsi_for_chassis (self); +} + +const gchar * +fu_engine_get_host_security_id (FuEngine *self) +{ + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + fu_engine_ensure_security_attrs (self); + return self->host_security_id; +} + +FuSecurityAttrs * +fu_engine_get_host_security_attrs (FuEngine *self) +{ + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + fu_engine_ensure_security_attrs (self); + return g_object_ref (self->host_security_attrs); +} + gboolean fu_engine_load_plugins (FuEngine *self, GError **error) { @@ -5322,6 +5900,8 @@ fu_engine_load_plugins (FuEngine *self, GError **error) g_autoptr(GDir) dir = NULL; g_autofree gchar *plugin_path = NULL; g_autofree gchar *suffix = g_strdup_printf (".%s", G_MODULE_SUFFIX); + g_autoptr(GPtrArray) plugins_disabled = g_ptr_array_new_with_free_func (g_free); + g_autoptr(GPtrArray) plugins_disabled_rt = g_ptr_array_new_with_free_func (g_free); /* search */ plugin_path = fu_common_get_path (FU_PATH_KIND_PLUGINDIR_PKG); @@ -5338,16 +5918,13 @@ fu_engine_load_plugins (FuEngine *self, GError **error) if (!g_str_has_suffix (fn, suffix)) continue; - /* is blacklisted */ + /* is disabled */ name = fu_plugin_guess_name_from_fn (fn); if (name == NULL) continue; - if (fu_engine_is_plugin_name_blacklisted (self, name)) { - g_debug ("plugin %s is blacklisted", name); - continue; - } - if (!fu_engine_is_plugin_name_whitelisted (self, name)) { - g_debug ("plugin %s is not whitelisted", name); + if (fu_engine_is_plugin_name_disabled (self, name) || + !fu_engine_is_plugin_name_enabled (self, name)) { + g_ptr_array_add (plugins_disabled, g_steal_pointer (&name)); continue; } @@ -5365,20 +5942,19 @@ fu_engine_load_plugins (FuEngine *self, GError **error) g_signal_connect (plugin, "add-firmware-gtype", G_CALLBACK (fu_engine_plugin_add_firmware_gtype_cb), self); - g_debug ("adding plugin %s", filename); /* if loaded from fu_engine_load() open the plugin */ if (self->usb_ctx != NULL) { if (!fu_plugin_open (plugin, filename, &error_local)) { - g_warning ("%s", error_local->message); + g_warning ("cannot load: %s", error_local->message); + fu_engine_add_plugin (self, plugin); continue; } } - /* self disabled */ - if (!fu_plugin_get_enabled (plugin)) { - g_debug ("%s self disabled", - fu_plugin_get_name (plugin)); + /* runtime disabled */ + if (fu_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED)) { + g_ptr_array_add (plugins_disabled_rt, g_steal_pointer (&name)); continue; } @@ -5404,11 +5980,28 @@ fu_engine_load_plugins (FuEngine *self, GError **error) g_signal_connect (plugin, "rules-changed", G_CALLBACK (fu_engine_plugin_rules_changed_cb), self); + g_signal_connect (plugin, "security-changed", + G_CALLBACK (fu_engine_plugin_security_changed_cb), + self); /* add */ fu_engine_add_plugin (self, plugin); } + /* show list */ + if (plugins_disabled->len > 0) { + g_autofree gchar *str = NULL; + g_ptr_array_add (plugins_disabled, NULL); + str = g_strjoinv (", ", (gchar **) plugins_disabled->pdata); + g_debug ("plugins disabled: %s", str); + } + if (plugins_disabled_rt->len > 0) { + g_autofree gchar *str = NULL; + g_ptr_array_add (plugins_disabled_rt, NULL); + str = g_strjoinv (", ", (gchar **) plugins_disabled_rt->pdata); + g_debug ("plugins runtime-disabled: %s", str); + } + /* depsolve into the correct order */ if (!fu_plugin_list_depsolve (self->plugin_list, error)) return FALSE; @@ -5500,13 +6093,9 @@ fu_engine_usb_device_added_cb (GUsbContext *ctx, const gchar *plugin_name = g_ptr_array_index (possible_plugins, i); g_autoptr(GError) error = NULL; - plugin = fu_plugin_list_find_by_name (self->plugin_list, - plugin_name, &error); - if (plugin == NULL) { - g_debug ("failed to find specified plugin %s: %s", - plugin_name, error->message); + plugin = fu_plugin_list_find_by_name (self->plugin_list, plugin_name, NULL); + if (plugin == NULL) continue; - } if (!fu_plugin_runner_usb_device_added (plugin, device, &error)) { if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) { @@ -5556,6 +6145,7 @@ fu_engine_update_history_device (FuEngine *self, FuDevice *dev_history, GError * FwupdRelease *rel_history; g_autofree gchar *btime = NULL; g_autoptr(FuDevice) dev = NULL; + g_autoptr(GHashTable) metadata_device = NULL; /* is in the device list */ dev = fu_device_list_get_by_id (self->device_list, @@ -5584,6 +6174,19 @@ fu_engine_update_history_device (FuEngine *self, FuDevice *dev_history, GError * return TRUE; } + /* save any additional report metadata */ + metadata_device = fu_device_report_metadata_post (dev); + if (metadata_device != NULL && g_hash_table_size (metadata_device) > 0) { + fwupd_release_add_metadata (rel_history, metadata_device); + if (!fu_history_set_device_metadata (self->history, + fu_device_get_id (dev_history), + fwupd_release_get_metadata (rel_history), + error)) { + g_prefix_error (error, "failed to set metadata: "); + return FALSE; + } + } + /* the system is running with the new firmware version */ if (fu_common_vercmp_full (fu_device_get_version (dev), fwupd_release_get_version (rel_history), @@ -5603,6 +6206,7 @@ fu_engine_update_history_device (FuEngine *self, FuDevice *dev_history, GError * fu_device_set_version (dev_history, fu_device_get_version (dev)); fu_device_remove_flag (dev_history, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); fu_device_set_update_state (dev_history, FWUPD_UPDATE_STATE_SUCCESS); + fu_device_set_update_error (dev_history, NULL); return fu_history_modify_device (self->history, dev_history, error); } @@ -5619,11 +6223,14 @@ fu_engine_update_history_device (FuEngine *self, FuDevice *dev_history, GError * if (fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED && fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED_TRANSIENT) { g_debug ("falling back to generic failure"); + fu_device_set_update_state (dev_history, FWUPD_UPDATE_STATE_FAILED); fu_device_set_update_error (dev_history, "failed to run update on reboot"); + } else { + fu_device_set_update_state (dev_history, fu_device_get_update_state (dev)); + fu_device_set_update_error (dev_history, fu_device_get_update_error (dev)); } /* update the state in the database */ - fu_device_set_update_error (dev_history, fu_device_get_update_error (dev)); return fu_history_modify_device (self->history, dev_history, error); } @@ -5645,8 +6252,10 @@ fu_engine_update_history_database (FuEngine *self, GError **error) continue; /* try to save the new update-state, but ignoring any error */ - if (!fu_engine_update_history_device (self, dev, &error_local)) - g_warning ("%s", error_local->message); + if (!fu_engine_update_history_device (self, dev, &error_local)) { + g_warning ("failed to update history database: %s", + error_local->message); + } } return TRUE; } @@ -5730,7 +6339,7 @@ fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error) /* cache machine ID so we can use it from a sandboxed app */ self->host_machine_id = fwupd_build_machine_id ("fwupd", &error_local); if (self->host_machine_id == NULL) - g_debug ("%s", error_local->message); + g_debug ("failed to build machine-id: %s", error_local->message); #endif /* read config file */ if (!fu_config_load (self->config, error)) { @@ -5798,6 +6407,7 @@ fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error) /* add the "built-in" firmware types */ fu_engine_add_firmware_gtype (self, "raw", FU_TYPE_FIRMWARE); fu_engine_add_firmware_gtype (self, "dfu", FU_TYPE_DFU_FIRMWARE); + fu_engine_add_firmware_gtype (self, "fmap", FU_TYPE_FMAP_FIRMWARE); fu_engine_add_firmware_gtype (self, "ihex", FU_TYPE_IHEX_FIRMWARE); fu_engine_add_firmware_gtype (self, "srec", FU_TYPE_SREC_FIRMWARE); @@ -5970,6 +6580,7 @@ fu_engine_init (FuEngine *self) self->history = fu_history_new (); self->plugin_list = fu_plugin_list_new (); self->plugin_filter = g_ptr_array_new_with_free_func (g_free); + self->host_security_attrs = fu_security_attrs_new (); self->udev_subsystems = g_ptr_array_new_with_free_func (g_free); #ifdef HAVE_GUDEV self->udev_changed_ids = g_hash_table_new_full (g_str_hash, g_str_equal, @@ -5977,7 +6588,6 @@ fu_engine_init (FuEngine *self) #endif self->runtime_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); self->compile_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - self->approved_firmware = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); self->firmware_gtypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); g_signal_connect (self->config, "changed", @@ -6045,10 +6655,14 @@ fu_engine_finalize (GObject *obj) #endif if (self->coldplug_id != 0) g_source_remove (self->coldplug_id); + if (self->approved_firmware != NULL) + g_hash_table_unref (self->approved_firmware); if (self->blocked_firmware != NULL) g_hash_table_unref (self->blocked_firmware); g_free (self->host_machine_id); + g_free (self->host_security_id); + g_object_unref (self->host_security_attrs); g_object_unref (self->idle); g_object_unref (self->config); g_object_unref (self->remote_list); @@ -6065,7 +6679,6 @@ fu_engine_finalize (GObject *obj) #endif g_hash_table_unref (self->runtime_versions); g_hash_table_unref (self->compile_versions); - g_hash_table_unref (self->approved_firmware); g_hash_table_unref (self->firmware_gtypes); g_object_unref (self->plugin_list); diff --git a/src/fu-engine.h b/src/fu-engine.h index 0c344fd39..47687578c 100644 --- a/src/fu-engine.h +++ b/src/fu-engine.h @@ -17,6 +17,7 @@ #include "fu-engine-request.h" #include "fu-install-task.h" #include "fu-plugin.h" +#include "fu-security-attrs.h" #define FU_TYPE_ENGINE (fu_engine_get_type ()) G_DECLARE_FINAL_TYPE (FuEngine, fu_engine, FU, ENGINE, GObject) @@ -50,6 +51,7 @@ gboolean fu_engine_load_plugins (FuEngine *self, gboolean fu_engine_get_tainted (FuEngine *self); const gchar *fu_engine_get_host_product (FuEngine *self); const gchar *fu_engine_get_host_machine_id (FuEngine *self); +const gchar *fu_engine_get_host_security_id (FuEngine *self); FwupdStatus fu_engine_get_status (FuEngine *self); XbSilo *fu_engine_get_silo_from_blob (FuEngine *self, GBytes *blob_cab, @@ -86,6 +88,9 @@ GPtrArray *fu_engine_get_upgrades (FuEngine *self, FwupdDevice *fu_engine_get_results (FuEngine *self, const gchar *device_id, GError **error); +FuSecurityAttrs *fu_engine_get_host_security_attrs (FuEngine *self); +GHashTable *fu_engine_get_report_metadata (FuEngine *self, + GError **error); gboolean fu_engine_clear_results (FuEngine *self, const gchar *device_id, GError **error); @@ -108,7 +113,7 @@ gboolean fu_engine_verify (FuEngine *self, gboolean fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error); -GBytes *fu_engine_firmware_read (FuEngine *self, +GBytes *fu_engine_firmware_dump (FuEngine *self, FuDevice *device, FwupdInstallFlags flags, GError **error); @@ -189,6 +194,8 @@ void fu_engine_add_plugin (FuEngine *self, void fu_engine_add_runtime_version (FuEngine *self, const gchar *component_id, const gchar *version); +gboolean fu_engine_check_trust (FuInstallTask *task, + GError **error); gboolean fu_engine_check_requirements (FuEngine *self, FuEngineRequest *request, FuInstallTask *task, diff --git a/src/fu-firmware-dump.c b/src/fu-firmware-dump.c index d7045db88..eaa90c0bf 100644 --- a/src/fu-firmware-dump.c +++ b/src/fu-firmware-dump.c @@ -45,7 +45,10 @@ main (int argc, char **argv) g_printerr ("firmware invalid type, expected .srec or .hex\n"); return 2; } - if (!fu_firmware_parse (firmware, blob, FWUPD_INSTALL_FLAG_FORCE, &error)) { + if (!fu_firmware_parse (firmware, blob, + FWUPD_INSTALL_FLAG_IGNORE_VID_PID | + FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM, + &error)) { g_printerr ("failed to parse file: %s\n", error->message); return 3; } diff --git a/src/fu-history.c b/src/fu-history.c index c8912c205..25627ed6e 100644 --- a/src/fu-history.c +++ b/src/fu-history.c @@ -288,7 +288,7 @@ fu_history_migrate_database_v5 (FuHistory *self, GError **error) return TRUE; } -/* returns 0 if database is not initialised */ +/* returns 0 if database is not initialized */ static guint fu_history_get_schema_version (FuHistory *self) { @@ -543,6 +543,62 @@ fu_history_modify_device (FuHistory *self, FuDevice *device, GError **error) return fu_history_stmt_exec (self, stmt, NULL, error); } +/** + * fu_history_set_device_metadata: + * @self: A #FuHistory + * @device_id: A DeviceID string + * @metadata: A #GHashTable of string:string + * @error: A #GError or NULL + * + * Modify a device in the history database + * + * Returns: @TRUE if successful, @FALSE for failure + * + * Since: 1.5.0 + **/ +gboolean +fu_history_set_device_metadata (FuHistory *self, + const gchar *device_id, + GHashTable *metadata, + GError **error) +{ + gint rc; + g_autofree gchar *metadata_str = NULL; + g_autoptr(GRWLockWriterLocker) locker = NULL; + g_autoptr(sqlite3_stmt) stmt = NULL; + + g_return_val_if_fail (FU_IS_HISTORY (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + + /* lazy load */ + if (!fu_history_load (self, error)) + return FALSE; + + /* overwrite entry if it exists */ + locker = g_rw_lock_writer_locker_new (&self->db_mutex); + g_return_val_if_fail (locker != NULL, FALSE); + g_debug ("modifying %s", device_id); + rc = sqlite3_prepare_v2 (self->db, + "UPDATE history SET " + "metadata = ?1 " + "WHERE device_id = ?2;", + -1, &stmt, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "failed to prepare SQL to update history: %s", + sqlite3_errmsg (self->db)); + return FALSE; + } + + + /* metadata is stored as a simple string */ + metadata_str = _convert_hash_to_string (metadata); + sqlite3_bind_text (stmt, 1, metadata_str, -1, SQLITE_STATIC); + sqlite3_bind_text (stmt, 2, device_id, -1, SQLITE_STATIC); + + return fu_history_stmt_exec (self, stmt, NULL, error); +} + /** * fu_history_add_device: * @self: A #FuHistory @@ -796,7 +852,6 @@ fu_history_get_device_by_id (FuHistory *self, const gchar *device_id, GError **e /* get all the devices */ locker = g_rw_lock_reader_locker_new (&self->db_mutex); g_return_val_if_fail (locker != NULL, NULL); - g_debug ("get device"); rc = sqlite3_prepare_v2 (self->db, "SELECT device_id, " "checksum, " @@ -1185,9 +1240,10 @@ fu_history_finalize (GObject *object) { FuHistory *self = FU_HISTORY (object); + g_rw_lock_clear (&self->db_mutex); + if (self->db != NULL) sqlite3_close (self->db); - g_rw_lock_clear (&self->db_mutex); G_OBJECT_CLASS (fu_history_parent_class)->finalize (object); } diff --git a/src/fu-history.h b/src/fu-history.h index 4dd095aae..84e4ae935 100644 --- a/src/fu-history.h +++ b/src/fu-history.h @@ -22,6 +22,10 @@ gboolean fu_history_add_device (FuHistory *self, gboolean fu_history_modify_device (FuHistory *self, FuDevice *device, GError **error); +gboolean fu_history_set_device_metadata (FuHistory *self, + const gchar *device_id, + GHashTable *metadata, + GError **error); gboolean fu_history_remove_device (FuHistory *self, FuDevice *device, GError **error); diff --git a/src/fu-idle.c b/src/fu-idle.c index 5873e4c8e..796f6bcb1 100644 --- a/src/fu-idle.c +++ b/src/fu-idle.c @@ -222,8 +222,8 @@ fu_idle_finalize (GObject *obj) FuIdle *self = FU_IDLE (obj); fu_idle_stop (self); - g_ptr_array_unref (self->items); g_rw_lock_clear (&self->items_mutex); + g_ptr_array_unref (self->items); G_OBJECT_CLASS (fu_idle_parent_class)->finalize (obj); } diff --git a/src/fu-install-task.c b/src/fu-install-task.c index ccab6bc57..77b58b130 100644 --- a/src/fu-install-task.c +++ b/src/fu-install-task.c @@ -198,6 +198,8 @@ fu_install_task_check_requirements (FuInstallTask *self, FwupdInstallFlags flags, GError **error) { + const gchar *branch_new; + const gchar *branch_old; const gchar *protocol; const gchar *version; const gchar *version_release_raw; @@ -258,9 +260,10 @@ fu_install_task_check_requirements (FuInstallTask *self, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "Device %s doesn't support %s", + "Device %s does not support %s, only %s", fu_device_get_name (self->device), - protocol); + protocol, + fu_device_get_protocol (self->device)); return FALSE; } @@ -275,6 +278,22 @@ fu_install_task_check_requirements (FuInstallTask *self, return FALSE; } + /* check the branch is not switching */ + branch_new = xb_node_query_text (self->component, "branch", NULL); + branch_old = fu_device_get_branch (self->device); + if ((flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0 && + g_strcmp0 (branch_old, branch_new) != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s [%s] would switch firmware branch from %s to %s", + fu_device_get_name (self->device), + fu_device_get_id (self->device), + branch_old != NULL ? branch_old : "default", + branch_new != NULL ? branch_new : "default"); + return FALSE; + } + /* no update abilities */ if (!fu_device_has_flag (self->device, FWUPD_DEVICE_FLAG_UPDATABLE) && !fu_device_has_flag (self->device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) { @@ -335,7 +354,8 @@ fu_install_task_check_requirements (FuInstallTask *self, } /* check the version formats match if set in the release */ - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + (flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0) { verfmts = xb_node_query (self->component, "custom/value[@key='LVFS::VersionFormat']", 0, NULL); @@ -377,7 +397,9 @@ fu_install_task_check_requirements (FuInstallTask *self, return FALSE; } self->is_downgrade = vercmp > 0; - if (self->is_downgrade && (flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0) { + if (self->is_downgrade && + (flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0 && + (flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_VERSION_NEWER, diff --git a/src/fu-keyring-utils.c b/src/fu-keyring-utils.c index 2bf46aae5..409220e67 100644 --- a/src/fu-keyring-utils.c +++ b/src/fu-keyring-utils.c @@ -30,10 +30,8 @@ fu_keyring_get_release_flags (XbNode *release, GBytes *blob; blob = g_object_get_data (G_OBJECT (release), "fwupd::ReleaseFlags"); - if (blob == NULL) { - g_debug ("no fwupd::ReleaseFlags set by loader"); + if (blob == NULL) return TRUE; - } if (g_bytes_get_size (blob) != sizeof(FwupdReleaseFlags)) { g_set_error_literal (error, FWUPD_ERROR, diff --git a/src/fu-main.c b/src/fu-main.c index e88e14267..175b0aef6 100644 --- a/src/fu-main.c +++ b/src/fu-main.c @@ -14,12 +14,16 @@ #include #include #include +#ifdef HAVE_POLKIT #include +#endif #include #include #include #include "fwupd-device-private.h" +#include "fwupd-plugin-private.h" +#include "fwupd-security-attr-private.h" #include "fwupd-release-private.h" #include "fwupd-remote-private.h" #include "fwupd-resources.h" @@ -29,14 +33,23 @@ #include "fu-device-private.h" #include "fu-engine.h" #include "fu-install-task.h" +#include "fu-security-attrs-private.h" +#ifdef HAVE_POLKIT #ifndef HAVE_POLKIT_0_114 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitSubject, g_object_unref) #pragma clang diagnostic pop -#endif +#endif /* HAVE_POLKIT_0_114 */ +#endif /* HAVE_POLKIT */ + +typedef enum { + FU_MAIN_MACHINE_KIND_PHYSICAL, + FU_MAIN_MACHINE_KIND_VIRTUAL, + FU_MAIN_MACHINE_KIND_CONTAINER, +} FuMainMachineKind; typedef struct { GDBusConnection *connection; @@ -48,11 +61,14 @@ typedef struct { #if GLIB_CHECK_VERSION(2,63,3) GMemoryMonitor *memory_monitor; #endif +#ifdef HAVE_POLKIT PolkitAuthority *authority; +#endif guint owner_id; FuEngine *engine; gboolean update_in_progress; gboolean pending_sigterm; + FuMainMachineKind machine_kind; } FuMainPrivate; static gboolean @@ -260,6 +276,22 @@ fu_main_device_array_to_variant (FuMainPrivate *priv, FuEngineRequest *request, return g_variant_new ("(aa{sv})", &builder); } +static GVariant * +fu_main_plugin_array_to_variant (GPtrArray *plugins) +{ + GVariantBuilder builder; + + g_return_val_if_fail (plugins->len > 0, NULL); + g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + + for (guint i = 0; i < plugins->len; i++) { + FuDevice *plugin = g_ptr_array_index (plugins, i); + GVariant *tmp = fwupd_plugin_to_variant (FWUPD_PLUGIN (plugin)); + g_variant_builder_add_value (&builder, tmp); + } + return g_variant_new ("(aa{sv})", &builder); +} + static GVariant * fu_main_release_array_to_variant (GPtrArray *results) { @@ -305,7 +337,9 @@ fu_main_result_array_to_variant (GPtrArray *results) typedef struct { GDBusMethodInvocation *invocation; FuEngineRequest *request; +#ifdef HAVE_POLKIT PolkitSubject *subject; +#endif GPtrArray *install_tasks; GPtrArray *action_ids; GPtrArray *checksums; @@ -324,8 +358,10 @@ fu_main_auth_helper_free (FuMainAuthHelper *helper) { if (helper->blob_cab != NULL) g_bytes_unref (helper->blob_cab); +#ifdef HAVE_POLKIT if (helper->subject != NULL) g_object_unref (helper->subject); +#endif if (helper->silo != NULL) g_object_unref (helper->silo); if (helper->request != NULL) @@ -349,6 +385,7 @@ fu_main_auth_helper_free (FuMainAuthHelper *helper) G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMainAuthHelper, fu_main_auth_helper_free) #pragma clang diagnostic pop +#ifdef HAVE_POLKIT /* error may or may not already have been set */ static gboolean fu_main_authorization_is_valid (PolkitAuthorizationResult *auth, GError **error) @@ -376,15 +413,30 @@ fu_main_authorization_is_valid (PolkitAuthorizationResult *auth, GError **error) /* success */ return TRUE; } +#else +static gboolean +fu_main_authorization_is_trusted (FuEngineRequest *request, GError **error) +{ + FwupdDeviceFlags flags = fu_engine_request_get_device_flags (request); + if ((flags & FWUPD_DEVICE_FLAG_TRUSTED) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_AUTH_FAILED, + "permission denied: untrusted client process"); + return FALSE; + } + return TRUE; +} +#endif /* HAVE_POLKIT */ static void fu_main_authorize_unlock_cb (GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitAuthorizationResult) auth = NULL; - /* get result */ fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE); auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source), @@ -393,6 +445,12 @@ fu_main_authorize_unlock_cb (GObject *source, GAsyncResult *res, gpointer user_d g_dbus_method_invocation_return_gerror (helper->invocation, error); return; } +#else + if (!fu_main_authorization_is_trusted (helper->request, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ /* authenticated */ if (!fu_engine_unlock (helper->priv->engine, helper->device_id, &error)) { @@ -409,6 +467,7 @@ fu_main_authorize_set_approved_firmware_cb (GObject *source, GAsyncResult *res, { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitAuthorizationResult) auth = NULL; /* get result */ @@ -419,6 +478,12 @@ fu_main_authorize_set_approved_firmware_cb (GObject *source, GAsyncResult *res, g_dbus_method_invocation_return_gerror (helper->invocation, error); return; } +#else + if (!fu_main_authorization_is_trusted (helper->request, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ /* success */ for (guint i = 0; i < helper->checksums->len; i++) { @@ -433,6 +498,7 @@ fu_main_authorize_set_blocked_firmware_cb (GObject *source, GAsyncResult *res, g { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitAuthorizationResult) auth = NULL; /* get result */ @@ -443,6 +509,12 @@ fu_main_authorize_set_blocked_firmware_cb (GObject *source, GAsyncResult *res, g g_dbus_method_invocation_return_gerror (helper->invocation, error); return; } +#else + if (!fu_main_authorization_is_trusted (helper->request, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ /* success */ if (!fu_engine_set_blocked_firmware (helper->priv->engine, helper->checksums, &error)) { @@ -458,6 +530,7 @@ fu_main_authorize_self_sign_cb (GObject *source, GAsyncResult *res, gpointer use g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; g_autofree gchar *sig = NULL; g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitAuthorizationResult) auth = NULL; /* get result */ @@ -468,6 +541,12 @@ fu_main_authorize_self_sign_cb (GObject *source, GAsyncResult *res, gpointer use g_dbus_method_invocation_return_gerror (helper->invocation, error); return; } +#else + if (!fu_main_authorization_is_trusted (helper->request, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ /* authenticated */ sig = fu_engine_self_sign (helper->priv->engine, helper->value, helper->flags, &error); @@ -485,6 +564,7 @@ fu_main_modify_config_cb (GObject *source, GAsyncResult *res, gpointer user_data { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitAuthorizationResult) auth = NULL; /* get result */ @@ -494,6 +574,12 @@ fu_main_modify_config_cb (GObject *source, GAsyncResult *res, gpointer user_data g_dbus_method_invocation_return_gerror (helper->invocation, error); return; } +#else + if (!fu_main_authorization_is_trusted (helper->request, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ if (!fu_engine_modify_config (helper->priv->engine, helper->key, helper->value, &error)) { g_dbus_method_invocation_return_gerror (helper->invocation, error); @@ -509,6 +595,7 @@ fu_main_authorize_activate_cb (GObject *source, GAsyncResult *res, gpointer user { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitAuthorizationResult) auth = NULL; /* get result */ @@ -519,6 +606,7 @@ fu_main_authorize_activate_cb (GObject *source, GAsyncResult *res, gpointer user g_dbus_method_invocation_return_gerror (helper->invocation, error); return; } +#endif /* HAVE_POLKIT */ /* authenticated */ if (!fu_engine_activate (helper->priv->engine, helper->device_id, &error)) { @@ -535,6 +623,7 @@ fu_main_authorize_verify_update_cb (GObject *source, GAsyncResult *res, gpointer { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitAuthorizationResult) auth = NULL; /* get result */ @@ -545,6 +634,12 @@ fu_main_authorize_verify_update_cb (GObject *source, GAsyncResult *res, gpointer g_dbus_method_invocation_return_gerror (helper->invocation, error); return; } +#else + if (!fu_main_authorization_is_trusted (helper->request, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ /* authenticated */ if (!fu_engine_verify_update (helper->priv->engine, helper->device_id, &error)) { @@ -561,6 +656,7 @@ fu_main_authorize_modify_remote_cb (GObject *source, GAsyncResult *res, gpointer { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitAuthorizationResult) auth = NULL; /* get result */ @@ -571,6 +667,12 @@ fu_main_authorize_modify_remote_cb (GObject *source, GAsyncResult *res, gpointer g_dbus_method_invocation_return_gerror (helper->invocation, error); return; } +#else + if (!fu_main_authorization_is_trusted (helper->request, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ /* authenticated */ if (!fu_engine_modify_remote (helper->priv->engine, @@ -588,6 +690,7 @@ fu_main_authorize_modify_remote_cb (GObject *source, GAsyncResult *res, gpointer static void fu_main_authorize_install_queue (FuMainAuthHelper *helper); +#ifdef HAVE_POLKIT static void fu_main_authorize_install_cb (GObject *source, GAsyncResult *res, gpointer user_data) { @@ -607,6 +710,7 @@ fu_main_authorize_install_cb (GObject *source, GAsyncResult *res, gpointer user_ /* do the next authentication action ID */ fu_main_authorize_install_queue (g_steal_pointer (&helper)); } +#endif /* HAVE_POLKIT */ static void fu_main_authorize_install_queue (FuMainAuthHelper *helper_ref) @@ -616,6 +720,7 @@ fu_main_authorize_install_queue (FuMainAuthHelper *helper_ref) g_autoptr(GError) error = NULL; gboolean ret; +#ifdef HAVE_POLKIT /* still more things to to authenticate */ if (helper->action_ids->len > 0) { g_autofree gchar *action_id = g_strdup (g_ptr_array_index (helper->action_ids, 0)); @@ -629,6 +734,7 @@ fu_main_authorize_install_queue (FuMainAuthHelper *helper_ref) g_steal_pointer (&helper)); return; } +#endif /* HAVE_POLKIT */ /* all authenticated, so install all the things */ priv->update_in_progress = TRUE; @@ -767,10 +873,14 @@ fu_main_install_with_helper (FuMainAuthHelper *helper_ref, GError **error) task, helper->flags | FWUPD_INSTALL_FLAG_FORCE, &error_local)) { - g_debug ("first pass requirement on %s:%s failed: %s", - fu_device_get_id (device), - xb_node_query_text (component, "id", NULL), - error_local->message); + if (!g_error_matches (error_local, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND)) { + g_debug ("first pass requirement on %s:%s failed: %s", + fu_device_get_id (device), + xb_node_query_text (component, "id", NULL), + error_local->message); + } g_ptr_array_add (errors, g_steal_pointer (&error_local)); continue; } @@ -789,6 +899,10 @@ fu_main_install_with_helper (FuMainAuthHelper *helper_ref, GError **error) g_ptr_array_add (errors, g_steal_pointer (&error_local)); continue; } + if (!fu_engine_check_trust (task, &error_local)) { + g_ptr_array_add (errors, g_steal_pointer (&error_local)); + continue; + } /* if component should have an update message from CAB */ fu_device_incorporate_from_component (device, component); @@ -869,6 +983,12 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, g_dbus_method_invocation_return_value (invocation, val); return; } + if (g_strcmp0 (method_name, "GetPlugins") == 0) { + g_debug ("Called %s()", method_name); + val = fu_main_plugin_array_to_variant (fu_engine_get_plugins (priv->engine)); + g_dbus_method_invocation_return_value (invocation, val); + return; + } if (g_strcmp0 (method_name, "GetReleases") == 0) { const gchar *device_id; g_autoptr(GPtrArray) releases = NULL; @@ -913,11 +1033,38 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, g_variant_new_tuple (&val, 1)); return; } + if (g_strcmp0 (method_name, "GetReportMetadata") == 0) { + GHashTableIter iter; + GVariantBuilder builder; + const gchar *key; + const gchar *value; + g_autoptr(GHashTable) metadata = NULL; + + metadata = fu_engine_get_report_metadata (priv->engine, &error); + if (metadata == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); + return; + } + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}")); + g_hash_table_iter_init (&iter, metadata); + while (g_hash_table_iter_next (&iter, + (gpointer *) &key, + (gpointer *) &value)) { + g_variant_builder_add_value (&builder, + g_variant_new ("{ss}", key, value)); + } + val = g_variant_builder_end (&builder); + g_dbus_method_invocation_return_value (invocation, + g_variant_new_tuple (&val, 1)); + return; + } if (g_strcmp0 (method_name, "SetApprovedFirmware") == 0) { g_autofree gchar *checksums_str = NULL; g_auto(GStrv) checksums = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitSubject) subject = NULL; +#endif /* HAVE_POLKIT */ g_variant_get (parameters, "(^as)", &checksums); checksums_str = g_strjoinv (",", checksums); @@ -932,6 +1079,7 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, helper->checksums = g_ptr_array_new_with_free_func (g_free); for (guint i = 0; checksums[i] != NULL; i++) g_ptr_array_add (helper->checksums, g_strdup (checksums[i])); +#ifdef HAVE_POLKIT subject = polkit_system_bus_name_new (sender); polkit_authority_check_authorization (priv->authority, subject, "org.freedesktop.fwupd.set-approved-firmware", @@ -940,14 +1088,18 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, NULL, fu_main_authorize_set_approved_firmware_cb, g_steal_pointer (&helper)); +#else + fu_main_authorize_set_approved_firmware_cb (NULL, NULL, g_steal_pointer (&helper)); +#endif /* HAVE_POLKIT */ return; } if (g_strcmp0 (method_name, "SetBlockedFirmware") == 0) { g_autofree gchar *checksums_str = NULL; g_auto(GStrv) checksums = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitSubject) subject = NULL; - +#endif g_variant_get (parameters, "(^as)", &checksums); checksums_str = g_strjoinv (",", checksums); g_debug ("Called %s(%s)", method_name, checksums_str); @@ -961,6 +1113,7 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, helper->checksums = g_ptr_array_new_with_free_func (g_free); for (guint i = 0; checksums[i] != NULL; i++) g_ptr_array_add (helper->checksums, g_strdup (checksums[i])); +#ifdef HAVE_POLKIT subject = polkit_system_bus_name_new (sender); polkit_authority_check_authorization (priv->authority, subject, "org.freedesktop.fwupd.set-approved-firmware", @@ -969,6 +1122,9 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, NULL, fu_main_authorize_set_blocked_firmware_cb, g_steal_pointer (&helper)); +#else + fu_main_authorize_set_blocked_firmware_cb (NULL, NULL, g_steal_pointer (&helper)); +#endif /* HAVE_POLKIT */ return; } if (g_strcmp0 (method_name, "SelfSign") == 0) { @@ -976,7 +1132,9 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, gchar *prop_key; g_autofree gchar *value = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitSubject) subject = NULL; +#endif g_autoptr(GVariantIter) iter = NULL; g_variant_get (parameters, "(sa{sv})", &value, &iter); @@ -1001,6 +1159,7 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, helper->value = g_steal_pointer (&value); helper->request = g_steal_pointer (&request); helper->invocation = g_object_ref (invocation); +#ifdef HAVE_POLKIT subject = polkit_system_bus_name_new (sender); polkit_authority_check_authorization (priv->authority, subject, "org.freedesktop.fwupd.self-sign", @@ -1009,6 +1168,9 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, NULL, fu_main_authorize_self_sign_cb, g_steal_pointer (&helper)); +#else + fu_main_authorize_self_sign_cb (NULL, NULL, g_steal_pointer (&helper)); +#endif /* HAVE_POLKIT */ return; } if (g_strcmp0 (method_name, "GetDowngrades") == 0) { @@ -1075,6 +1237,21 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, g_dbus_method_invocation_return_value (invocation, val); return; } + if (g_strcmp0 (method_name, "GetHostSecurityAttrs") == 0) { + g_autoptr(FuSecurityAttrs) attrs = NULL; + g_debug ("Called %s()", method_name); + if (priv->machine_kind != FU_MAIN_MACHINE_KIND_PHYSICAL) { + g_dbus_method_invocation_return_error_literal (invocation, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "HSI unavailable for hypervisor"); + return; + } + attrs = fu_engine_get_host_security_attrs (priv->engine); + val = fu_security_attrs_to_variant (attrs); + g_dbus_method_invocation_return_value (invocation, val); + return; + } if (g_strcmp0 (method_name, "ClearResults") == 0) { const gchar *device_id; g_variant_get (parameters, "(&s)", &device_id); @@ -1165,8 +1342,9 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, if (g_strcmp0 (method_name, "Unlock") == 0) { const gchar *device_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitSubject) subject = NULL; - +#endif /* HAVE_POLKIT */ g_variant_get (parameters, "(&s)", &device_id); g_debug ("Called %s(%s)", method_name, device_id); if (!fu_main_device_id_valid (device_id, &error)) { @@ -1181,6 +1359,7 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, helper->request = g_steal_pointer (&request); helper->invocation = g_object_ref (invocation); helper->device_id = g_strdup (device_id); +#ifdef HAVE_POLKIT subject = polkit_system_bus_name_new (sender); polkit_authority_check_authorization (priv->authority, subject, "org.freedesktop.fwupd.device-unlock", @@ -1189,13 +1368,17 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, NULL, fu_main_authorize_unlock_cb, g_steal_pointer (&helper)); +#else + fu_main_authorize_unlock_cb (NULL, NULL, g_steal_pointer (&helper)); +#endif /* HAVE_POLKIT */ return; } if (g_strcmp0 (method_name, "Activate") == 0) { const gchar *device_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitSubject) subject = NULL; - +#endif g_variant_get (parameters, "(&s)", &device_id); g_debug ("Called %s(%s)", method_name, device_id); if (!fu_main_device_id_valid (device_id, &error)) { @@ -1210,6 +1393,7 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, helper->request = g_steal_pointer (&request); helper->invocation = g_object_ref (invocation); helper->device_id = g_strdup (device_id); +#ifdef HAVE_POLKIT subject = polkit_system_bus_name_new (sender); polkit_authority_check_authorization (priv->authority, subject, "org.freedesktop.fwupd.device-activate", @@ -1218,14 +1402,18 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, NULL, fu_main_authorize_activate_cb, g_steal_pointer (&helper)); +#else + fu_main_authorize_activate_cb (NULL, NULL, g_steal_pointer (&helper)); +#endif /* HAVE_POLKIT */ return; } if (g_strcmp0 (method_name, "ModifyConfig") == 0) { g_autofree gchar *key = NULL; g_autofree gchar *value = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitSubject) subject = NULL; - +#endif g_variant_get (parameters, "(ss)", &key, &value); g_debug ("Called %s(%s=%s)", method_name, key, value); @@ -1236,6 +1424,7 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, helper->value = g_steal_pointer (&value); helper->request = g_steal_pointer (&request); helper->invocation = g_object_ref (invocation); +#ifdef HAVE_POLKIT subject = polkit_system_bus_name_new (sender); polkit_authority_check_authorization (priv->authority, subject, "org.freedesktop.fwupd.modify-config", @@ -1244,6 +1433,9 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, NULL, fu_main_modify_config_cb, g_steal_pointer (&helper)); +#else + fu_main_modify_config_cb (NULL, NULL, g_steal_pointer (&helper)); +#endif /* HAVE_POLKIT */ return; } if (g_strcmp0 (method_name, "ModifyRemote") == 0) { @@ -1251,8 +1443,9 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, const gchar *key = NULL; const gchar *value = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitSubject) subject = NULL; - +#endif /* check the id exists */ g_variant_get (parameters, "(&s&s&s)", &remote_id, &key, &value); g_debug ("Called %s(%s,%s=%s)", method_name, remote_id, key, value); @@ -1268,6 +1461,7 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, /* authenticate */ fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH); +#ifdef HAVE_POLKIT subject = polkit_system_bus_name_new (sender); polkit_authority_check_authorization (priv->authority, subject, "org.freedesktop.fwupd.modify-remote", @@ -1276,12 +1470,17 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, NULL, fu_main_authorize_modify_remote_cb, g_steal_pointer (&helper)); +#else + fu_main_authorize_modify_remote_cb (NULL, NULL, g_steal_pointer (&helper)); +#endif /* HAVE_POLKIT */ return; } if (g_strcmp0 (method_name, "VerifyUpdate") == 0) { const gchar *device_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT g_autoptr(PolkitSubject) subject = NULL; +#endif /* check the id exists */ g_variant_get (parameters, "(&s)", &device_id); @@ -1299,6 +1498,7 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, helper->priv = priv; /* authenticate */ +#ifdef HAVE_POLKIT fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH); subject = polkit_system_bus_name_new (sender); polkit_authority_check_authorization (priv->authority, subject, @@ -1308,6 +1508,9 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, NULL, fu_main_authorize_verify_update_cb, g_steal_pointer (&helper)); +#else + fu_main_authorize_verify_update_cb (NULL, NULL, g_steal_pointer (&helper)); +#endif /* HAVE_POLKIT */ return; } if (g_strcmp0 (method_name, "Verify") == 0) { @@ -1376,9 +1579,17 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, if (g_strcmp0 (prop_key, "allow-reinstall") == 0 && g_variant_get_boolean (prop_value) == TRUE) helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; - if (g_strcmp0 (prop_key, "force") == 0 && + if (g_strcmp0 (prop_key, "allow-branch-switch") == 0 && g_variant_get_boolean (prop_value) == TRUE) + helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (g_strcmp0 (prop_key, "force") == 0 && + g_variant_get_boolean (prop_value) == TRUE) { helper->flags |= FWUPD_INSTALL_FLAG_FORCE; + helper->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER; + } + if (g_strcmp0 (prop_key, "ignore-power") == 0 && + g_variant_get_boolean (prop_value) == TRUE) + helper->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER; if (g_strcmp0 (prop_key, "no-history") == 0 && g_variant_get_boolean (prop_value) == TRUE) helper->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY; @@ -1414,7 +1625,9 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, } /* install all the things in the store */ +#ifdef HAVE_POLKIT helper->subject = polkit_system_bus_name_new (sender); +#endif /* HAVE_POLKIT */ if (!fu_main_install_with_helper (g_steal_pointer (&helper), &error)) { g_dbus_method_invocation_return_gerror (invocation, error); return; @@ -1494,6 +1707,9 @@ fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender, if (g_strcmp0 (property_name, "HostMachineId") == 0) return g_variant_new_string (fu_engine_get_host_machine_id (priv->engine)); + if (g_strcmp0 (property_name, "HostSecurityId") == 0) + return g_variant_new_string (fu_engine_get_host_security_id (priv->engine)); + if (g_strcmp0 (property_name, "Interactive") == 0) return g_variant_new_boolean (isatty (fileno (stdout)) != 0); @@ -1552,7 +1768,7 @@ fu_main_on_name_acquired_cb (GDBusConnection *connection, const gchar *name, gpointer user_data) { - g_debug ("FuMain: acquired name: %s", name); + g_debug ("acquired name: %s", name); } static void @@ -1561,7 +1777,7 @@ fu_main_on_name_lost_cb (GDBusConnection *connection, gpointer user_data) { FuMainPrivate *priv = (FuMainPrivate *) user_data; - g_debug ("FuMain: lost name: %s", name); + g_warning ("another service has claimed the dbus name %s", name); g_main_loop_quit (priv->loop); } @@ -1624,6 +1840,30 @@ fu_main_load_introspection (const gchar *filename, GError **error) return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error); } +static gboolean +fu_main_is_hypervisor (void) +{ + g_autofree gchar *buf = NULL; + gsize bufsz = 0; + if (!g_file_get_contents ("/proc/cpuinfo", &buf, &bufsz, NULL)) + return FALSE; + return g_strstr_len (buf, (gssize) bufsz, "hypervisor") != NULL; +} + +static gboolean +fu_main_is_container (void) +{ + g_autofree gchar *buf = NULL; + gsize bufsz = 0; + if (!g_file_get_contents ("/proc/1/cgroup", &buf, &bufsz, NULL)) + return FALSE; + if (g_strstr_len (buf, (gssize) bufsz, "docker") != NULL) + return TRUE; + if (g_strstr_len (buf, (gssize) bufsz, "lxc") != NULL) + return TRUE; + return FALSE; +} + static void fu_main_private_free (FuMainPrivate *priv) { @@ -1638,8 +1878,10 @@ fu_main_private_free (FuMainPrivate *priv) g_object_unref (priv->engine); if (priv->connection != NULL) g_object_unref (priv->connection); +#ifdef HAVE_POLKIT if (priv->authority != NULL) g_object_unref (priv->authority); +#endif if (priv->argv0_monitor != NULL) { g_file_monitor_cancel (priv->argv0_monitor); g_object_unref (priv->argv0_monitor); @@ -1750,12 +1992,21 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } +#ifdef HAVE_POLKIT /* get authority */ priv->authority = polkit_authority_get_sync (NULL, &error); if (priv->authority == NULL) { g_printerr ("Failed to load authority: %s\n", error->message); return EXIT_FAILURE; } +#endif + + /* are we a VM? */ + if (fu_main_is_hypervisor ()) { + priv->machine_kind = FU_MAIN_MACHINE_KIND_VIRTUAL; + } else if (fu_main_is_container ()) { + priv->machine_kind = FU_MAIN_MACHINE_KIND_CONTAINER; + } /* own the object */ priv->owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, @@ -1774,10 +2025,8 @@ main (int argc, char *argv[]) else if (timed_exit) g_timeout_add_seconds (5, fu_main_timed_exit_cb, priv->loop); - g_debug ("Started with locale %s", g_getenv ("LANG")); - /* wait */ - g_message ("Daemon ready for requests"); + g_message ("Daemon ready for requests (locale %s)", g_getenv ("LANG")); g_main_loop_run (priv->loop); /* success */ diff --git a/src/fu-plugin-list.c b/src/fu-plugin-list.c index 08cb3b7eb..777bbe0c4 100644 --- a/src/fu-plugin-list.c +++ b/src/fu-plugin-list.c @@ -145,6 +145,8 @@ fu_plugin_list_depsolve (FuPluginList *self, GError **error) for (guint i = 0; i < self->plugins->len; i++) { FuPlugin *plugin = g_ptr_array_index (self->plugins, i); deps = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_RUN_AFTER); + if (deps == NULL) + continue; for (guint j = 0; j < deps->len && !changes; j++) { const gchar *plugin_name = g_ptr_array_index (deps, j); dep = fu_plugin_list_find_by_name (self, plugin_name, NULL); @@ -155,7 +157,7 @@ fu_plugin_list_depsolve (FuPluginList *self, GError **error) fu_plugin_get_name (plugin)); continue; } - if (!fu_plugin_get_enabled (dep)) + if (fu_plugin_has_flag (dep, FWUPD_PLUGIN_FLAG_DISABLED)) continue; if (fu_plugin_get_order (plugin) <= fu_plugin_get_order (dep)) { g_debug ("%s [%u] to be ordered after %s [%u] " @@ -173,6 +175,8 @@ fu_plugin_list_depsolve (FuPluginList *self, GError **error) for (guint i = 0; i < self->plugins->len; i++) { FuPlugin *plugin = g_ptr_array_index (self->plugins, i); deps = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_RUN_BEFORE); + if (deps == NULL) + continue; for (guint j = 0; j < deps->len && !changes; j++) { const gchar *plugin_name = g_ptr_array_index (deps, j); dep = fu_plugin_list_find_by_name (self, plugin_name, NULL); @@ -183,7 +187,7 @@ fu_plugin_list_depsolve (FuPluginList *self, GError **error) fu_plugin_get_name (plugin)); continue; } - if (!fu_plugin_get_enabled (dep)) + if (fu_plugin_has_flag (dep, FWUPD_PLUGIN_FLAG_DISABLED)) continue; if (fu_plugin_get_order (plugin) >= fu_plugin_get_order (dep)) { g_debug ("%s [%u] to be ordered before %s [%u] " @@ -203,6 +207,8 @@ fu_plugin_list_depsolve (FuPluginList *self, GError **error) for (guint i = 0; i < self->plugins->len; i++) { FuPlugin *plugin = g_ptr_array_index (self->plugins, i); deps = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_BETTER_THAN); + if (deps == NULL) + continue; for (guint j = 0; j < deps->len && !changes; j++) { const gchar *plugin_name = g_ptr_array_index (deps, j); dep = fu_plugin_list_find_by_name (self, plugin_name, NULL); @@ -213,7 +219,7 @@ fu_plugin_list_depsolve (FuPluginList *self, GError **error) fu_plugin_get_name (plugin)); continue; } - if (!fu_plugin_get_enabled (dep)) + if (fu_plugin_has_flag (dep, FWUPD_PLUGIN_FLAG_DISABLED)) continue; if (fu_plugin_get_priority (plugin) <= fu_plugin_get_priority (dep)) { g_debug ("%s [%u] better than %s [%u] " @@ -242,20 +248,22 @@ fu_plugin_list_depsolve (FuPluginList *self, GError **error) /* check for conflicts */ for (guint i = 0; i < self->plugins->len; i++) { FuPlugin *plugin = g_ptr_array_index (self->plugins, i); - if (!fu_plugin_get_enabled (plugin)) + if (fu_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED)) continue; deps = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_CONFLICTS); + if (deps == NULL) + continue; for (guint j = 0; j < deps->len && !changes; j++) { const gchar *plugin_name = g_ptr_array_index (deps, j); dep = fu_plugin_list_find_by_name (self, plugin_name, NULL); if (dep == NULL) continue; - if (!fu_plugin_get_enabled (dep)) + if (fu_plugin_has_flag (dep, FWUPD_PLUGIN_FLAG_DISABLED)) continue; g_debug ("disabling %s as conflicts with %s", fu_plugin_get_name (dep), fu_plugin_get_name (plugin)); - fu_plugin_set_enabled (dep, FALSE); + fu_plugin_add_flag (dep, FWUPD_PLUGIN_FLAG_DISABLED); } } diff --git a/src/fu-polkit-agent.c b/src/fu-polkit-agent.c new file mode 100644 index 000000000..afe721ae0 --- /dev/null +++ b/src/fu-polkit-agent.c @@ -0,0 +1,239 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2011 Lennart Poettering + * Copyright (C) 2012 Matthias Klumpp + * Copyright (C) 2015-2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#ifdef __linux__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fu-common.h" +#include "fu-polkit-agent.h" + +static pid_t agent_pid = 0; + +static int +fork_agent (pid_t *pid, const char *path, ...) +{ + char **l; + gboolean stderr_is_tty; + gboolean stdout_is_tty; + int fd; + pid_t n_agent_pid; + pid_t parent_pid; + unsigned n, i; + va_list ap; + + g_return_val_if_fail (pid != 0, 0); + g_assert (path); + + parent_pid = getpid (); + + /* spawns a temporary TTY agent, making sure it goes away when + * we go away */ + n_agent_pid = fork (); + if (n_agent_pid < 0) + return -errno; + + if (n_agent_pid != 0) { + *pid = n_agent_pid; + return 0; + } + +#ifdef __linux__ + /* make sure the agent goes away when the parent dies */ + if (prctl (PR_SET_PDEATHSIG, SIGTERM) < 0) + _exit (EXIT_FAILURE); +#endif + /* check whether our parent died before we were able + * to set the death signal */ + if (getppid () != parent_pid) + _exit (EXIT_SUCCESS); + + /* TODO: it might be more clean to close all FDs so we don't leak them to the agent */ + stdout_is_tty = isatty (STDOUT_FILENO); + stderr_is_tty = isatty (STDERR_FILENO); + + if (!stdout_is_tty || !stderr_is_tty) { + /* Detach from stdout/stderr. and reopen + * /dev/tty for them. This is important to + * ensure that when systemctl is started via + * popen() or a similar call that expects to + * read EOF we actually do generate EOF and + * not delay this indefinitely by because we + * keep an unused copy of stdin around. */ + fd = open("/dev/tty", O_WRONLY); + if (fd < 0) { + g_error ("Failed to open /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + if (!stdout_is_tty) + dup2 (fd, STDOUT_FILENO); + if (!stderr_is_tty) + dup2 (fd, STDERR_FILENO); + if (fd > 2) + close (fd); + } + + /* count arguments */ + va_start (ap, path); + for (n = 0; va_arg (ap, char*); n++) + ; + va_end(ap); + + /* allocate strv */ + l = alloca (sizeof(char *) * (n + 1)); + + /* fill in arguments */ + va_start (ap, path); + for (i = 0; i <= n; i++) + l[i] = va_arg (ap, char*); + va_end (ap); + + execv (path, l); + _exit (EXIT_FAILURE); +} + +static int +close_nointr (int fd) +{ + g_assert (fd >= 0); + for (;;) { + int r; + r = close (fd); + if (r >= 0) + return r; + if (errno != EINTR) + return -errno; + } +} + +static void +close_nointr_nofail (int fd) +{ + int saved_errno = errno; + /* cannot fail, and guarantees errno is unchanged */ + g_assert (close_nointr (fd) == 0); + errno = saved_errno; +} + +static int +fd_wait_for_event (int fd, int event, uint64_t t) +{ + struct pollfd pollfd = { 0 }; + int r; + + pollfd.fd = fd; + pollfd.events = event; + r = poll (&pollfd, 1, t == (uint64_t) -1 ? -1 : (int) (t / 1000)); + if (r < 0) + return -errno; + if (r == 0) + return 0; + + return pollfd.revents; +} + +static int +wait_for_terminate (pid_t pid) +{ + g_return_val_if_fail (pid >= 1, 0); + + for (;;) { + int status; + if (waitpid (pid, &status, 0) < 0) { + if (errno == EINTR) + continue; + return -errno; + } + return 0; + } +} + +gboolean +fu_polkit_agent_open (GError **error) +{ + int r; + int pipe_fd[2]; + g_autofree gchar *notify_fd = NULL; + g_autofree gchar *pkttyagent_fn = NULL; + + if (agent_pid > 0) + return TRUE; + + /* find binary */ + pkttyagent_fn = fu_common_find_program_in_path ("pkttyagent", error); + if (pkttyagent_fn == NULL) + return FALSE; + + /* check STDIN here, not STDOUT, since this is about input, not output */ + if (!isatty (STDIN_FILENO)) + return TRUE; + if (pipe (pipe_fd) < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to create pipe: %s", + strerror (-errno)); + return FALSE; + } + + /* fork pkttyagent */ + notify_fd = g_strdup_printf ("%i", pipe_fd[1]); + r = fork_agent (&agent_pid, + pkttyagent_fn, + pkttyagent_fn, + "--notify-fd", notify_fd, + "--fallback", NULL); + if (r < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to fork TTY ask password agent: %s", + strerror (-r)); + close_nointr_nofail (pipe_fd[1]); + close_nointr_nofail (pipe_fd[0]); + return FALSE; + } + + /* close the writing side, because that is the one for the agent */ + close_nointr_nofail (pipe_fd[1]); + + /* wait until the agent closes the fd */ + fd_wait_for_event (pipe_fd[0], POLLHUP, (uint64_t) -1); + + close_nointr_nofail (pipe_fd[0]); + return TRUE; +} + +void +fu_polkit_agent_close (void) { + + if (agent_pid <= 0) + return; + + /* inform agent that we are done */ + kill (agent_pid, SIGTERM); + kill (agent_pid, SIGCONT); + wait_for_terminate (agent_pid); + agent_pid = 0; +} diff --git a/src/fu-polkit-agent.h b/src/fu-polkit-agent.h new file mode 100644 index 000000000..15f3b831e --- /dev/null +++ b/src/fu-polkit-agent.h @@ -0,0 +1,13 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2011 Lennart Poettering + * Copyright (C) 2012 Matthias Klumpp + * Copyright (C) 2015-2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +gboolean fu_polkit_agent_open (GError **error); +void fu_polkit_agent_close (void); diff --git a/src/fu-progressbar.c b/src/fu-progressbar.c index db0bd6834..9a7378c5a 100644 --- a/src/fu-progressbar.c +++ b/src/fu-progressbar.c @@ -162,6 +162,8 @@ fu_progressbar_refresh (FuProgressbar *self, FwupdStatus status, guint percentag percentage = 100; status = self->status; is_idle_newline = TRUE; + } else if (status == FWUPD_STATUS_WAITING_FOR_AUTH) { + is_idle_newline = TRUE; } title = fu_progressbar_status_to_string (status); g_string_append (str, title); @@ -243,7 +245,8 @@ fu_progressbar_spin_cb (gpointer user_data) FuProgressbar *self = FU_PROGRESSBAR (user_data); /* ignore */ - if (self->status == FWUPD_STATUS_IDLE) + if (self->status == FWUPD_STATUS_IDLE || + self->status == FWUPD_STATUS_WAITING_FOR_AUTH) return G_SOURCE_CONTINUE; /* move the spinner index up to down */ @@ -310,6 +313,7 @@ fu_progressbar_update (FuProgressbar *self, FwupdStatus status, guint percentage * execute the callback just do the refresh now manually */ if (percentage == 0 && status != FWUPD_STATUS_IDLE && + status != FWUPD_STATUS_WAITING_FOR_AUTH && self->status != FWUPD_STATUS_UNKNOWN) { if ((g_get_monotonic_time () - self->last_animated) / 1000 > 40) { fu_progressbar_spin_inc (self); diff --git a/src/fu-remote-list.c b/src/fu-remote-list.c index 1646bde3a..e9a24e8f2 100644 --- a/src/fu-remote-list.c +++ b/src/fu-remote-list.c @@ -34,6 +34,7 @@ struct _FuRemoteList GObject parent_instance; GPtrArray *array; /* (element-type FwupdRemote) */ GPtrArray *monitors; /* (element-type GFileMonitor) */ + GHashTable *hash_unfound; /* utf8 : NULL */ XbSilo *silo; }; @@ -183,7 +184,7 @@ fu_remote_list_add_for_path (FuRemoteList *self, const gchar *path, GError **err fwupd_remote_set_remotes_dir (remote, remotesdir); /* load from keyfile */ - g_debug ("loading remotes from %s", filename); + g_debug ("loading remote from %s", filename); if (!fwupd_remote_load_from_filename (remote, filename, NULL, error)) { g_prefix_error (error, "failed to load %s: ", filename); @@ -296,6 +297,7 @@ static guint fu_remote_list_depsolve_with_direction (FuRemoteList *self, gint inc) { guint cnt = 0; + for (guint i = 0; i < self->array->len; i++) { FwupdRemote *remote = g_ptr_array_index (self->array, i); gchar **order = inc < 0 ? fwupd_remote_get_order_after (remote) : @@ -310,7 +312,11 @@ fu_remote_list_depsolve_with_direction (FuRemoteList *self, gint inc) } remote2 = fu_remote_list_get_by_id (self, order[j]); if (remote2 == NULL) { + if (g_hash_table_contains (self->hash_unfound, order[j])) + continue; g_debug ("ignoring unfound remote %s", order[j]); + g_hash_table_insert (self->hash_unfound, + g_strdup (order[j]), NULL); continue; } if (fwupd_remote_get_priority (remote) > fwupd_remote_get_priority (remote2)) @@ -487,6 +493,7 @@ fu_remote_list_init (FuRemoteList *self) { self->array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); self->monitors = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_remote_list_monitor_unref); + self->hash_unfound = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } static void @@ -497,6 +504,7 @@ fu_remote_list_finalize (GObject *obj) g_object_unref (self->silo); g_ptr_array_unref (self->array); g_ptr_array_unref (self->monitors); + g_hash_table_unref (self->hash_unfound); G_OBJECT_CLASS (fu_remote_list_parent_class)->finalize (obj); } diff --git a/src/fu-security-attr.c b/src/fu-security-attr.c new file mode 100644 index 000000000..29765c905 --- /dev/null +++ b/src/fu-security-attr.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include +#include + +#include "fwupd-security-attr-private.h" + +#include "fu-security-attr.h" + +gchar * +fu_security_attr_get_name (FwupdSecurityAttr *attr) +{ + const gchar *appstream_id = fwupd_security_attr_get_appstream_id (attr); + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE) == 0) { + /* TRANSLATORS: Title: SPI refers to the flash chip in the computer */ + return g_strdup (_("SPI write")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_BLE) == 0) { + /* TRANSLATORS: Title: SPI refers to the flash chip in the computer */ + return g_strdup (_("SPI lock")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP) == 0) { + /* TRANSLATORS: Title: SPI refers to the flash chip in the computer */ + return g_strdup (_("SPI BIOS region")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_ACPI_DMAR) == 0) { + /* TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack */ + return g_strdup (_("Pre-boot DMA protection")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel */ + return g_strdup (_("Intel BootGuard")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel, + * verified boot refers to the way the boot process is verified */ + return g_strdup (_("Intel BootGuard verified boot")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel, + * ACM means to verify the integrity of Initial Boot Block */ + return g_strdup (_("Intel BootGuard ACM protected")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel, + * error policy is what to do on failure */ + return g_strdup (_("Intel BootGuard error policy")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel, + * OTP = one time programmable */ + return g_strdup (_("Intel BootGuard OTP fuse")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_CET_ENABLED) == 0) { + /* TRANSLATORS: Title: CET = Control-flow Enforcement Technology, + * enabled means supported by the processor */ + return g_strdup (_("Intel CET Enabled")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_CET_ACTIVE) == 0) { + /* TRANSLATORS: Title: CET = Control-flow Enforcement Technology, + * active means being used by the OS */ + return g_strdup (_("Intel CET Active")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_SMAP) == 0) { + /* TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention */ + return g_strdup (_("Intel SMAP")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM) == 0) { + /* TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME */ + return g_strdup (_("Encrypted RAM")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_IOMMU) == 0) { + /* TRANSLATORS: Title: https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit */ + return g_strdup (_("IOMMU")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN) == 0) { + /* TRANSLATORS: Title: lockdown is a security mode of the kernel */ + return g_strdup (_("Linux kernel lockdown")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED) == 0) { + /* TRANSLATORS: Title: if it's tainted or not */ + return g_strdup (_("Linux kernel")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP) == 0) { + /* TRANSLATORS: Title: swap space or swap partition */ + return g_strdup (_("Linux swap")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM) == 0) { + /* TRANSLATORS: Title: sleep state */ + return g_strdup (_("Suspend-to-ram")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE) == 0) { + /* TRANSLATORS: Title: a better sleep state */ + return g_strdup (_("Suspend-to-idle")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT) == 0) { + /* TRANSLATORS: Title: SB is a way of locking down UEFI */ + return g_strdup (_("UEFI secure boot")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0) == 0) { + /* TRANSLATORS: Title: the PCR is rebuilt from the TPM event log */ + return g_strdup (_("TPM PCR0 reconstruction")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20) == 0) { + /* TRANSLATORS: Title: TPM = Trusted Platform Module */ + return g_strdup (_("TPM v2.0")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE) == 0) { + const gchar *kind = fwupd_security_attr_get_metadata (attr, "kind"); + if (kind != NULL) { + /* TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT */ + return g_strdup_printf (_("%s manufacturing mode"), kind); + } + /* TRANSLATORS: Title: MEI = Intel Management Engine */ + return g_strdup (_("MEI manufacturing mode")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP) == 0) { + const gchar *kind = fwupd_security_attr_get_metadata (attr, "kind"); + if (kind != NULL) { + /* TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT */ + return g_strdup_printf (_("%s override"), kind); + } + /* TRANSLATORS: Title: MEI = Intel Management Engine, and the + * "override" is the physical PIN that can be driven to + * logic high -- luckily it is probably not accessible to + * end users on consumer boards */ + return g_strdup (_("MEI override")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_VERSION) == 0) { + /* TRANSLATORS: Title: MEI = Intel Management Engine */ + const gchar *kind = fwupd_security_attr_get_metadata (attr, "kind"); + const gchar *version = fwupd_security_attr_get_metadata (attr, "version"); + if (kind != NULL && version != NULL) { + /* TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number */ + return g_strdup_printf (_("%s v%s"), kind, version); + } + if (kind != NULL) { + /* TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT */ + return g_strdup_printf (_("%s version"), kind); + } + return g_strdup (_("MEI version")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_UPDATES) == 0) { + /* TRANSLATORS: Title: if firmware updates are available */ + return g_strdup (_("Firmware updates")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_ATTESTATION) == 0) { + /* TRANSLATORS: Title: if we can verify the firmware checksums */ + return g_strdup (_("Firmware attestation")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS) == 0) { + /* TRANSLATORS: Title: if the fwupd plugins are all present and correct */ + return g_strdup (_("fwupd plugins")); + } + if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_DCI_ENABLED) == 0 || + g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_DCI_LOCKED) == 0) { + /* TRANSLATORS: Title: Direct Connect Interface (DCI) allows + * debugging of Intel processors using the USB3 port */ + return g_strdup (_("Intel DCI debugger")); + } + + /* we should not get here */ + return g_strdup (fwupd_security_attr_get_name (attr)); +} + +const gchar * +fu_security_attr_get_result (FwupdSecurityAttr *attr) +{ + FwupdSecurityAttrResult result = fwupd_security_attr_get_result (attr); + if (result == FWUPD_SECURITY_ATTR_RESULT_VALID) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Valid"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_VALID) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Invalid"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_ENABLED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Enabled"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Disabled"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_LOCKED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Locked"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Unlocked"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Encrypted"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Unencrypted"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_TAINTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Tainted"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Untainted"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_FOUND) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Found"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Not found"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_SUPPORTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Supported"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Not supported"); + } + + /* fallback */ + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("OK"); + } + + /* TRANSLATORS: Suffix: the fallback HSI result */ + return _("Failed"); +} diff --git a/src/fu-security-attr.h b/src/fu-security-attr.h new file mode 100644 index 000000000..9967cf27d --- /dev/null +++ b/src/fu-security-attr.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +gchar *fu_security_attr_get_name (FwupdSecurityAttr *attr); +const gchar *fu_security_attr_get_result (FwupdSecurityAttr *attr); diff --git a/src/fu-self-test.c b/src/fu-self-test.c index ba7ed9d5a..aaa3cd26c 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -25,6 +25,8 @@ #include "fu-plugin-list.h" #include "fu-progressbar.h" #include "fu-hash.h" +#include "fu-security-attr.h" +#include "fu-security-attrs.h" #include "fu-smbios-private.h" typedef struct { @@ -1074,15 +1076,18 @@ fu_engine_device_parent_func (gconstpointer user_data) /* add two together */ fu_engine_add_device (engine, device2); + /* this is normally done by fu_plugin_device_add() */ + fu_engine_add_device (engine, device3); + /* verify both children were adopted */ g_assert (fu_device_get_parent (device3) == device2); g_assert (fu_device_get_parent (device1) == device2); g_assert_cmpstr (fu_device_get_vendor (device3), ==, "oem"); /* verify order */ - g_assert_cmpint (fu_device_get_order (device1), ==, 0); - g_assert_cmpint (fu_device_get_order (device2), ==, 1); - g_assert_cmpint (fu_device_get_order (device3), ==, 0); + g_assert_cmpint (fu_device_get_order (device1), ==, -1); + g_assert_cmpint (fu_device_get_order (device2), ==, 0); + g_assert_cmpint (fu_device_get_order (device3), ==, -1); } static void @@ -1380,12 +1385,17 @@ fu_engine_downgrade_func (gconstpointer user_data) fu_device_set_name (device, "Test Device"); fu_device_add_guid (device, "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"); fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); +#ifndef HAVE_POLKIT + g_test_expect_message ("FuEngine", G_LOG_LEVEL_WARNING, "*archive signature missing or not trusted"); +#endif fu_engine_add_device (engine, device); devices = fu_engine_get_devices (engine, &error); g_assert_no_error (error); g_assert (devices != NULL); g_assert_cmpint (devices->len, ==, 1); +#ifdef HAVE_POLKIT g_assert (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED)); +#endif g_assert (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED)); /* get the releases for one device */ @@ -1490,13 +1500,17 @@ fu_engine_install_duration_func (gconstpointer user_data) fu_device_add_guid (device, "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"); fu_device_set_install_duration (device, 999); fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); +#ifndef HAVE_POLKIT + g_test_expect_message ("FuEngine", G_LOG_LEVEL_WARNING, "*archive signature missing or not trusted"); +#endif fu_engine_add_device (engine, device); devices = fu_engine_get_devices (engine, &error); g_assert_no_error (error); g_assert (devices != NULL); g_assert_cmpint (devices->len, ==, 1); +#ifdef HAVE_POLKIT g_assert (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED)); - +#endif /* check the release install duration */ releases = fu_engine_get_releases (engine, request, @@ -1970,14 +1984,15 @@ fu_device_list_delay_func (gconstpointer user_data) fu_device_list_add (device_list, device1); g_assert_cmpint (added_cnt, ==, 1); g_assert_cmpint (removed_cnt, ==, 0); - g_assert_cmpint (changed_cnt, ==, 0); + g_assert_cmpint (changed_cnt, ==, 1); /* add a device with the same ID */ fu_device_set_id (device2, "device1"); fu_device_list_add (device_list, device2); + fu_device_set_remove_delay (device2, 100); g_assert_cmpint (added_cnt, ==, 1); g_assert_cmpint (removed_cnt, ==, 0); - g_assert_cmpint (changed_cnt, ==, 0); + g_assert_cmpint (changed_cnt, ==, 2); /* spin a bit */ fu_test_loop_run_with_timeout (10); @@ -2390,7 +2405,7 @@ fu_plugin_list_depsolve_func (gconstpointer user_data) plugin = g_ptr_array_index (plugins, 0); g_assert_cmpstr (fu_plugin_get_name (plugin), ==, "plugin2"); g_assert_cmpint (fu_plugin_get_order (plugin), ==, 0); - g_assert (fu_plugin_get_enabled (plugin)); + g_assert_false (fu_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED)); /* add another rule, then re-depsolve */ fu_plugin_add_rule (plugin1, FU_PLUGIN_RULE_CONFLICTS, "plugin2"); @@ -2400,11 +2415,11 @@ fu_plugin_list_depsolve_func (gconstpointer user_data) plugin = fu_plugin_list_find_by_name (plugin_list, "plugin1", &error); g_assert_no_error (error); g_assert (plugin != NULL); - g_assert (fu_plugin_get_enabled (plugin)); + g_assert_false (fu_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED)); plugin = fu_plugin_list_find_by_name (plugin_list, "plugin2", &error); g_assert_no_error (error); g_assert (plugin != NULL); - g_assert (!fu_plugin_get_enabled (plugin)); + g_assert_true (fu_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED)); } static void @@ -2946,6 +2961,15 @@ fu_plugin_composite_func (gconstpointer user_data) } } +static void +fu_security_attr_func (gconstpointer user_data) +{ + g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_new (NULL); + for (guint i = 0; i < FWUPD_SECURITY_ATTR_RESULT_LAST; i++) { + fwupd_security_attr_set_result (attr, i); + g_assert_cmpstr (fu_security_attr_get_result (attr), !=, NULL); + } +} static void fu_memcpy_func (gconstpointer user_data) @@ -3127,6 +3151,8 @@ main (int argc, char **argv) fu_plugin_module_func); g_test_add_data_func ("/fwupd/memcpy", self, fu_memcpy_func); + g_test_add_data_func ("/fwupd/security-attr", self, + fu_security_attr_func); g_test_add_data_func ("/fwupd/device-list", self, fu_device_list_func); g_test_add_data_func ("/fwupd/device-list{delay}", self, diff --git a/src/fu-tool.c b/src/fu-tool.c index 92d698929..46e4fefd9 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -26,6 +26,7 @@ #include "fu-history.h" #include "fu-plugin-private.h" #include "fu-progressbar.h" +#include "fu-security-attrs-private.h" #include "fu-smbios-private.h" #include "fu-util-common.h" #include "fu-debug.h" @@ -60,7 +61,7 @@ struct FuUtilPrivate { gboolean cleanup_blob; gboolean enable_json_state; FwupdInstallFlags flags; - gboolean show_all_devices; + gboolean show_all; gboolean disable_ssl_strict; /* only valid in update and downgrade */ FuUtilOperation current_operation; @@ -119,6 +120,51 @@ fu_util_save_current_state (FuUtilPrivate *priv, GError **error) return g_file_set_contents (filename, state, -1, error); } +static void +fu_util_show_plugin_warnings (FuUtilPrivate *priv) +{ + FwupdPluginFlags flags = FWUPD_PLUGIN_FLAG_NONE; + GPtrArray *plugins; + + /* get a superset so we do not show the same message more than once */ + plugins = fu_engine_get_plugins (priv->engine); + for (guint i = 0; i < plugins->len; i++) { + FwupdPlugin *plugin = g_ptr_array_index (plugins, i); + if (!fwupd_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING)) + continue; + flags |= fwupd_plugin_get_flags (plugin); + } + + /* never show these, they're way too generic */ + flags &= ~FWUPD_PLUGIN_FLAG_DISABLED; + flags &= ~FWUPD_PLUGIN_FLAG_NO_HARDWARE; + + /* print */ + for (guint i = 0; i < 64; i++) { + FwupdPluginFlags flag = (guint64) 1 << i; + const gchar *tmp; + g_autofree gchar *fmt = NULL; + g_autofree gchar *url= NULL; + g_autoptr(GString) str = g_string_new (NULL); + if ((flags & flag) == 0) + continue; + tmp = fu_util_plugin_flag_to_string ((guint64) 1 << i); + if (tmp == NULL) + continue; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED); + g_string_append_printf (str, "%s %s\n", fmt, tmp); + + url = g_strdup_printf ("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s", + fwupd_plugin_flag_to_string (flag)); + g_string_append (str, " "); + /* TRANSLATORS: %s is a link to a website */ + g_string_append_printf (str, _("See %s for more information."), url); + g_string_append (str, "\n"); + g_printerr ("%s", str->str); + } +} + static gboolean fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error) { @@ -131,9 +177,15 @@ fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **err if (!fu_engine_load (priv->engine, flags, error)) return FALSE; if (fu_engine_get_tainted (priv->engine)) { - g_printerr ("WARNING: This tool has loaded 3rd party code and " - "is no longer supported by the upstream developers!\n"); + g_autofree gchar *fmt = NULL; + + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED); + g_printerr ("%s This tool has loaded 3rd party code and " + "is no longer supported by the upstream developers!\n", + fmt); } + fu_util_show_plugin_warnings (priv); return TRUE; } @@ -268,10 +320,9 @@ static gboolean fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error) { GPtrArray *plugins; - guint cnt = 0; /* load engine */ - if (!fu_engine_load_plugins (priv->engine, error)) + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) return FALSE; /* print */ @@ -279,15 +330,12 @@ fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error) g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb); for (guint i = 0; i < plugins->len; i++) { FuPlugin *plugin = g_ptr_array_index (plugins, i); - if (!fu_plugin_get_enabled (plugin)) - continue; - g_print ("%s\n", fu_plugin_get_name (plugin)); - cnt++; + g_autofree gchar *str = fu_util_plugin_to_string (FWUPD_PLUGIN (plugin), 0); + g_print ("%s\n", str); } - if (cnt == 0) { + if (plugins->len == 0) { /* TRANSLATORS: nothing found */ g_print ("%s\n", _("No plugins found")); - return TRUE; } return TRUE; @@ -313,201 +361,6 @@ fu_util_get_tree_title (FuUtilPrivate *priv) return g_strdup (fu_engine_get_host_product (priv->engine)); } -static gboolean -fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) -{ - g_autoptr(GPtrArray) devices = NULL; - g_autoptr(GNode) root = g_node_new (NULL); - g_autofree gchar *title = NULL; - - /* load engine */ - if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) - return FALSE; - title = fu_util_get_tree_title (priv); - - /* get devices from daemon */ - devices = fu_engine_get_devices (priv->engine, error); - if (devices == NULL) - return FALSE; - fwupd_device_array_ensure_parents (devices); - for (guint i = 0; i < devices->len; i++) { - FwupdDevice *dev = g_ptr_array_index (devices, i); - g_autoptr(GPtrArray) rels = NULL; - g_autoptr(GError) error_local = NULL; - GNode *child; - - /* not going to have results, so save a engine round-trip */ - if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE)) - continue; - if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) { - /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS - * %1 is the device name */ - g_autofree gchar *tmp = g_strdup_printf (_("• %s has no available firmware updates"), - fwupd_device_get_name (dev)); - g_printerr ("%s\n", tmp); - continue; - } - if (!fu_util_filter_device (priv, dev)) - continue; - - /* get the releases for this device and filter for validity */ - rels = fu_engine_get_upgrades (priv->engine, - priv->request, - fwupd_device_get_id (dev), - &error_local); - if (rels == NULL) { - /* TRANSLATORS: message letting the user know no device upgrade available - * %1 is the device name */ - g_autofree gchar *tmp = g_strdup_printf (_("• %s has the latest available firmware version"), - fwupd_device_get_name (dev)); - g_printerr ("%s\n", tmp); - /* discard the actual reason from user, but leave for debugging */ - g_debug ("%s", error_local->message); - continue; - } - child = g_node_append_data (root, dev); - - for (guint j = 0; j < rels->len; j++) { - FwupdRelease *rel = g_ptr_array_index (rels, j); - g_node_append_data (child, g_object_ref (rel)); - } - } - if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1) - fu_util_print_tree (root, title); - /* save the device state for other applications to see */ - if (!fu_util_save_current_state (priv, error)) - return FALSE; - - /* success */ - return TRUE; -} - -static gboolean -fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) -{ - g_autoptr(GPtrArray) array = NULL; - g_autoptr(GNode) root = g_node_new (NULL); - g_autofree gchar *title = NULL; - gint fd; - - /* load engine */ - if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) - return FALSE; - title = fu_util_get_tree_title (priv); - - /* check args */ - if (g_strv_length (values) != 1) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_ARGS, - "Invalid arguments"); - return FALSE; - } - - /* implied, important for get-details on a device not in your system */ - priv->show_all_devices = TRUE; - - /* open file */ - fd = open (values[0], O_RDONLY); - if (fd < 0) { - fu_util_maybe_prefix_sandbox_error (values[0], error); - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "failed to open %s", - values[0]); - return FALSE; - } - array = fu_engine_get_details (priv->engine, priv->request, fd, error); - close (fd); - - if (array == NULL) - return FALSE; - for (guint i = 0; i < array->len; i++) { - FwupdDevice *dev = g_ptr_array_index (array, i); - FwupdRelease *rel; - GNode *child; - if (!fu_util_filter_device (priv, dev)) - continue; - child = g_node_append_data (root, dev); - rel = fwupd_device_get_release_default (dev); - if (rel != NULL) - g_node_append_data (child, rel); - - } - fu_util_print_tree (root, title); - - return TRUE; -} - -static gboolean -fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error) -{ - g_autoptr(GString) str = g_string_new (NULL); - - for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) { - const gchar *tmp = fwupd_device_flag_to_string (i); - if (tmp == NULL) - break; - if (i != FWUPD_DEVICE_FLAG_INTERNAL) - g_string_append (str, " "); - g_string_append (str, tmp); - g_string_append (str, " ~"); - g_string_append (str, tmp); - } - g_print ("%s\n", str->str); - - return TRUE; -} - -static void -fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev) -{ - for (guint i = 0; i < devs->len; i++) { - FuDevice *dev_tmp = g_ptr_array_index (devs, i); - if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp))) - continue; - if (!priv->show_all_devices && - !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp))) - continue; - if (fu_device_get_parent (dev_tmp) == dev) { - GNode *child = g_node_append_data (root, dev_tmp); - fu_util_build_device_tree (priv, child, devs, dev_tmp); - } - } -} - -static gboolean -fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) -{ - g_autoptr(GNode) root = g_node_new (NULL); - g_autofree gchar *title = NULL; - g_autoptr(GPtrArray) devs = NULL; - - /* load engine */ - if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) - return FALSE; - title = fu_util_get_tree_title (priv); - - /* print */ - devs = fu_engine_get_devices (priv->engine, error); - if (devs == NULL) - return FALSE; - - /* print */ - if (devs->len == 0) { - /* TRANSLATORS: nothing attached that can be upgraded */ - g_print ("%s\n", _("No hardware detected with firmware update capability")); - return TRUE; - } - fwupd_device_array_ensure_parents (devs); - fu_util_build_device_tree (priv, root, devs, NULL); - fu_util_print_tree (root, title); - - /* save the device state for other applications to see */ - return fu_util_save_current_state (priv, error); -} - static FuDevice * fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error) { @@ -575,6 +428,253 @@ fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices_opt, GError * return g_object_ref (dev); } +static FuDevice * +fu_util_get_device (FuUtilPrivate *priv, const gchar *id, GError **error) +{ + if (fwupd_guid_is_valid (id)) { + g_autoptr(GPtrArray) devices = NULL; + devices = fu_engine_get_devices_by_guid (priv->engine, id, error); + if (devices == NULL) + return NULL; + return fu_util_prompt_for_device (priv, devices, error); + } + + /* did this look like a GUID? */ + for (guint i = 0; id[i] != '\0'; i++) { + if (id[i] == '-') { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return NULL; + } + } + return fu_engine_get_device (priv->engine, id, error); +} + +static gboolean +fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GNode) root = g_node_new (NULL); + g_autofree gchar *title = NULL; + gboolean no_updates_header = FALSE; + gboolean latest_header = FALSE; + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + title = fu_util_get_tree_title (priv); + + /* parse arguments */ + if (g_strv_length (values) == 0) { + devices = fu_engine_get_devices (priv->engine, error); + if (devices == NULL) + return FALSE; + } else if (g_strv_length (values) == 1) { + FuDevice *device; + device = fu_util_get_device (priv, values[0], error); + if (device == NULL) + return FALSE; + devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_ptr_array_add (devices, device); + } else { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + fwupd_device_array_ensure_parents (devices); + g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index (devices, i); + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GError) error_local = NULL; + GNode *child; + + /* not going to have results, so save a engine round-trip */ + if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE)) + continue; + if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) { + if (!no_updates_header) { + /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */ + g_printerr ("%s\n", _("Devices with no available firmware updates: ")); + no_updates_header = TRUE; + } + g_printerr (" • %s\n", fwupd_device_get_name (dev)); + continue; + } + if (!fu_util_filter_device (priv, dev)) + continue; + + /* get the releases for this device and filter for validity */ + rels = fu_engine_get_upgrades (priv->engine, + priv->request, + fwupd_device_get_id (dev), + &error_local); + if (rels == NULL) { + if (!latest_header) { + /* TRANSLATORS: message letting the user know no device upgrade available */ + g_printerr ("%s\n", _("Devices with the latest available firmware version:")); + latest_header = TRUE; + } + g_printerr (" • %s\n", fwupd_device_get_name (dev)); + /* discard the actual reason from user, but leave for debugging */ + g_debug ("%s", error_local->message); + continue; + } + child = g_node_append_data (root, dev); + + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel = g_ptr_array_index (rels, j); + g_node_append_data (child, g_object_ref (rel)); + } + } + /* save the device state for other applications to see */ + if (!fu_util_save_current_state (priv, error)) + return FALSE; + + /* updates */ + if (g_node_n_nodes (root, G_TRAVERSE_ALL) <= 1) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No updates available for remaining devices"); + return FALSE; + } + + fu_util_print_tree (root, title); + return TRUE; +} + +static gboolean +fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) array = NULL; + g_autoptr(GNode) root = g_node_new (NULL); + g_autofree gchar *title = NULL; + gint fd; + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + title = fu_util_get_tree_title (priv); + + /* check args */ + if (g_strv_length (values) != 1) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* implied, important for get-details on a device not in your system */ + priv->show_all = TRUE; + + /* open file */ + fd = open (values[0], O_RDONLY); + if (fd < 0) { + fu_util_maybe_prefix_sandbox_error (values[0], error); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to open %s", + values[0]); + return FALSE; + } + array = fu_engine_get_details (priv->engine, priv->request, fd, error); + close (fd); + + if (array == NULL) + return FALSE; + for (guint i = 0; i < array->len; i++) { + FwupdDevice *dev = g_ptr_array_index (array, i); + FwupdRelease *rel; + GNode *child; + if (!fu_util_filter_device (priv, dev)) + continue; + child = g_node_append_data (root, dev); + rel = fwupd_device_get_release_default (dev); + if (rel != NULL) + g_node_append_data (child, rel); + + } + fu_util_print_tree (root, title); + + return TRUE; +} + +static gboolean +fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GString) str = g_string_new (NULL); + + for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) { + const gchar *tmp = fwupd_device_flag_to_string (i); + if (tmp == NULL) + break; + if (i != FWUPD_DEVICE_FLAG_INTERNAL) + g_string_append (str, " "); + g_string_append (str, tmp); + g_string_append (str, " ~"); + g_string_append (str, tmp); + } + g_print ("%s\n", str->str); + + return TRUE; +} + +static void +fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev) +{ + for (guint i = 0; i < devs->len; i++) { + FuDevice *dev_tmp = g_ptr_array_index (devs, i); + if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp))) + continue; + if (!priv->show_all && + !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp))) + continue; + if (fu_device_get_parent (dev_tmp) == dev) { + GNode *child = g_node_append_data (root, dev_tmp); + fu_util_build_device_tree (priv, child, devs, dev_tmp); + } + } +} + +static gboolean +fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GNode) root = g_node_new (NULL); + g_autofree gchar *title = NULL; + g_autoptr(GPtrArray) devs = NULL; + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + title = fu_util_get_tree_title (priv); + + /* print */ + devs = fu_engine_get_devices (priv->engine, error); + if (devs == NULL) + return FALSE; + + /* print */ + if (devs->len == 0) { + /* TRANSLATORS: nothing attached that can be upgraded */ + g_print ("%s\n", _("No hardware detected with firmware update capability")); + return TRUE; + } + fwupd_device_array_ensure_parents (devs); + fu_util_build_device_tree (priv, root, devs, NULL); + fu_util_print_tree (root, title); + + /* save the device state for other applications to see */ + return fu_util_save_current_state (priv, error); +} + static void fu_util_update_device_changed_cb (FwupdClient *client, FwupdDevice *device, @@ -639,30 +739,6 @@ fu_util_display_current_message (FuUtilPrivate *priv) g_clear_pointer (&priv->current_message, g_free); } -static FuDevice * -fu_util_get_device (FuUtilPrivate *priv, const gchar *id, GError **error) -{ - if (fwupd_guid_is_valid (id)) { - g_autoptr(GPtrArray) devices = NULL; - devices = fu_engine_get_devices_by_guid (priv->engine, id, error); - if (devices == NULL) - return NULL; - return fu_util_prompt_for_device (priv, devices, error); - } - - /* did this look like a GUID? */ - for (guint i = 0; id[i] != '\0'; i++) { - if (id[i] == '-') { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_ARGS, - "Invalid arguments"); - return NULL; - } - } - return fu_engine_get_device (priv->engine, id, error); -} - static gboolean fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -745,7 +821,7 @@ fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error) } static gboolean -fu_util_firmware_read (FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_firmware_dump (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(FuDevice) device = NULL; g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0); @@ -794,7 +870,7 @@ fu_util_firmware_read (FuUtilPrivate *priv, gchar **values, GError **error) G_CALLBACK (fu_util_update_device_changed_cb), priv); /* dump firmware */ - blob_fw = fu_engine_firmware_read (priv->engine, device, priv->flags, error); + blob_fw = fu_engine_firmware_dump (priv->engine, device, priv->flags, error); if (blob_fw == NULL) return FALSE; return fu_common_set_contents_bytes (values[0], blob_fw, error); @@ -816,7 +892,9 @@ fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **err { NULL } }; for (guint i = 0; argv[i][0] != NULL; i++) { g_autoptr(GError) error_local = NULL; - if (!fu_common_find_program_in_path (argv[i][0], &error_local)) { + g_autofree gchar *fn_tmp = NULL; + fn_tmp = fu_common_find_program_in_path (argv[i][0], &error_local); + if (fn_tmp == NULL) { g_debug ("%s", error_local->message); continue; } @@ -1046,11 +1124,14 @@ static gboolean fu_util_update_all (FuUtilPrivate *priv, GError **error) { g_autoptr(GPtrArray) devices = NULL; + gboolean no_updates_header = FALSE; + gboolean latest_header = FALSE; devices = fu_engine_get_devices (priv->engine, error); if (devices == NULL) return FALSE; fwupd_device_array_ensure_parents (devices); + g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb); for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); FwupdRelease *rel; @@ -1064,11 +1145,12 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error) if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE)) continue; if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) { - /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS - * %1 is the device name */ - g_autofree gchar *tmp = g_strdup_printf (_("• %s has no available firmware updates"), - fwupd_device_get_name (dev)); - g_printerr ("%s\n", tmp); + if (!no_updates_header) { + /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */ + g_printerr ("%s\n", _("Devices with no available firmware updates: ")); + no_updates_header = TRUE; + } + g_printerr (" • %s\n", fwupd_device_get_name (dev)); continue; } if (!fu_util_filter_device (priv, dev)) @@ -1080,11 +1162,12 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error) device_id, &error_local); if (rels == NULL) { - /* TRANSLATORS: message letting the user know no device upgrade available - * %1 is the device name */ - g_autofree gchar *tmp = g_strdup_printf (_("• %s has the latest available firmware version"), - fwupd_device_get_name (dev)); - g_printerr ("%s\n", tmp); + if (!latest_header) { + /* TRANSLATORS: message letting the user know no device upgrade available */ + g_printerr ("%s\n", _("Devices with the latest available firmware version:")); + latest_header = TRUE; + } + g_printerr (" • %s\n", fwupd_device_get_name (dev)); /* discard the actual reason from user, but leave for debugging */ g_debug ("%s", error_local->message); continue; @@ -1288,6 +1371,66 @@ fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error) return fu_device_detach (device, error); } +static gboolean +fu_util_unbind_driver (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + + /* get device */ + if (g_strv_length (values) == 1) { + device = fu_util_get_device (priv, values[0], error); + } else { + device = fu_util_prompt_for_device (priv, NULL, error); + } + if (device == NULL) + return FALSE; + + /* run vfunc */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_unbind_driver (device, error); +} + +static gboolean +fu_util_bind_driver (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + + /* get device */ + if (g_strv_length (values) == 3) { + device = fu_util_get_device (priv, values[2], error); + if (device == NULL) + return FALSE; + } else if (g_strv_length (values) == 2) { + device = fu_util_prompt_for_device (priv, NULL, error); + if (device == NULL) + return FALSE; + } else { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* run vfunc */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_bind_driver (device, values[0], values[1], error); +} + static gboolean fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -1317,20 +1460,57 @@ fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error) } static gboolean -fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_check_activation_needed (FuUtilPrivate *priv, GError **error) { gboolean has_pending = FALSE; g_autoptr(FuHistory) history = fu_history_new (); + g_autoptr(GPtrArray) devices = fu_history_get_devices (history, error); + if (devices == NULL) + return FALSE; + + /* only start up the plugins needed */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index (devices, i); + if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + fu_engine_add_plugin_filter (priv->engine, + fu_device_get_plugin (dev)); + has_pending = TRUE; + } + } + + if (!has_pending) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No devices to activate"); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error) +{ + gboolean has_pending = FALSE; g_autoptr(GPtrArray) devices = NULL; /* check the history database before starting the daemon */ + if (!fu_util_check_activation_needed (priv, error)) + return FALSE; + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error)) + return FALSE; + + /* parse arguments */ if (g_strv_length (values) == 0) { - devices = fu_history_get_devices (history, error); + devices = fu_engine_get_devices (priv->engine, error); if (devices == NULL) return FALSE; } else if (g_strv_length (values) == 1) { FuDevice *device; - device = fu_history_get_device_by_id (history, values[0], error); + device = fu_util_get_device (priv, values[0], error); if (device == NULL) return FALSE; devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); @@ -1343,38 +1523,30 @@ fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error) return FALSE; } - /* nothing to do */ - for (guint i = 0; i < devices->len; i++) { - FuDevice *dev = g_ptr_array_index (devices, i); - if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { - fu_engine_add_plugin_filter (priv->engine, - fu_device_get_plugin (dev)); - has_pending = TRUE; - } - } - - if (!has_pending) { - g_printerr ("No firmware to activate\n"); - return TRUE; - } - - /* load engine */ - if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error)) - return FALSE; - /* activate anything with _NEEDS_ACTIVATION */ + /* order by device priority */ + g_ptr_array_sort (devices, fu_util_device_order_sort_cb); for (guint i = 0; i < devices->len; i++) { FuDevice *device = g_ptr_array_index (devices, i); if (!fu_util_filter_device (priv, FWUPD_DEVICE (device))) continue; if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) continue; + has_pending = TRUE; /* TRANSLATORS: shown when shutting down to switch to the new version */ g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device)); if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error)) return FALSE; } + if (!has_pending) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No devices to activate"); + return FALSE; + } + return TRUE; } @@ -1674,6 +1846,178 @@ fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +static gboolean +fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error) +{ + GType gtype; + g_autofree gchar *firmware_type = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) images = NULL; + + /* check args */ + if (g_strv_length (values) == 0 || g_strv_length (values) > 2) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments: filename required"); + return FALSE; + } + if (g_strv_length (values) == 2) + firmware_type = g_strdup (values[1]); + + /* load file */ + blob = fu_common_get_contents_bytes (values[0], error); + if (blob == NULL) + return FALSE; + + /* load engine */ + if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error)) + return FALSE; + + /* find the GType to use */ + if (firmware_type == NULL) + firmware_type = fu_util_prompt_for_firmware_type (priv, error); + if (firmware_type == NULL) + return FALSE; + gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type); + if (gtype == G_TYPE_INVALID) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not supported", firmware_type); + return FALSE; + } + firmware = g_object_new (gtype, NULL); + if (!fu_firmware_parse (firmware, blob, priv->flags, error)) + return FALSE; + str = fu_firmware_to_string (firmware); + g_print ("%s", str); + images = fu_firmware_get_images (firmware); + for (guint i = 0; i < images->len; i++) { + FuFirmwareImage *img = g_ptr_array_index (images, i); + g_autofree gchar *fn = NULL; + g_autoptr(GBytes) blob_img = NULL; + + /* get raw image without generated header, footer or crc */ + blob_img = fu_firmware_image_get_bytes (img); + if (blob_img == NULL || g_bytes_get_size (blob_img) == 0) + continue; + + /* use suitable filename */ + if (fu_firmware_image_get_filename (img) != NULL) { + fn = g_strdup (fu_firmware_image_get_filename (img)); + } else if (fu_firmware_image_get_id (img) != NULL) { + fn = g_strdup_printf ("id-%s.fw", fu_firmware_image_get_id (img)); + } else if (fu_firmware_image_get_idx (img) != 0x0) { + fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_image_get_idx (img)); + } else { + fn = g_strdup_printf ("img-0x%x.fw", i); + } + /* TRANSLATORS: decompressing images from a container firmware */ + g_print ("%s : %s\n", _("Writing file:"), fn); + if (!fu_common_set_contents_bytes (fn, blob_img, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_util_firmware_build (FuUtilPrivate *priv, gchar **values, GError **error) +{ + GType gtype = FU_TYPE_FIRMWARE; + const gchar *tmp; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuFirmware) firmware_dst = NULL; + g_autoptr(GBytes) blob_dst = NULL; + g_autoptr(GBytes) blob_src = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + g_autoptr(XbNode) n = NULL; + g_autoptr(XbSilo) silo = NULL; + + /* check args */ + if (g_strv_length (values) != 2) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments: filename required"); + return FALSE; + } + + /* load file */ + blob_src = fu_common_get_contents_bytes (values[0], error); + if (blob_src == NULL) + return FALSE; + + /* load engine */ + if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error)) + return FALSE; + + /* parse XML */ + if (!xb_builder_source_load_bytes (source, blob_src, + XB_BUILDER_SOURCE_FLAG_NONE, + error)) { + g_prefix_error (error, "could not parse XML: "); + return FALSE; + } + xb_builder_import_source (builder, source); + silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); + if (silo == NULL) + return FALSE; + + /* create FuFirmware of specific GType */ + n = xb_silo_query_first (silo, "firmware", error); + if (n == NULL) + return FALSE; + tmp = xb_node_get_attr (n, "gtype"); + if (tmp != NULL) { + gtype = g_type_from_name (tmp); + if (gtype == G_TYPE_INVALID) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not registered", tmp); + return FALSE; + } + } + tmp = xb_node_get_attr (n, "id"); + if (tmp != NULL) { + gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp); + if (gtype == G_TYPE_INVALID) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not supported", tmp); + return FALSE; + } + } + firmware = g_object_new (gtype, NULL); + if (!fu_firmware_build (firmware, n, error)) + return FALSE; + + /* write new file */ + blob_dst = fu_firmware_write (firmware, error); + if (blob_dst == NULL) + return FALSE; + if (!fu_common_set_contents_bytes (values[1], blob_dst, error)) + return FALSE; + + /* show what we wrote */ + firmware_dst = g_object_new (gtype, NULL); + if (!fu_firmware_parse (firmware_dst, blob_dst, priv->flags, error)) + return FALSE; + str = fu_firmware_to_string (firmware_dst); + g_print ("%s", str); + + /* success */ + return TRUE; +} + static gboolean fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -1729,6 +2073,9 @@ fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error) "GType %s not supported", firmware_type_src); return FALSE; } + firmware_src = g_object_new (gtype_src, NULL); + if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error)) + return FALSE; gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst); if (gtype_dst == G_TYPE_INVALID) { g_set_error (error, @@ -1737,9 +2084,6 @@ fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error) "GType %s not supported", firmware_type_dst); return FALSE; } - firmware_src = g_object_new (gtype_src, NULL); - if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error)) - return FALSE; str_src = fu_firmware_to_string (firmware_src); g_print ("%s", str_src); @@ -1878,39 +2222,43 @@ fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error g_autoptr(GBytes) bytes_raw = NULL; g_autoptr(GBytes) bytes_sig = NULL; - /* payload */ - metadata_uri = fwupd_remote_get_metadata_uri (remote); - if (metadata_uri == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "no metadata URI available"); - return FALSE; - } - fn_raw = fu_util_get_user_cache_path (metadata_uri); - if (!fu_common_mkdir_parent (fn_raw, error)) - return FALSE; - if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error)) - return FALSE; - bytes_raw = fu_common_get_contents_bytes (fn_raw, error); - if (bytes_raw == NULL) - return FALSE; - /* signature */ metadata_uri = fwupd_remote_get_metadata_uri_sig (remote); if (metadata_uri == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "no metadata signature URI available"); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "no metadata signature URI available for %s", + fwupd_remote_get_id (remote)); return FALSE; } fn_sig = fu_util_get_user_cache_path (metadata_uri); + if (!fu_common_mkdir_parent (fn_sig, error)) + return FALSE; if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error)) return FALSE; bytes_sig = fu_common_get_contents_bytes (fn_sig, error); if (bytes_sig == NULL) return FALSE; + if (!fwupd_remote_load_signature_bytes (remote, bytes_sig, error)) + return FALSE; + + /* payload */ + metadata_uri = fwupd_remote_get_metadata_uri (remote); + if (metadata_uri == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "no metadata URI available for %s", + fwupd_remote_get_id (remote)); + return FALSE; + } + fn_raw = fu_util_get_user_cache_path (metadata_uri); + if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error)) + return FALSE; + bytes_raw = fu_common_get_contents_bytes (fn_raw, error); + if (bytes_raw == NULL) + return FALSE; /* send to daemon */ g_debug ("updating %s", fwupd_remote_get_id (remote)); @@ -1979,6 +2327,44 @@ fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +static gboolean +fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error) +{ + FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE; + g_autoptr(FuSecurityAttrs) attrs = NULL; + g_autoptr(GPtrArray) items = NULL; + g_autofree gchar *str = NULL; + + /* not ready yet */ + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "The HSI specification is not yet complete. " + "To ignore this warning, use --force"); + return FALSE; + } + + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + + /* TRANSLATORS: this is a string like 'HSI:2-U' */ + g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"), + fu_engine_get_host_security_id (priv->engine)); + + /* show or hide different elements */ + if (priv->show_all) { + flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES; + flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS; + } + + /* print the "why" */ + attrs = fu_engine_get_host_security_attrs (priv->engine); + items = fu_security_attrs_get_all (attrs); + str = fu_util_security_attrs_to_string (items, flags); + g_print ("%s\n", str); + return TRUE; +} static FuVolume * fu_util_prompt_for_volume (GError **error) @@ -1989,6 +2375,8 @@ fu_util_prompt_for_volume (GError **error) /* exactly one */ volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error); + if (volumes == NULL) + return NULL; if (volumes->len == 1) { volume = g_ptr_array_index (volumes, 0); /* TRANSLATORS: Volume has been chosen by the user */ @@ -2062,14 +2450,145 @@ fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } + +static gboolean +fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error) +{ + const gchar *branch; + g_autoptr(FwupdRelease) rel = NULL; + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free); + g_autoptr(FuDevice) dev = NULL; + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + + /* find the device and check it has multiple branches */ + priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES; + if (g_strv_length (values) == 1) + dev = fu_util_get_device (priv, values[1], error); + else + dev = fu_util_prompt_for_device (priv, NULL, error); + if (dev == NULL) + return FALSE; + if (!fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Multiple branches not available"); + return FALSE; + } + + /* get all releases, including the alternate branch versions */ + rels = fu_engine_get_releases (priv->engine, + priv->request, + fu_device_get_id (dev), + error); + if (rels == NULL) + return FALSE; + + /* get all the unique branches */ + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel_tmp = g_ptr_array_index (rels, i); + const gchar *branch_tmp = fu_util_release_get_branch (rel_tmp); + if (g_ptr_array_find_with_equal_func (branches, branch_tmp, + g_str_equal, NULL)) + continue; + g_ptr_array_add (branches, g_strdup (branch_tmp)); + } + + /* branch name is optional */ + if (g_strv_length (values) > 1) { + branch = values[1]; + } else if (branches->len == 1) { + branch = g_ptr_array_index (branches, 0); + } else { + guint idx; + + /* TRANSLATORS: get interactive prompt, where branch is the + * supplier of the firmware, e.g. "non-free" or "free" */ + g_print ("%s\n", _("Choose a branch:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print ("0.\t%s\n", _("Cancel")); + for (guint i = 0; i < branches->len; i++) { + const gchar *branch_tmp = g_ptr_array_index (branches, i); + g_print ("%u.\t%s\n", i + 1, branch_tmp); + } + idx = fu_util_prompt_for_number (branches->len); + if (idx == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return FALSE; + } + branch = g_ptr_array_index (branches, idx - 1); + } + + /* sanity check */ + if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s is already on branch %s", + fu_device_get_name (dev), + branch); + return FALSE; + } + + /* the releases are ordered by version */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel_tmp = g_ptr_array_index (rels, j); + if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) { + rel = g_object_ref (rel_tmp); + break; + } + } + if (rel == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No releases for branch %s", + branch); + return FALSE; + } + + /* we're switching branch */ + if (!fu_util_switch_branch_warning (FWUPD_DEVICE (dev), rel, FALSE, error)) + return FALSE; + + /* update the console if composite devices are also updated */ + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect (priv->engine, "device-changed", + G_CALLBACK (fu_util_update_device_changed_cb), priv); + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (!fu_util_install_release (priv, rel, error)) + return FALSE; + fu_util_display_current_message (priv); + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug ("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete (priv->completion_flags, TRUE, error); +} + int main (int argc, char *argv[]) { + gboolean allow_branch_switch = FALSE; gboolean allow_older = FALSE; gboolean allow_reinstall = FALSE; gboolean force = FALSE; gboolean ret; gboolean version = FALSE; + gboolean ignore_checksum = FALSE; + gboolean ignore_power = FALSE; + gboolean ignore_vid_pid = FALSE; gboolean interactive = isatty (fileno (stdout)) != 0; g_auto(GStrv) plugin_glob = NULL; g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1); @@ -2087,21 +2606,39 @@ main (int argc, char *argv[]) { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older, /* TRANSLATORS: command line option */ _("Allow downgrading firmware versions"), NULL }, + { "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch, + /* TRANSLATORS: command line option */ + _("Allow switching firmware branch"), NULL }, { "force", '\0', 0, G_OPTION_ARG_NONE, &force, /* TRANSLATORS: command line option */ - _("Override plugin warning"), NULL }, + _("Force the action by relaxing some runtime checks"), NULL }, + { "ignore-checksum", '\0', 0, G_OPTION_ARG_NONE, &ignore_checksum, + /* TRANSLATORS: command line option */ + _("Ignore firmware checksum failures"), NULL }, + { "ignore-vid-pid", '\0', 0, G_OPTION_ARG_NONE, &ignore_vid_pid, + /* TRANSLATORS: command line option */ + _("Ignore firmware hardware mismatch failures"), NULL }, + { "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power, + /* TRANSLATORS: command line option */ + _("Ignore requirement of external power source"), NULL }, { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check, /* TRANSLATORS: command line option */ _("Do not check for reboot after update"), NULL }, { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check, /* TRANSLATORS: command line option */ _("Do not perform device safety checks"), NULL }, - { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices, + { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all, + /* TRANSLATORS: command line option */ + _("Show all results"), NULL }, + { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all, /* TRANSLATORS: command line option */ _("Show devices that are not updatable"), NULL }, - { "plugin-whitelist", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob, + { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob, /* TRANSLATORS: command line option */ - _("Manually whitelist specific plugins"), NULL }, + _("Manually enable specific plugins"), NULL }, + { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob, + /* TRANSLATORS: command line option */ + _("Manually enable specific plugins"), NULL }, { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob, /* TRANSLATORS: command line option */ _("Run the plugin composite prepare routine when using install-blob"), NULL }, @@ -2177,7 +2714,7 @@ main (int argc, char *argv[]) fu_util_get_history); fu_util_cmd_array_add (cmd_array, "get-updates,get-upgrades", - NULL, + "[DEVICE-ID|GUID]", /* TRANSLATORS: command description */ _("Gets the list of updates for connected hardware"), fu_util_get_updates); @@ -2229,6 +2766,18 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Detach to bootloader mode"), fu_util_detach); + fu_util_cmd_array_add (cmd_array, + "unbind-driver", + "[DEVICE-ID|GUID]", + /* TRANSLATORS: command description */ + _("Unbind current driver"), + fu_util_unbind_driver); + fu_util_cmd_array_add (cmd_array, + "bind-driver", + "subsystem driver [DEVICE-ID|GUID]", + /* TRANSLATORS: command description */ + _("Bind new kernel driver"), + fu_util_bind_driver); fu_util_cmd_array_add (cmd_array, "activate", "[DEVICE-ID|GUID]", @@ -2267,23 +2816,35 @@ main (int argc, char *argv[]) _("Update the stored metadata with current contents"), fu_util_verify_update); fu_util_cmd_array_add (cmd_array, - "firmware-read", + "firmware-dump", "FILENAME [DEVICE-ID|GUID]", /* TRANSLATORS: command description */ _("Read a firmware blob from a device"), - fu_util_firmware_read); + fu_util_firmware_dump); fu_util_cmd_array_add (cmd_array, "firmware-convert", "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]", /* TRANSLATORS: command description */ _("Convert a firmware file"), fu_util_firmware_convert); + fu_util_cmd_array_add (cmd_array, + "firmware-build", + "BUILDER-XML FILENAME-DST", + /* TRANSLATORS: command description */ + _("Build a firmware file"), + fu_util_firmware_build); fu_util_cmd_array_add (cmd_array, "firmware-parse", "FILENAME [FIRMWARE-TYPE]", /* TRANSLATORS: command description */ _("Parse and show details about a firmware file"), fu_util_firmware_parse); + fu_util_cmd_array_add (cmd_array, + "firmware-extract", + "FILENAME [FIRMWARE-TYPE]", + /* TRANSLATORS: command description */ + _("Extract a firmware blob to images"), + fu_util_firmware_extract); fu_util_cmd_array_add (cmd_array, "get-firmware-types", NULL, @@ -2302,24 +2863,36 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Refresh metadata from remote server"), fu_util_refresh); + fu_util_cmd_array_add (cmd_array, + "security", + NULL, + /* TRANSLATORS: command description */ + _("Gets the host security attributes"), + fu_util_security); fu_util_cmd_array_add (cmd_array, "esp-mount", NULL, /* TRANSLATORS: command description */ - _("Mounts the ESP."), + _("Mounts the ESP"), fu_util_esp_mount); fu_util_cmd_array_add (cmd_array, "esp-unmount", NULL, /* TRANSLATORS: command description */ - _("Unmounts the ESP."), + _("Unmounts the ESP"), fu_util_esp_unmount); fu_util_cmd_array_add (cmd_array, "esp-list", NULL, /* TRANSLATORS: command description */ - _("Lists files on the ESP."), + _("Lists files on the ESP"), fu_util_esp_list); + fu_util_cmd_array_add (cmd_array, + "switch-branch", + "[DEVICE-ID|GUID] [BRANCH]", + /* TRANSLATORS: command description */ + _("Switch the firmware branch on the device"), + fu_util_switch_branch); /* do stuff on ctrl+c */ priv->cancellable = g_cancellable_new (); @@ -2343,6 +2916,7 @@ main (int argc, char *argv[]) /* set our implemented feature set */ fu_engine_request_set_feature_flags (priv->request, FWUPD_FEATURE_FLAG_DETACH_ACTION | + FWUPD_FEATURE_FLAG_SWITCH_BRANCH | FWUPD_FEATURE_FLAG_UPDATE_ACTION); } @@ -2368,10 +2942,13 @@ main (int argc, char *argv[]) /* allow disabling SSL strict mode for broken corporate proxies */ if (priv->disable_ssl_strict) { + g_autofree gchar *fmt = NULL; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED); /* TRANSLATORS: try to help */ - g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, " - "to do this automatically in the future " - "export DISABLE_SSL_STRICT in your environment")); + g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, " + "to do this automatically in the future " + "export DISABLE_SSL_STRICT in your environment")); g_setenv ("DISABLE_SSL_STRICT", "1", TRUE); } @@ -2394,8 +2971,18 @@ main (int argc, char *argv[]) priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; if (allow_older) priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; - if (force) + if (allow_branch_switch) + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (force) { priv->flags |= FWUPD_INSTALL_FLAG_FORCE; + priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER; + } + if (ignore_checksum) + priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM; + if (ignore_vid_pid) + priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID; + if (ignore_power) + priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER; /* load engine */ priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES); @@ -2419,24 +3006,21 @@ main (int argc, char *argv[]) return EXIT_SUCCESS; } - /* any plugin whitelist specified */ + /* any plugin allowlist specified */ for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++) fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]); /* run the specified command */ ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error); if (!ret) { + g_printerr ("%s\n", error->message); if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) { - g_autofree gchar *tmp = NULL; - tmp = g_option_context_get_help (priv->context, TRUE, NULL); - g_print ("%s\n\n%s", error->message, tmp); - return EXIT_FAILURE; - } - if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { - g_print ("%s\n", error->message); + /* TRANSLATORS: error message explaining command to run to how to get help */ + g_printerr ("\n%s\n", _("Use fwupdtool --help for help")); + } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_debug ("%s\n", error->message); return EXIT_NOTHING_TO_DO; } - g_print ("%s\n", error->message); return EXIT_FAILURE; } diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 1c6b3ce6a..580bae264 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -12,11 +12,14 @@ #include #include #include -#include +#include #include "fu-common.h" +#include "fu-device-private.h" #include "fu-util-common.h" #include "fu-device.h" +#include "fu-security-attr.h" +#include "fu-security-attrs.h" #ifdef HAVE_SYSTEMD #include "fu-systemd.h" @@ -33,6 +36,12 @@ fu_util_get_systemd_unit (void) return SYSTEMD_FWUPD_UNIT; } +gchar * +fu_util_term_format (const gchar *text, FuUtilTermColor fg_color) +{ + return g_strdup_printf ("\033[%um\033[1m%s\033[0m", fg_color, text); +} + #ifdef HAVE_SYSTEMD static const gchar * fu_util_get_expected_command (const gchar *target) @@ -584,6 +593,17 @@ fu_util_cmd_array_to_string (GPtrArray *array) return g_string_free (string, FALSE); } +const gchar * +fu_util_release_get_branch (FwupdRelease *release) +{ + const gchar *tmp = fwupd_release_get_branch (release); + if (tmp == NULL) { + /* TRANSLATORS: this is the default branch name when unset */ + return _("default"); + } + return tmp; +} + gchar * fu_util_release_get_name (FwupdRelease *release) { @@ -965,7 +985,7 @@ fu_util_device_flag_to_string (guint64 device_flag) } if (device_flag == FWUPD_DEVICE_FLAG_REQUIRE_AC) { /* TRANSLATORS: Must be plugged in to an outlet */ - return _("Requires AC power"); + return _("System requires external power source"); } if (device_flag == FWUPD_DEVICE_FLAG_LOCKED) { /* TRANSLATORS: Is locked and can be unlocked */ @@ -1071,6 +1091,14 @@ fu_util_device_flag_to_string (guint64 device_flag) /* TRANSLATORS: a version check is required for all firmware */ return _("Device is required to install all provided releases"); } + if (device_flag == FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES) { + /* TRANSLATORS: there is more than one supplier of the firmware */ + return _("Device supports switching to a different branch of firmware"); + } + if (device_flag == FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL) { + /* TRANSLATORS: save the old firmware to disk before installing the new one */ + return _("Device will backup firmware before installing"); + } if (device_flag == FWUPD_DEVICE_FLAG_MD_SET_NAME) { /* skip */ return NULL; @@ -1276,6 +1304,121 @@ fu_util_device_to_string (FwupdDevice *dev, guint idt) return g_string_free (str, FALSE); } +const gchar * +fu_util_plugin_flag_to_string (FwupdPluginFlags plugin_flag) +{ + if (plugin_flag == FWUPD_PLUGIN_FLAG_UNKNOWN) + return NULL; + if (plugin_flag == FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE) + return NULL; + if (plugin_flag == FWUPD_PLUGIN_FLAG_USER_WARNING) + return NULL; + if (plugin_flag == FWUPD_PLUGIN_FLAG_NONE) { + /* TRANSLATORS: Plugin is active and in use */ + return _("Enabled"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_DISABLED) { + /* TRANSLATORS: Plugin is inactive and not used */ + return _("Disabled"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_NO_HARDWARE) { + /* TRANSLATORS: not required for this system */ + return _("Required hardware was not found"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_LEGACY_BIOS) { + /* TRANSLATORS: system is not booted in UEFI mode */ + return _("Firmware can not be updated in legacy BIOS mode"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED) { + /* TRANSLATORS: capsule updates are an optional BIOS feature */ + return _("UEFI capsule updates not available or enabled"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED) { + /* TRANSLATORS: user needs to run a command */ + return _("Firmware updates disabled; run 'fwupdmgr unlock' to enable"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED) { + /* TRANSLATORS: the user is using Gentoo/Arch and has screwed something up */ + return _("Required efivarfs filesystem was not found"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND) { + /* TRANSLATORS: partition refers to something on disk, again, hey Arch users */ + return _("UEFI ESP partition not detected or configured"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_FAILED_OPEN) { + /* TRANSLATORS: Failed to open plugin, hey Arch users */ + return _("Plugin dependencies missing"); + } + + /* fall back for unknown types */ + return fwupd_plugin_flag_to_string (plugin_flag); +} + +static gchar * +fu_util_plugin_flag_to_cli_text (FwupdPluginFlags plugin_flag) +{ + switch (plugin_flag) { + case FWUPD_PLUGIN_FLAG_UNKNOWN: + case FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE: + case FWUPD_PLUGIN_FLAG_USER_WARNING: + return NULL; + case FWUPD_PLUGIN_FLAG_NONE: + return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag), + FU_UTIL_CLI_COLOR_GREEN); + case FWUPD_PLUGIN_FLAG_DISABLED: + case FWUPD_PLUGIN_FLAG_NO_HARDWARE: + return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag), + FU_UTIL_CLI_COLOR_BLACK); + case FWUPD_PLUGIN_FLAG_LEGACY_BIOS: + case FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED: + case FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED: + case FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED: + case FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND: + return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag), + FU_UTIL_TERM_COLOR_RED); + default: + break; + } + + /* fall back for unknown types */ + return g_strdup (fwupd_plugin_flag_to_string (plugin_flag)); +} + +gchar * +fu_util_plugin_to_string (FwupdPlugin *plugin, guint idt) +{ + GString *str = g_string_new (NULL); + const gchar *hdr; + guint64 flags = fwupd_plugin_get_flags (plugin); + + fu_common_string_append_kv (str, idt, fwupd_plugin_get_name (plugin), NULL); + + /* TRANSLATORS: description of plugin state, e.g. disabled */ + hdr = _("Flags"); + if (flags == 0x0) { + const gchar *tmp = fu_util_plugin_flag_to_cli_text (flags); + g_autofree gchar *li = g_strdup_printf ("• %s", tmp); + fu_common_string_append_kv (str, idt + 1, hdr, li); + } else { + for (guint i = 0; i < 64; i++) { + g_autofree gchar *li = NULL; + g_autofree gchar *tmp = NULL; + if ((flags & ((guint64) 1 << i)) == 0) + continue; + tmp = fu_util_plugin_flag_to_cli_text ((guint64) 1 << i); + if (tmp == NULL) + continue; + li = g_strdup_printf ("• %s", tmp); + fu_common_string_append_kv (str, idt + 1, hdr, li); + + /* clear header */ + hdr = ""; + } + } + + return g_string_free (str, FALSE); +} + static const gchar * fu_util_license_to_string (const gchar *license) { @@ -1335,6 +1478,11 @@ fu_util_release_to_string (FwupdRelease *rel, guint idt) fu_common_string_append_kv (str, idt + 1, _("Remote ID"), fwupd_release_get_remote_id (rel)); } + if (fwupd_release_get_branch (rel) != NULL) { + /* TRANSLATORS: the stream of firmware, e.g. nonfree or open-source */ + fu_common_string_append_kv (str, idt + 1, _("Branch"), + fwupd_release_get_branch (rel)); + } if (fwupd_release_get_summary (rel) != NULL) { /* TRANSLATORS: one line summary of device */ fu_common_string_append_kv (str, idt + 1, _("Summary"), @@ -1543,6 +1691,132 @@ fu_util_remote_to_string (FwupdRemote *remote, guint idt) return g_string_free (str, FALSE); } +static void +fu_security_attr_append_str (FwupdSecurityAttr *attr, GString *str, FuSecurityAttrToStringFlags flags) +{ + g_autofree gchar *name = NULL; + + /* hide obsoletes by default */ + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED) && + (flags & FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES) == 0) + return; + + name = fu_security_attr_get_name (attr); + if (name == NULL) + name = g_strdup (fwupd_security_attr_get_appstream_id (attr)); + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { + g_string_append (str, "✦ "); + } else if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { + g_string_append (str, "✔ "); + } else { + g_string_append (str, "✘ "); + } + g_string_append_printf (str, "%s:", name); + for (guint i = fu_common_strwidth (name); i < 30; i++) + g_string_append (str, " "); + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { + g_string_append_printf (str, "\033[37m\033[1m%s\033[0m", fu_security_attr_get_result (attr)); + } else if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { + g_string_append_printf (str, "\033[32m\033[1m%s\033[0m", fu_security_attr_get_result (attr)); + } else { + g_string_append_printf (str, "\033[31m\033[1m%s\033[0m", fu_security_attr_get_result (attr)); + } + if ((flags & FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS) > 0 && + fwupd_security_attr_get_url (attr) != NULL) { + g_string_append_printf (str, ": %s", + fwupd_security_attr_get_url (attr)); + } + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { + /* TRANSLATORS: this is shown as a suffix for obsoleted tests */ + g_string_append_printf (str, " %s", _("(obsoleted)")); + } + g_string_append_printf (str, "\n"); +} + +gchar * +fu_util_security_attrs_to_string (GPtrArray *attrs, FuSecurityAttrToStringFlags strflags) +{ + FwupdSecurityAttrFlags flags = FWUPD_SECURITY_ATTR_FLAG_NONE; + const FwupdSecurityAttrFlags hpi_suffixes[] = { + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES, + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION, + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE, + FWUPD_SECURITY_ATTR_FLAG_NONE, + }; + GString *str = g_string_new (NULL); + gboolean low_help = FALSE; + gboolean runtime_help = FALSE; + gboolean pcr0_help = FALSE; + + for (guint j = 1; j <= FWUPD_SECURITY_ATTR_LEVEL_LAST; j++) { + gboolean has_header = FALSE; + for (guint i = 0; i < attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i); + if (fwupd_security_attr_get_level (attr) != j) + continue; + if (!has_header) { + g_string_append_printf (str, "\n\033[1mHSI-%u\033[0m\n", j); + has_header = TRUE; + } + fu_security_attr_append_str (attr, str, strflags); + /* make sure they have at least HSI-1 */ + if (j < FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT && + !fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) + low_help = TRUE; + + /* check for PCR0 not matching */ + if (g_strcmp0 (fwupd_security_attr_get_appstream_id (attr), + FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0) == 0 && + fwupd_security_attr_get_result (attr) == FWUPD_SECURITY_ATTR_RESULT_NOT_VALID) + pcr0_help = TRUE; + } + } + for (guint i = 0; i < attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i); + flags |= fwupd_security_attr_get_flags (attr); + } + for (guint j = 0; hpi_suffixes[j] != FWUPD_SECURITY_ATTR_FLAG_NONE; j++) { + if (flags & hpi_suffixes[j]) { + g_string_append_printf (str, "\n\033[1m%s -%s\033[0m\n", + /* TRANSLATORS: this is the HSI suffix */ + _("Runtime Suffix"), + fwupd_security_attr_flag_to_suffix (hpi_suffixes[j])); + for (guint i = 0; i < attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i); + if (!fwupd_security_attr_has_flag (attr, hpi_suffixes[j])) + continue; + if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) && + !fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) + runtime_help = TRUE; + fu_security_attr_append_str (attr, str, strflags); + } + } + } + + if (low_help) { + g_string_append_printf (str, "\n%s\n » %s\n", + /* TRANSLATORS: this is instructions on how to improve the HSI security level */ + _("This system has a low HSI security level."), + "https://github.com/fwupd/fwupd/wiki/Low-host-security-level"); + } + if (runtime_help) { + g_string_append_printf (str, "\n%s\n » %s\n", + /* TRANSLATORS: this is instructions on how to improve the HSI suffix */ + _("This system has HSI runtime issues."), + "https://github.com/fwupd/fwupd/wiki/Host-security-ID-runtime-issues"); + } + + if (pcr0_help) { + g_string_append_printf (str, "\n%s\n » %s\n", + /* TRANSLATORS: this is more background on a security measurement problem */ + _("The TPM PCR0 differs from reconstruction."), + "https://github.com/fwupd/fwupd/wiki/TPM-PCR0-differs-from-reconstruction"); + + } + + return g_string_free (str, FALSE); +} + gboolean fu_util_send_report (FwupdClient *client, const gchar *report_uri, @@ -1627,3 +1901,95 @@ fu_util_send_report (FwupdClient *client, /* success */ return TRUE; } + +gint +fu_util_sort_devices_by_flags_cb (gconstpointer a, gconstpointer b) +{ + FuDevice *dev_a = *((FuDevice **) a); + FuDevice *dev_b = *((FuDevice **) b); + + if ((!fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_UPDATABLE) && + fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_UPDATABLE)) || + (!fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_SUPPORTED) && + fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_SUPPORTED))) + return -1; + if ((fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_UPDATABLE) && + !fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_UPDATABLE)) || + (fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_SUPPORTED) && + !fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_SUPPORTED))) + return 1; + + return 0; +} + +static gint +fu_util_device_order_compare (FuDevice *device1, FuDevice *device2) +{ + if (fu_device_get_order (device1) < fu_device_get_order (device2)) + return -1; + if (fu_device_get_order (device1) > fu_device_get_order (device2)) + return 1; + return 0; +} + +gint +fu_util_device_order_sort_cb (gconstpointer a, gconstpointer b) +{ + FuDevice *device_a = *((FuDevice **) a); + FuDevice *device_b = *((FuDevice **) b); + return fu_util_device_order_compare (device_a, device_b); +} + + +gboolean +fu_util_switch_branch_warning (FwupdDevice *dev, + FwupdRelease *rel, + gboolean assume_yes, + GError **error) +{ + const gchar *desc_markup = NULL; + g_autofree gchar *desc_plain = NULL; + g_autoptr(GString) desc_full = g_string_new (NULL); + + /* warn the user if the vendor is different */ + if (g_strcmp0 (fwupd_device_get_vendor (dev), fwupd_release_get_vendor (rel)) != 0) { + /* TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name */ + g_string_append_printf (desc_full, _("The firmware from %s is not " + "supplied by %s, the hardware vendor."), + fwupd_release_get_vendor (rel), + fwupd_device_get_vendor (dev)); + g_string_append (desc_full, "\n\n"); + /* TRANSLATORS: %1 is the device vendor name */ + g_string_append_printf (desc_full, _("Your hardware may be damaged using this firmware, " + "and installing this release may void any warranty " + "with %s."), + fwupd_device_get_vendor (dev)); + g_string_append (desc_full, "\n\n"); + } + + /* from the in the AppStream data */ + desc_markup = fwupd_release_get_description (rel); + if (desc_markup == NULL) + return TRUE; + desc_plain = fu_util_convert_description (desc_markup, error); + if (desc_plain == NULL) + return FALSE; + g_string_append (desc_full, desc_plain); + + /* show and ask user to confirm */ + fu_util_warning_box (desc_full->str, 80); + if (!assume_yes) { + /* ask for permission */ + g_print ("\n%s [y|N]: ", + /* TRANSLATORS: should the branch be changed */ + _("Do you understand the consequences of changing the firmware branch?")); + if (!fu_util_prompt_for_boolean (FALSE)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Declined branch switch"); + return FALSE; + } + } + return TRUE; +} diff --git a/src/fu-util-common.h b/src/fu-util-common.h index f5b58a5f7..35f3b21ac 100644 --- a/src/fu-util-common.h +++ b/src/fu-util-common.h @@ -24,8 +24,29 @@ typedef struct { FuUtilCmdFunc callback; } FuUtilCmd; +typedef enum { + FU_SECURITY_ATTR_TO_STRING_FLAG_NONE = 0, + FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES = 1 << 0, + FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS = 1 << 1, + /*< private >*/ + FU_SECURITY_ATTR_TO_STRING_FLAG_LAST +} FuSecurityAttrToStringFlags; + +typedef enum { + FU_UTIL_CLI_COLOR_BLACK = 30, + FU_UTIL_TERM_COLOR_RED = 31, + FU_UTIL_CLI_COLOR_GREEN = 32, + FU_UTIL_CLI_COLOR_YELLOW = 33, + FU_UTIL_CLI_COLOR_BLUE = 34, + FU_UTIL_CLI_COLOR_MAGENTA = 35, + FU_UTIL_CLI_COLOR_CYAN = 36, + FU_UTIL_CLI_COLOR_WHITE = 37, +} FuUtilTermColor; + void fu_util_print_data (const gchar *title, const gchar *msg); +gchar *fu_util_term_format (const gchar *text, + FuUtilTermColor fg_color); guint fu_util_prompt_for_number (guint maxnum); gboolean fu_util_prompt_for_boolean (gboolean def); @@ -58,6 +79,7 @@ gboolean fu_util_cmd_array_run (GPtrArray *array, gchar **values, GError **error); gchar *fu_util_release_get_name (FwupdRelease *release); +const gchar *fu_util_release_get_branch (FwupdRelease *release); const gchar *fu_util_get_systemd_unit (void); gboolean fu_util_using_correct_daemon (GError **error); @@ -72,13 +94,27 @@ gchar *fu_util_time_to_str (guint64 tmp); gchar *fu_util_device_to_string (FwupdDevice *dev, guint idt); +gchar *fu_util_plugin_to_string (FwupdPlugin *plugin, + guint idt); +const gchar *fu_util_plugin_flag_to_string (FwupdPluginFlags plugin_flag); gchar *fu_util_release_to_string (FwupdRelease *rel, guint idt); gchar *fu_util_remote_to_string (FwupdRemote *remote, guint idt); +gchar *fu_util_security_attrs_to_string (GPtrArray *attrs, + FuSecurityAttrToStringFlags flags); gboolean fu_util_send_report (FwupdClient *client, const gchar *report_uri, const gchar *data, const gchar *sig, gchar **uri, GError **error); +gint fu_util_sort_devices_by_flags_cb (gconstpointer a, + gconstpointer b); +gint fu_util_device_order_sort_cb (gconstpointer a, + gconstpointer b); + +gboolean fu_util_switch_branch_warning (FwupdDevice *dev, + FwupdRelease *rel, + gboolean assume_yes, + GError **error); diff --git a/src/fu-util.c b/src/fu-util.c index 22cc5e42b..bc4c7a3c4 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -25,7 +25,9 @@ #include "fu-history.h" #include "fu-plugin-private.h" +#include "fu-polkit-agent.h" #include "fu-progressbar.h" +#include "fu-security-attrs.h" #include "fu-util-common.h" #include "fwupd-common-private.h" @@ -65,7 +67,7 @@ struct FuUtilPrivate { gboolean no_safety_check; gboolean assume_yes; gboolean sign; - gboolean show_all_devices; + gboolean show_all; gboolean disable_ssl_strict; /* only valid in update and downgrade */ FuUtilOperation current_operation; @@ -505,7 +507,7 @@ fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, Fw FwupdDevice *dev_tmp = g_ptr_array_index (devs, i); if (!fu_util_filter_device (priv, dev_tmp)) continue; - if (!priv->show_all_devices && + if (!priv->show_all && !fu_util_is_interesting_device (dev_tmp)) continue; if (fwupd_device_get_parent (dev_tmp) == dev) { @@ -552,6 +554,31 @@ fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +static gboolean +fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) plugins = NULL; + + /* get results from daemon */ + plugins = fwupd_client_get_plugins (priv->client, NULL, error); + if (plugins == NULL) + return FALSE; + + /* print */ + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index (plugins, i); + g_autofree gchar *str = fu_util_plugin_to_string (FWUPD_PLUGIN (plugin), 0); + g_print ("%s\n", str); + } + if (plugins->len == 0) { + /* TRANSLATORS: nothing found */ + g_print ("%s\n", _("No plugins found")); + } + + /* success */ + return TRUE; +} + static gchar * fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error) { @@ -655,7 +682,7 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) } /* implied, important for get-details on a device not in your system */ - priv->show_all_devices = TRUE; + priv->show_all = TRUE; array = fwupd_client_get_details (priv->client, values[0], NULL, error); if (array == NULL) @@ -1368,15 +1395,32 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) gboolean supported = FALSE; g_autoptr(GNode) root = g_node_new (NULL); g_autofree gchar *title = fu_util_get_tree_title (priv); + gboolean no_updates_header = FALSE; + gboolean latest_header = FALSE; /* are the remotes very old */ if (!fu_util_perhaps_refresh_remotes (priv, error)) return FALSE; - /* get devices from daemon */ - devices = fwupd_client_get_devices (priv->client, NULL, error); - if (devices == NULL) + /* handle both forms */ + if (g_strv_length (values) == 0) { + devices = fwupd_client_get_devices (priv->client, NULL, error); + if (devices == NULL) + return FALSE; + } else if (g_strv_length (values) == 1) { + FwupdDevice *device = fu_util_get_device_by_id (priv, values[0], error); + if (device == NULL) + return FALSE; + devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + g_ptr_array_add (devices, device); + } else { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); return FALSE; + } + g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb); for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); g_autoptr(GPtrArray) rels = NULL; @@ -1387,11 +1431,12 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE)) continue; if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) { - /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS - * %1 is the device name */ - g_autofree gchar *tmp = g_strdup_printf (_("• %s has no available firmware updates"), - fwupd_device_get_name (dev)); - g_printerr ("%s\n", tmp); + if (!no_updates_header) { + /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */ + g_printerr ("%s\n", _("Devices with no available firmware updates: ")); + no_updates_header = TRUE; + } + g_printerr (" • %s\n", fwupd_device_get_name (dev)); continue; } if (!fu_util_filter_device (priv, dev)) @@ -1403,11 +1448,12 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) fwupd_device_get_id (dev), NULL, &error_local); if (rels == NULL) { - /* TRANSLATORS: message letting the user know no device upgrade available - * %1 is the device name */ - g_autofree gchar *tmp = g_strdup_printf (_("• %s has the latest available firmware version"), - fwupd_device_get_name (dev)); - g_printerr ("%s\n", tmp); + if (!latest_header) { + /* TRANSLATORS: message letting the user know no device upgrade available */ + g_printerr ("%s\n", _("Devices with the latest available firmware version:")); + latest_header = TRUE; + } + g_printerr (" • %s\n", fwupd_device_get_name (dev)); /* discard the actual reason from user, but leave for debugging */ g_debug ("%s", error_local->message); continue; @@ -1421,9 +1467,6 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) } } - if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1) - fu_util_print_tree (root, title); - /* nag? */ if (!fu_util_perhaps_show_unreported (priv, error)) return FALSE; @@ -1431,11 +1474,21 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) /* no devices supported by LVFS or all are filtered */ if (!supported) { g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "No updatable devices"); + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No updatable devices"); return FALSE; } + /* no updates available */ + if (g_node_n_nodes (root, G_TRAVERSE_ALL) <= 1) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No updates available for remaining devices"); + return FALSE; + } + + fu_util_print_tree (root, title); /* success */ return TRUE; @@ -1515,6 +1568,8 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error) { g_autoptr(GPtrArray) devices = NULL; gboolean supported = FALSE; + gboolean no_updates_header = FALSE; + gboolean latest_header = FALSE; /* get devices from daemon */ devices = fwupd_client_get_devices (priv->client, NULL, error); @@ -1523,6 +1578,7 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error) priv->current_operation = FU_UTIL_OPERATION_UPDATE; g_signal_connect (priv->client, "device-changed", G_CALLBACK (fu_util_update_device_changed_cb), priv); + g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb); for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); FwupdRelease *rel; @@ -1535,11 +1591,12 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error) if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE)) continue; if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) { - /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS - * %1 is the device name */ - g_autofree gchar *tmp = g_strdup_printf (_("• %s has no available firmware updates"), - fwupd_device_get_name (dev)); - g_printerr ("%s\n", tmp); + if (!no_updates_header) { + /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */ + g_printerr ("%s\n", _("Devices with no available firmware updates: ")); + no_updates_header = TRUE; + } + g_printerr (" • %s\n", fwupd_device_get_name (dev)); continue; } if (!fu_util_filter_device (priv, dev)) @@ -1551,11 +1608,12 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error) fwupd_device_get_id (dev), NULL, &error_local); if (rels == NULL) { - /* TRANSLATORS: message letting the user know no device upgrade available - * %1 is the device name */ - g_autofree gchar *tmp = g_strdup_printf (_("• %s has the latest available firmware version"), - fwupd_device_get_name (dev)); - g_printerr ("%s\n", tmp); + if (!latest_header) { + /* TRANSLATORS: message letting the user know no device upgrade available */ + g_printerr ("%s\n", _("Devices with the latest available firmware version:")); + latest_header = TRUE; + } + g_printerr (" • %s\n", fwupd_device_get_name (dev)); /* discard the actual reason from user, but leave for debugging */ g_debug ("%s", error_local->message); continue; @@ -1842,6 +1900,122 @@ fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error) return fu_util_prompt_complete (priv->completion_flags, TRUE, error); } +static gboolean +fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error) +{ + const gchar *remote_id; + const gchar *branch; + g_autoptr(FwupdRelease) rel = NULL; + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free); + g_autoptr(FwupdDevice) dev = NULL; + + /* find the device and check it has multiple branches */ + priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES; + dev = fu_util_get_device_or_prompt (priv, values, error); + if (dev == NULL) + return FALSE; + + /* get all releases, including the alternate branch versions */ + rels = fwupd_client_get_releases (priv->client, fwupd_device_get_id (dev), + NULL, error); + if (rels == NULL) + return FALSE; + + /* get all the unique branches */ + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel_tmp = g_ptr_array_index (rels, i); + const gchar *branch_tmp = fu_util_release_get_branch (rel_tmp); + if (g_ptr_array_find_with_equal_func (branches, branch_tmp, + g_str_equal, NULL)) + continue; + g_ptr_array_add (branches, g_strdup (branch_tmp)); + } + + /* branch name is optional */ + if (g_strv_length (values) > 1) { + branch = values[1]; + } else if (branches->len == 1) { + branch = g_ptr_array_index (branches, 0); + } else { + guint idx; + + /* TRANSLATORS: get interactive prompt, where branch is the + * supplier of the firmware, e.g. "non-free" or "free" */ + g_print ("%s\n", _("Choose a branch:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print ("0.\t%s\n", _("Cancel")); + for (guint i = 0; i < branches->len; i++) { + const gchar *branch_tmp = g_ptr_array_index (branches, i); + g_print ("%u.\t%s\n", i + 1, branch_tmp); + } + idx = fu_util_prompt_for_number (branches->len); + if (idx == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return FALSE; + } + branch = g_ptr_array_index (branches, idx - 1); + } + + /* sanity check */ + if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s is already on branch %s", + fu_device_get_name (dev), + branch); + return FALSE; + } + + /* the releases are ordered by version */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel_tmp = g_ptr_array_index (rels, j); + if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) { + rel = g_object_ref (rel_tmp); + break; + } + } + if (rel == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No releases for branch %s", + branch); + return FALSE; + } + + /* we're switching branch */ + if (!fu_util_switch_branch_warning (dev, rel, priv->assume_yes, error)) + return FALSE; + + /* update the console if composite devices are also updated */ + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect (priv->client, "device-changed", + G_CALLBACK (fu_util_update_device_changed_cb), priv); + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (!fu_util_update_device_with_release (priv, dev, rel, error)) + return FALSE; + fu_util_display_current_message (priv); + + /* send report if we're supposed to */ + remote_id = fwupd_release_get_remote_id (rel); + if (!fu_util_maybe_send_reports (priv, remote_id, error)) + return FALSE; + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug ("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete (priv->completion_flags, TRUE, error); +} + static gboolean fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -1862,10 +2036,7 @@ fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error) } } } else if (g_strv_length (values) == 1) { - FwupdDevice *device = fwupd_client_get_device_by_id (priv->client, - values[0], - NULL, - error); + FwupdDevice *device = fu_util_get_device_by_id (priv, values[0], error); if (device == NULL) return FALSE; devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); @@ -1890,6 +2061,8 @@ fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error) } /* activate anything with _NEEDS_ACTIVATION */ + /* order by device priority */ + g_ptr_array_sort (devices, fu_util_device_order_sort_cb); for (guint i = 0; i < devices->len; i++) { FwupdDevice *device = g_ptr_array_index (devices, i); if (!fu_util_filter_device (priv, device)) @@ -2000,6 +2173,231 @@ fu_util_modify_config (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +static FwupdRemote * +fu_util_get_remote_with_security_report_uri (FuUtilPrivate *priv, GError **error) +{ + g_autoptr(GPtrArray) remotes = NULL; + + /* get all remotes */ + remotes = fwupd_client_get_remotes (priv->client, NULL, error); + if (remotes == NULL) + return NULL; + + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index (remotes, i); + if (!fwupd_remote_get_enabled (remote)) + continue; + if (fwupd_remote_get_security_report_uri (remote) != NULL) + return g_object_ref (remote); + } + + /* failed */ + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No remotes specified SecurityReportURI"); + return NULL; +} + +static gboolean +fu_util_upload_security (FuUtilPrivate *priv, GPtrArray *attrs, GError **error) +{ + GHashTableIter iter; + const gchar *key; + const gchar *value; + g_autofree gchar *data = NULL; + g_autofree gchar *sig = NULL; + g_autoptr(FwupdRemote) remote = NULL; + g_autoptr(GBytes) upload_response = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GHashTable) metadata = NULL; + g_autoptr(JsonBuilder) builder = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonNode) json_root = NULL; + + /* can we find a remote with a security attr */ + remote = fu_util_get_remote_with_security_report_uri (priv, &error_local); + if (remote == NULL) { + g_debug ("failed to find suitable remote: %s", error_local->message); + return TRUE; + } + if (!priv->assume_yes && + !fwupd_remote_get_automatic_security_reports (remote)) { + g_autofree gchar *tmp = NULL; + /* TRANSLATORS: ask the user to share, %s is something like: + * "Linux Vendor Firmware Service" */ + tmp = g_strdup_printf ("Upload these anonymous results to the %s to help other users?", + fwupd_remote_get_title (remote)); + + g_print ("\n%s [y|N]: ", tmp); + if (!fu_util_prompt_for_boolean (FALSE)) { + g_print ("%s [Y|n]: ", + /* TRANSLATORS: stop nagging the user */ + _("Ask again next time?")); + if (!fu_util_prompt_for_boolean (TRUE)) { + if (!fwupd_client_modify_remote (priv->client, + fwupd_remote_get_id (remote), + "SecurityReportURI", "", + NULL, error)) + return FALSE; + } + return TRUE; + } + } + + /* get metadata */ + metadata = fwupd_client_get_report_metadata (priv->client, + priv->cancellable, + error); + if (metadata == NULL) + return FALSE; + + /* create header */ + builder = json_builder_new (); + json_builder_begin_object (builder); + json_builder_set_member_name (builder, "ReportVersion"); + json_builder_add_int_value (builder, 2); + json_builder_set_member_name (builder, "MachineId"); + json_builder_add_string_value (builder, fwupd_client_get_host_machine_id (priv->client)); + + /* this is system metadata not stored in the database */ + json_builder_set_member_name (builder, "Metadata"); + json_builder_begin_object (builder); + + g_hash_table_iter_init (&iter, metadata); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) { + json_builder_set_member_name (builder, key); + json_builder_add_string_value (builder, value); + } + json_builder_set_member_name (builder, "HostSecurityId"); + json_builder_add_string_value (builder, fwupd_client_get_host_security_id (priv->client)); + json_builder_end_object (builder); + + /* attrs */ + json_builder_set_member_name (builder, "SecurityAttributes"); + json_builder_begin_array (builder); + for (guint i = 0; i < attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i); + json_builder_begin_object (builder); + fwupd_security_attr_to_json (attr, builder); + json_builder_end_object (builder); + } + json_builder_end_array (builder); + json_builder_end_object (builder); + + /* export as a string */ + json_root = json_builder_get_root (builder); + json_generator = json_generator_new (); + json_generator_set_pretty (json_generator, TRUE); + json_generator_set_root (json_generator, json_root); + data = json_generator_to_data (json_generator, NULL); + if (data == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to convert to JSON string"); + return FALSE; + } + + /* self sign data */ + if (priv->sign) { + sig = fwupd_client_self_sign (priv->client, data, + FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP, + priv->cancellable, error); + if (sig == NULL) + return FALSE; + } + + /* ask for permission */ + if (!priv->assume_yes && + !fwupd_remote_get_automatic_security_reports (remote)) { + fu_util_print_data (_("Target"), fwupd_remote_get_security_report_uri (remote)); + fu_util_print_data (_("Payload"), data); + if (sig != NULL) + fu_util_print_data (_("Signature"), sig); + g_print ("%s [Y|n]: ", _("Proceed with upload?")); + if (!fu_util_prompt_for_boolean (TRUE)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED, + "User declined action"); + return FALSE; + } + } + + /* POST request */ + upload_response = fwupd_client_upload_bytes (priv->client, + fwupd_remote_get_security_report_uri (remote), + data, sig, + FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART, + priv->cancellable, error); + if (upload_response == NULL) + return FALSE; + + /* TRANSLATORS: success, so say thank you to the user */ + g_print ("%s\n", "Host Security ID attributes uploaded successfully, thanks!"); + + /* as this worked, ask if the user want to do this every time */ + if (!fwupd_remote_get_automatic_security_reports (remote)) { + g_print ("%s [y|N]: ", + /* TRANSLATORS: can we JFDI? */ + _("Automatically upload every time?")); + if (fu_util_prompt_for_boolean (FALSE)) { + if (!fwupd_client_modify_remote (priv->client, + fwupd_remote_get_id (remote), + "AutomaticSecurityReports", "true", + NULL, error)) + return FALSE; + } + } + + return TRUE; +} + +static gboolean +fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error) +{ + FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE; + g_autoptr(GPtrArray) attrs = NULL; + g_autofree gchar *str = NULL; + + /* not ready yet */ + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "The HSI specification is not yet complete. " + "To ignore this warning, use --force"); + return FALSE; + } + + /* TRANSLATORS: this is a string like 'HSI:2-U' */ + g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"), + fwupd_client_get_host_security_id (priv->client)); + + /* print the "why" */ + attrs = fwupd_client_get_host_security_attrs (priv->client, + priv->cancellable, + error); + if (attrs == NULL) + return FALSE; + + /* show or hide different elements */ + if (priv->show_all) { + flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES; + flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS; + } + str = fu_util_security_attrs_to_string (attrs, flags); + g_print ("%s\n", str); + + /* opted-out */ + if (priv->no_unreported_check) + return TRUE; + + /* upload, with confirmation */ + return fu_util_upload_security (priv, attrs, error); +} + static void fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) @@ -2062,6 +2460,7 @@ fu_util_check_daemon_version (FuUtilPrivate *priv, GError **error) static gboolean fu_util_check_polkit_actions (GError **error) { +#ifdef HAVE_POLKIT g_autofree gchar *directory = fu_common_get_path (FU_PATH_KIND_POLKIT_ACTIONS); g_autofree gchar *filename = g_build_filename (directory, "org.freedesktop.fwupd.policy", @@ -2073,18 +2472,11 @@ fu_util_check_polkit_actions (GError **error) "PolicyKit files are missing, see https://github.com/fwupd/fwupd/wiki/PolicyKit-files-are-missing"); return FALSE; } +#endif return TRUE; } -static void -fu_util_display_help (FuUtilPrivate *priv) -{ - g_autofree gchar *tmp = NULL; - tmp = g_option_context_get_help (priv->context, TRUE, NULL); - g_printerr ("%s\n", tmp); -} - #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free) @@ -2246,12 +2638,63 @@ fu_util_get_blocked_firmware (FuUtilPrivate *priv, gchar **values, GError **erro return TRUE; } +static void +fu_util_show_plugin_warnings (FuUtilPrivate *priv) +{ + FwupdPluginFlags flags = FWUPD_PLUGIN_FLAG_NONE; + g_autoptr(GPtrArray) plugins = NULL; + + /* get plugins from daemon, ignoring if the daemon is too old */ + plugins = fwupd_client_get_plugins (priv->client, NULL, NULL); + if (plugins == NULL) + return; + + /* get a superset so we do not show the same message more than once */ + for (guint i = 0; i < plugins->len; i++) { + FwupdPlugin *plugin = g_ptr_array_index (plugins, i); + if (!fwupd_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING)) + continue; + flags |= fwupd_plugin_get_flags (plugin); + } + + /* never show these, they're way too generic */ + flags &= ~FWUPD_PLUGIN_FLAG_DISABLED; + flags &= ~FWUPD_PLUGIN_FLAG_NO_HARDWARE; + + /* print */ + for (guint i = 0; i < 64; i++) { + FwupdPluginFlags flag = (guint64) 1 << i; + const gchar *tmp; + g_autofree gchar *fmt = NULL; + g_autofree gchar *url= NULL; + g_autoptr(GString) str = g_string_new (NULL); + if ((flags & flag) == 0) + continue; + tmp = fu_util_plugin_flag_to_string (flag); + if (tmp == NULL) + continue; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED); + g_string_append_printf (str, "%s %s\n", fmt, tmp); + + url = g_strdup_printf ("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s", + fwupd_plugin_flag_to_string (flag)); + g_string_append (str, " "); + /* TRANSLATORS: %s is a link to a website */ + g_string_append_printf (str, _("See %s for more information."), url); + g_string_append (str, "\n"); + g_printerr ("%s", str->str); + } +} + int main (int argc, char *argv[]) { gboolean force = FALSE; + gboolean allow_branch_switch = FALSE; gboolean allow_older = FALSE; gboolean allow_reinstall = FALSE; + gboolean ignore_power = FALSE; gboolean is_interactive = TRUE; gboolean no_history = FALSE; gboolean offline = FALSE; @@ -2260,6 +2703,7 @@ main (int argc, char *argv[]) gboolean version = FALSE; g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1); g_autoptr(GError) error = NULL; + g_autoptr(GError) error_polkit = NULL; g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new (); g_autofree gchar *cmd_descriptions = NULL; g_autofree gchar *filter = NULL; @@ -2279,9 +2723,12 @@ main (int argc, char *argv[]) { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older, /* TRANSLATORS: command line option */ _("Allow downgrading firmware versions"), NULL }, + { "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch, + /* TRANSLATORS: command line option */ + _("Allow switching firmware branch"), NULL }, { "force", '\0', 0, G_OPTION_ARG_NONE, &force, /* TRANSLATORS: command line option */ - _("Override warnings and force the action"), NULL }, + _("Force the action by relaxing some runtime checks"), NULL }, { "assume-yes", 'y', 0, G_OPTION_ARG_NONE, &priv->assume_yes, /* TRANSLATORS: command line option */ _("Answer yes to all questions"), NULL }, @@ -2303,7 +2750,10 @@ main (int argc, char *argv[]) { "no-history", '\0', 0, G_OPTION_ARG_NONE, &no_history, /* TRANSLATORS: command line option */ _("Do not write to the history database"), NULL }, - { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices, + { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all, + /* TRANSLATORS: command line option */ + _("Show all results"), NULL }, + { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all, /* TRANSLATORS: command line option */ _("Show devices that are not updatable"), NULL }, { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict, @@ -2313,6 +2763,9 @@ main (int argc, char *argv[]) /* TRANSLATORS: command line option */ _("Filter with a set of device flags using a ~ prefix to " "exclude, e.g. 'internal,~needs-reboot'"), NULL }, + { "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power, + /* TRANSLATORS: command line option */ + _("Ignore requirement of external power source"), NULL }, { NULL} }; @@ -2368,7 +2821,7 @@ main (int argc, char *argv[]) fu_util_get_details); fu_util_cmd_array_add (cmd_array, "get-updates,get-upgrades", - NULL, + "[DEVICE-ID|GUID]", /* TRANSLATORS: command description */ _("Gets the list of updates for connected hardware"), fu_util_get_updates); @@ -2466,44 +2919,62 @@ main (int argc, char *argv[]) "get-approved-firmware", NULL, /* TRANSLATORS: firmware approved by the admin */ - _("Gets the list of approved firmware."), + _("Gets the list of approved firmware"), fu_util_get_approved_firmware); fu_util_cmd_array_add (cmd_array, "set-approved-firmware", "CHECKSUM1[,CHECKSUM2][,CHECKSUM3]", /* TRANSLATORS: firmware approved by the admin */ - _("Sets the list of approved firmware."), + _("Sets the list of approved firmware"), fu_util_set_approved_firmware); fu_util_cmd_array_add (cmd_array, "modify-config", "KEY,VALUE", /* TRANSLATORS: sets something in daemon.conf */ - _("Modifies a daemon configuration value."), + _("Modifies a daemon configuration value"), fu_util_modify_config); fu_util_cmd_array_add (cmd_array, "reinstall", "[DEVICE-ID|GUID]", /* TRANSLATORS: command description */ - _("Reinstall current firmware on the device."), + _("Reinstall current firmware on the device"), fu_util_reinstall); + fu_util_cmd_array_add (cmd_array, + "switch-branch", + "[DEVICE-ID|GUID] [BRANCH]", + /* TRANSLATORS: command description */ + _("Switch the firmware branch on the device"), + fu_util_switch_branch); + fu_util_cmd_array_add (cmd_array, + "security", + NULL, + /* TRANSLATORS: command description */ + _("Gets the host security attributes"), + fu_util_security); fu_util_cmd_array_add (cmd_array, "block-firmware", "[CHECKSUM]", /* TRANSLATORS: command description */ - _("Blocks a specific firmware from being installed."), + _("Blocks a specific firmware from being installed"), fu_util_block_firmware); fu_util_cmd_array_add (cmd_array, "unblock-firmware", "[CHECKSUM]", /* TRANSLATORS: command description */ - _("Blocks a specific firmware from being installed."), + _("Unblocks a specific firmware from being installed"), fu_util_unblock_firmware); fu_util_cmd_array_add (cmd_array, "get-blocked-firmware", NULL, /* TRANSLATORS: command description */ - _("Gets the list of blocked firmware."), + _("Gets the list of blocked firmware"), fu_util_get_blocked_firmware); + fu_util_cmd_array_add (cmd_array, + "get-plugins", + NULL, + /* TRANSLATORS: command description */ + _("Get all enabled plugins registered with the system"), + fu_util_get_plugins); /* do stuff on ctrl+c */ priv->cancellable = g_cancellable_new (); @@ -2538,10 +3009,13 @@ main (int argc, char *argv[]) /* allow disabling SSL strict mode for broken corporate proxies */ if (priv->disable_ssl_strict) { + g_autofree gchar *fmt = NULL; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED); /* TRANSLATORS: try to help */ - g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, " - "to do this automatically in the future " - "export DISABLE_SSL_STRICT in your environment")); + g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, " + "to do this automatically in the future " + "export DISABLE_SSL_STRICT in your environment")); g_setenv ("DISABLE_SSL_STRICT", "1", TRUE); } @@ -2584,10 +3058,26 @@ main (int argc, char *argv[]) priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; if (allow_older) priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; - if (force) + if (allow_branch_switch) + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (force) { priv->flags |= FWUPD_INSTALL_FLAG_FORCE; + priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER; + } if (no_history) priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY; + if (ignore_power) + priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER; + +#ifdef HAVE_POLKIT + /* start polkit tty agent to listen for password requests */ + if (is_interactive) { + if (!fu_polkit_agent_open (&error_polkit)) { + g_printerr ("Failed to open polkit agent: %s\n", + error_polkit->message); + } + } +#endif /* connect to the daemon */ priv->client = fwupd_client_new (); @@ -2617,10 +3107,19 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } if (fwupd_client_get_tainted (priv->client)) { - g_printerr ("WARNING: The daemon has loaded 3rd party code and " - "is no longer supported by the upstream developers!\n"); + g_autofree gchar *fmt = NULL; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED); + g_printerr ("%s %s\n", + fmt, + /* TRANSLATORS: the user is SOL for support... */ + _("The daemon has loaded 3rd party code and " + "is no longer supported by the upstream developers!")); } + /* show user-visible warnings from the plugins */ + fu_util_show_plugin_warnings (priv); + /* we know the runtime daemon version now */ fwupd_client_set_user_agent_for_package (priv->client, "fwupdmgr", PACKAGE_VERSION); @@ -2651,6 +3150,7 @@ main (int argc, char *argv[]) if (is_interactive) { if (!fwupd_client_set_feature_flags (priv->client, FWUPD_FEATURE_FLAG_CAN_REPORT | + FWUPD_FEATURE_FLAG_SWITCH_BRANCH | FWUPD_FEATURE_FLAG_UPDATE_ACTION | FWUPD_FEATURE_FLAG_DETACH_ACTION, priv->cancellable, &error)) { @@ -2663,15 +3163,23 @@ main (int argc, char *argv[]) /* run the specified command */ ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error); if (!ret) { - ret = EXIT_FAILURE; g_printerr ("%s\n", error->message); - if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) - fu_util_display_help (priv); - else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) - ret = EXIT_NOTHING_TO_DO; - } else { - ret = EXIT_SUCCESS; + if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) { + /* TRANSLATORS: error message explaining command to run to how to get help */ + g_printerr ("\n%s\n", _("Use fwupdmgr --help for help")); + } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_debug ("%s\n", error->message); + return EXIT_NOTHING_TO_DO; + } + return EXIT_FAILURE; } - return ret; + +#ifdef HAVE_POLKIT + /* stop listening for polkit questions */ + fu_polkit_agent_close (); +#endif + + /* success */ + return EXIT_SUCCESS; } diff --git a/src/meson.build b/src/meson.build index 030c97093..678694b19 100644 --- a/src/meson.build +++ b/src/meson.build @@ -4,11 +4,17 @@ if build_daemon ) endif +client_src = [] systemd_src = [] +daemon_dep = [] if get_option('systemd') systemd_src += 'fu-systemd.c' endif +if build_daemon and get_option('polkit') + client_src += 'fu-polkit-agent.c' + daemon_dep += polkit +endif if build_daemon fwupdmgr = executable( @@ -17,7 +23,9 @@ fwupdmgr = executable( 'fu-util.c', 'fu-history.c', 'fu-progressbar.c', + 'fu-security-attr.c', 'fu-util-common.c', + client_src, systemd_src ], include_directories : [ @@ -49,6 +57,7 @@ fwupdagent = executable( 'fwupdagent', sources : [ 'fu-agent.c', + 'fu-security-attr.c', 'fu-util-common.c', systemd_src, ], @@ -80,6 +89,7 @@ fwupdoffline = executable( sources : [ 'fu-history.c', 'fu-offline.c', + 'fu-security-attr.c', 'fu-util-common.c', systemd_src ], @@ -133,6 +143,7 @@ fwupdtool = executable( 'fu-plugin-list.c', 'fu-progressbar.c', 'fu-remote-list.c', + 'fu-security-attr.c', 'fu-util-common.c', systemd_src ], @@ -163,22 +174,24 @@ fwupdtool = executable( install_dir : bindir ) -if build_daemon and get_option('man') +if get_option('man') help2man = find_program('help2man') - custom_target('fwupdmgr-man', - input : fwupdmgr, - output : 'fwupdmgr.1', - command : [ - help2man, '@INPUT@', - '--no-info', - '--output', '@OUTPUT@', - '--name', 'Firmware update manager client utility', - '--manual', 'User Commands', - '--version-string', fwupd_version, - ], - install : true, - install_dir : join_paths(mandir, 'man1'), - ) + if build_daemon + custom_target('fwupdmgr-man', + input : fwupdmgr, + output : 'fwupdmgr.1', + command : [ + help2man, '@INPUT@', + '--no-info', + '--output', '@OUTPUT@', + '--name', 'Firmware update manager client utility', + '--manual', 'User Commands', + '--version-string', fwupd_version, + ], + install : true, + install_dir : join_paths(mandir, 'man1'), + ) + endif if get_option('agent') custom_target('fwupdagent-man', input : fwupdagent, @@ -195,22 +208,22 @@ if build_daemon and get_option('man') install_dir : join_paths(mandir, 'man1'), ) endif -endif -if get_option('man') - custom_target('fwupdtool-man', - input : fwupdtool, - output : 'fwupdtool.1', - command : [ - help2man, '@INPUT@', - '--no-info', - '--output', '@OUTPUT@', - '--name', 'Standalone firmware update utility', - '--manual', 'User Commands', - '--version-string', fwupd_version, - ], - install : true, - install_dir : join_paths(mandir, 'man1'), - ) + if build_standalone + custom_target('fwupdtool-man', + input : fwupdtool, + output : 'fwupdtool.1', + command : [ + help2man, '@INPUT@', + '--no-info', + '--output', '@OUTPUT@', + '--name', 'Standalone firmware update utility', + '--manual', 'User Commands', + '--version-string', fwupd_version, + ], + install : true, + install_dir : join_paths(mandir, 'man1'), + ) + endif endif if build_daemon @@ -232,6 +245,7 @@ executable( 'fu-main.c', 'fu-plugin-list.c', 'fu-remote-list.c', + 'fu-security-attr.c', systemd_src ], include_directories : [ @@ -247,12 +261,12 @@ executable( gmodule, gudev, gusb, - polkit, soup, sqlite, valgrind, libarchive, libjsonglib, + daemon_dep ], link_with : [ fwupd, @@ -289,6 +303,7 @@ if get_option('tests') 'fu-plugin-list.c', 'fu-progressbar.c', 'fu-remote-list.c', + 'fu-security-attr.c', 'fu-self-test.c', systemd_src ], @@ -337,6 +352,7 @@ if get_option('tests') fwupdplugin_incdir, ], dependencies : [ + libxmlb, gio, ], link_with : [ diff --git a/src/org.freedesktop.fwupd.xml b/src/org.freedesktop.fwupd.xml index 68a96f7f0..77b708b4c 100644 --- a/src/org.freedesktop.fwupd.xml +++ b/src/org.freedesktop.fwupd.xml @@ -44,6 +44,17 @@ + + + + + + The Host Security ID, for instance HSI:2UA + + + + + @@ -106,6 +117,24 @@ + + + + + + Gets a list of all the plugins being used by the daemon. + + + + + + + An array of plugins, with any properties set on each. + + + + + @@ -242,6 +271,42 @@ + + + + + + Gets a list of all the Host Security ID attributes. + + + + + + + An array of HSI attributes, with any properties set on each. + + + + + + + + + + + Gets metadata to include with the firmware and security reports. + + + + + + + An array of string key values. + + + + + @@ -569,7 +634,7 @@ - The key, e.g. 'BlacklistPlugins'. + The key, e.g. 'DisabledPlugins'. diff --git a/subprojects/gusb.wrap b/subprojects/gusb.wrap index 9d331e370..539253671 100644 --- a/subprojects/gusb.wrap +++ b/subprojects/gusb.wrap @@ -1,4 +1,4 @@ [wrap-git] directory = gusb url = https://github.com/hughsie/libgusb.git -revision = 43b327a1e213aff00833842c455a796a068077cd +revision = 0.3.5