From 9c89e3ada2dd30d683c44364e8eea5de757b74eb Mon Sep 17 00:00:00 2001 From: Sean Rhodes Date: Fri, 12 Nov 2021 08:54:50 +0000 Subject: util: Add coreboot-configurator A simple GUI to change settings in coreboot's CBFS, via the nvramtool utility. Test on the StarBook Mk IV running coreboot 4.15 with: * Ubuntu 20.04 * Ubuntu 21.10 * MX Linux 21 * elementary OS 6 * Manjaro 21 Signed-off-by: Sean Rhodes Change-Id: I491922bf55ed87c2339897099634a38f8d055876 Reviewed-on: https://review.coreboot.org/c/coreboot/+/59256 Tested-by: build bot (Jenkins) Reviewed-by: Martin Roth --- util/coreboot-configurator/src/README.md | 31 + .../src/application/AboutDialog.cpp | 21 + .../src/application/AboutDialog.h | 21 + .../src/application/AboutDialog.ui | 141 ++++ .../src/application/Configuration.cpp | 41 ++ .../src/application/Configuration.h | 18 + .../src/application/MainWindow.cpp | 388 +++++++++++ .../src/application/MainWindow.h | 54 ++ .../src/application/MainWindow.ui | 118 ++++ .../src/application/NvramToolCli.cpp | 120 ++++ .../src/application/NvramToolCli.h | 21 + .../src/application/ToggleSwitch.cpp | 38 ++ .../src/application/ToggleSwitch.h | 39 ++ .../src/application/ToggleSwitch.svg.h | 78 +++ util/coreboot-configurator/src/application/Util.h | 26 + .../coreboot-configurator/src/application/lang.qrc | 3 + .../coreboot-configurator/src/application/main.cpp | 20 + .../src/application/meson.build | 35 + .../src/application/qrc/categories.yaml | 119 ++++ .../src/application/qrc/star.svg | 391 +++++++++++ .../src/application/qrc/toggle-off.svg | 4 + .../src/application/qrc/toggle-on.svg | 65 ++ .../src/application/resources.qrc | 12 + util/coreboot-configurator/src/meson.build | 4 + .../src/resources/coreboot-configurator.desktop | 9 + .../src/resources/coreboot_configurator.svg | 748 +++++++++++++++++++++ .../src/resources/meson.build | 43 ++ .../src/resources/org.coreboot.nvramtool.policy | 13 + .../src/resources/org.coreboot.reboot.policy | 12 + 29 files changed, 2633 insertions(+) create mode 100644 util/coreboot-configurator/src/README.md create mode 100644 util/coreboot-configurator/src/application/AboutDialog.cpp create mode 100644 util/coreboot-configurator/src/application/AboutDialog.h create mode 100644 util/coreboot-configurator/src/application/AboutDialog.ui create mode 100644 util/coreboot-configurator/src/application/Configuration.cpp create mode 100644 util/coreboot-configurator/src/application/Configuration.h create mode 100644 util/coreboot-configurator/src/application/MainWindow.cpp create mode 100644 util/coreboot-configurator/src/application/MainWindow.h create mode 100644 util/coreboot-configurator/src/application/MainWindow.ui create mode 100644 util/coreboot-configurator/src/application/NvramToolCli.cpp create mode 100644 util/coreboot-configurator/src/application/NvramToolCli.h create mode 100644 util/coreboot-configurator/src/application/ToggleSwitch.cpp create mode 100644 util/coreboot-configurator/src/application/ToggleSwitch.h create mode 100644 util/coreboot-configurator/src/application/ToggleSwitch.svg.h create mode 100644 util/coreboot-configurator/src/application/Util.h create mode 100644 util/coreboot-configurator/src/application/lang.qrc create mode 100644 util/coreboot-configurator/src/application/main.cpp create mode 100644 util/coreboot-configurator/src/application/meson.build create mode 100644 util/coreboot-configurator/src/application/qrc/categories.yaml create mode 100644 util/coreboot-configurator/src/application/qrc/star.svg create mode 100644 util/coreboot-configurator/src/application/qrc/toggle-off.svg create mode 100644 util/coreboot-configurator/src/application/qrc/toggle-on.svg create mode 100644 util/coreboot-configurator/src/application/resources.qrc create mode 100644 util/coreboot-configurator/src/meson.build create mode 100644 util/coreboot-configurator/src/resources/coreboot-configurator.desktop create mode 100644 util/coreboot-configurator/src/resources/coreboot_configurator.svg create mode 100644 util/coreboot-configurator/src/resources/meson.build create mode 100644 util/coreboot-configurator/src/resources/org.coreboot.nvramtool.policy create mode 100644 util/coreboot-configurator/src/resources/org.coreboot.reboot.policy (limited to 'util/coreboot-configurator/src') diff --git a/util/coreboot-configurator/src/README.md b/util/coreboot-configurator/src/README.md new file mode 100644 index 0000000000..e3386242a2 --- /dev/null +++ b/util/coreboot-configurator/src/README.md @@ -0,0 +1,31 @@ +# Categories ![alt text](images/StarLabs_Logo.png "Star Labs Systems") + +CMOS values should be added to [categories.yaml](src/application/categories.yaml]. + +This allows `coreboot-configurator` to display them in a relavant tab, with a nice +name and help text. Without this, they will still be visible in the **Raw** tab. + +An example entry is below: +``` +processor: + displayName: Processor + me_state: + displayName: Intel Management Engine + type: bool + help: Enable or disable the Intel Management Engine +``` + +To explain the options: +``` +**tabgroup**: <- This is the reference to the tab group + displayName: **Hello World** <- This is the name of the group that the user + will see + **setting_1**: <- This is the value that should match the CMOS + option. + displayName: **Hi World** <- This is the name of the option that the user + will see. + type: **bool** <- Valid type are: bool (checkbox) and enum + <- (dropdown). + help: **Greet the World** <- Help text that is displayed when hovering on the + option. +``` diff --git a/util/coreboot-configurator/src/application/AboutDialog.cpp b/util/coreboot-configurator/src/application/AboutDialog.cpp new file mode 100644 index 0000000000..8282e0c063 --- /dev/null +++ b/util/coreboot-configurator/src/application/AboutDialog.cpp @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include "AboutDialog.h" +#include "NvramToolCli.h" +#include "ui_AboutDialog.h" + +AboutDialog::AboutDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutDialog) +{ + ui->setupUi(this); + + ui->logoLabel->setPixmap(QPixmap(":/images/star.svg")); + + ui->versionLabel->setText(""+NvramToolCli::version()+""); +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} diff --git a/util/coreboot-configurator/src/application/AboutDialog.h b/util/coreboot-configurator/src/application/AboutDialog.h new file mode 100644 index 0000000000..7a3123335d --- /dev/null +++ b/util/coreboot-configurator/src/application/AboutDialog.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#pragma once + +#include + +namespace Ui { +class AboutDialog; +} + +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = nullptr); + ~AboutDialog(); + +private: + Ui::AboutDialog *ui; +}; diff --git a/util/coreboot-configurator/src/application/AboutDialog.ui b/util/coreboot-configurator/src/application/AboutDialog.ui new file mode 100644 index 0000000000..009acc24b9 --- /dev/null +++ b/util/coreboot-configurator/src/application/AboutDialog.ui @@ -0,0 +1,141 @@ + + + AboutDialog + + + + 0 + 0 + 412 + 273 + + + + About + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">coreboot configurator</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + <html><head/><body><p>A simple GUI to change settings in coreboot's CBFS, via the nvramtool utility.</p></body></html> + + + Qt::AlignCenter + + + true + + + + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + <html><head/><body><p><a href="https://support.starlabs.systems"><span style=" text-decoration: underline; color:#0000ff;">starlabs.systems</span></a></p></body></html> + + + Qt::AlignCenter + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + true + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/util/coreboot-configurator/src/application/Configuration.cpp b/util/coreboot-configurator/src/application/Configuration.cpp new file mode 100644 index 0000000000..9f383f83e2 --- /dev/null +++ b/util/coreboot-configurator/src/application/Configuration.cpp @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include + +#include "Configuration.h" +#include "Util.h" + +QMap Configuration::fromFile(const QString &curr_path) +{ + QFile curr_file(curr_path); + + if ( !curr_file.open(QFile::ReadOnly) + || !curr_file.isReadable() + || curr_file.atEnd()) { + return {}; + } + + auto result = Util::parseParameters(curr_file); + + curr_file.close(); + return result; +} + + +bool Configuration::toFile(const QString &curr_path, const Parameters ¶ms) +{ + QFile output(curr_path); + + if(!output.open(QFile::WriteOnly|QFile::Truncate)){ + return false; + } + QTextStream outStream(&output); + for(auto it = params.begin(); it != params.end(); ++it){ + outStream << it.key() << " = " << it.value() << "\n"; + } + + output.close(); + return true; +} diff --git a/util/coreboot-configurator/src/application/Configuration.h b/util/coreboot-configurator/src/application/Configuration.h new file mode 100644 index 0000000000..b2559d4960 --- /dev/null +++ b/util/coreboot-configurator/src/application/Configuration.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#pragma once + +#include +#include +#include +#include + + +namespace Configuration { + +using Parameters = QMap; + +Parameters fromFile(const QString& curr_path); +bool toFile(const QString& curr_path, const Parameters& params); + +} diff --git a/util/coreboot-configurator/src/application/MainWindow.cpp b/util/coreboot-configurator/src/application/MainWindow.cpp new file mode 100644 index 0000000000..d51937d161 --- /dev/null +++ b/util/coreboot-configurator/src/application/MainWindow.cpp @@ -0,0 +1,388 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include + +#include "AboutDialog.h" +#include "Configuration.h" +#include "MainWindow.h" +#include "NvramToolCli.h" +#include "ToggleSwitch.h" +#include "ui_MainWindow.h" + +static auto s_errorWindowTitle = MainWindow::tr("Error Occured"); +static auto s_nvramErrorMessage = MainWindow::tr("Nvramtool was not able to access cmos settings. Look at documentation for possible causes of errors."); + +QString makeNvramErrorMessage(const QString& error){ + if(!error.trimmed().isEmpty()){ + return QString(MainWindow::tr("%1

Error message:
%2")).arg(s_nvramErrorMessage, + Qt::convertFromPlainText(error)); + } + return s_nvramErrorMessage; +} + +namespace YAML { +template <> +struct convert{ + static Node encode(const QString& rhs) { return Node(rhs.toUtf8().data()); } + + static bool decode(const Node& node, QString& rhs) { + if (!node.IsScalar()) + return false; + rhs = QString::fromStdString(node.Scalar()); + return true; + } +}; +} + +static auto s_metadataErrorMessage = MainWindow::tr("Can't load categories metadata file. Check your installation."); +static constexpr char s_sudoProg[] = "/usr/bin/pkexec"; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(ui->actionAbout, &QAction::triggered, this, [](){ + AboutDialog().exec(); + }); + +#if MOCK + this->setWindowTitle("coreboot configurator "+tr("[MOCKED DATA]")); +#else + this->setWindowTitle("coreboot configurator"); +#endif + this->setWindowIcon(QIcon::fromTheme("coreboot_configurator")); + + QFile catFile(":/config/categories.yaml"); + + if(!catFile.open(QFile::ReadOnly)){ + QMessageBox::critical(this, s_errorWindowTitle, s_metadataErrorMessage); + this->close(); + return; + } + + m_categories = YAML::Load(catFile.readAll()); + + if(m_categories.IsNull() || !m_categories.IsDefined()){ + QMessageBox::critical(this, s_errorWindowTitle, s_metadataErrorMessage); + this->close(); + return; + } + + QShortcut* returnAction = new QShortcut(QKeySequence("Ctrl+Return"), this); + connect(returnAction, &QShortcut::activated, this, &MainWindow::on_saveButton_clicked); + + generateUi(); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::pullSettings() +{ + QString error; + m_parameters = NvramToolCli::readParameters(&error); + + if(m_parameters.isEmpty()){ + QMessageBox::critical(this, s_errorWindowTitle, makeNvramErrorMessage(error)); + + /* we need delayed close as initialization error happened before event loop start so we can't stop application properly */ + QTimer::singleShot(0, this, &MainWindow::close); + } +} + +void MainWindow::pushSettings() +{ + QString error; + if(!NvramToolCli::writeParameters(m_parameters, &error)){ + QMessageBox::critical(this, s_errorWindowTitle, makeNvramErrorMessage(error)); + } +} + + +QComboBox* MainWindow::createComboBox(const QString& key) { + auto box = new QComboBox(this); + + auto opts = NvramToolCli::readOptions(key); + + box->addItems(opts); + box->setCurrentText(m_parameters[key]); + + connect(ui->advancedModeCheckBox, &QCheckBox::clicked, this, [box](bool clicked){ + box->setEditable(clicked); + }); + + connect(this, &MainWindow::updateValue, this, [box, this, key](const QString& name){ + if(key!=name || m_parameters[name]==box->currentText()){ + return; + } + box->setCurrentText(m_parameters[name]); + }); + + connect(box, &QComboBox::currentTextChanged, this, [key, this](const QString& value){ + if(value==m_parameters[key]){ + return; + } + m_parameters[key] = value; + emit updateValue(key); + }); + + return box; +} +QString boolToString(bool value){ + return value?QStringLiteral("Enable"):QStringLiteral("Disable"); +} +bool stringToBool(const QString& str){ + return str==QStringLiteral("Enable"); +} +QCheckBox* MainWindow::createCheckBox(const QString& key) { + auto box = new ToggleSwitch(this); + + box->setChecked(stringToBool(m_parameters[key])); + + connect(this, &MainWindow::updateValue, this, [box, this, key](const QString& name){ + + if(key!=name || m_parameters[name]==boolToString(box->isChecked())){ + return; + } + auto newValue = stringToBool(m_parameters[name]); + + box->setChecked(newValue); + }); + + connect(box, &QCheckBox::clicked, this, [key, this](bool checked){ + auto value = boolToString(checked); + if(value==m_parameters[key]){ + return; + } + m_parameters[key] = value; + emit updateValue(key); + }); + + return box; +} + + +QTableWidget *MainWindow::createRawTable() +{ + /* Create Raw values table */ + auto table = new QTableWidget(m_parameters.size(), 2); + table->setHorizontalHeaderLabels({tr("Key"), tr("Value")}); + table->horizontalHeader()->setSectionResizeMode(0,QHeaderView::Stretch); + table->verticalHeader()->hide(); + table->setSelectionBehavior(QTableWidget::SelectRows); + + connect(table, &QTableWidget::cellChanged, this, [table, this](int row, int column){ + if(column != 1 || row >= table->rowCount() || row < 0 ){ + /* Weird state when changed cell is not a value cell */ + return; + } + auto keyItem = table->item(row, 0); + auto valueItem = table->item(row, 1); + + if(keyItem == nullptr || valueItem == nullptr){ + /* Invalid cells */ + return; + } + + if(valueItem->text()==m_parameters[keyItem->text()]){ + return; + } + + m_parameters[keyItem->text()] = valueItem->text(); + emit updateValue(keyItem->text()); + }); + + auto it = m_parameters.begin(); + for(int i = 0; isetFlags(item->flags() ^ Qt::ItemIsEditable); + table->setItem(i,0,item); + + item = new QTableWidgetItem(it.value()); + connect(this, &MainWindow::updateValue, this, [item, it, this](const QString& name){ + if(it.key()!=name || m_parameters[name]==item->text()){ + return; + } + item->setText(m_parameters[name]); + }); + + table->setItem(i,1,item); + } + return table; +} + +void MainWindow::generateUi() +{ + pullSettings(); + + if(!m_categories.IsMap()){ + return; + } + for(const auto& category : m_categories){ + if(!category.second.IsMap()){ + continue; + } + auto name = category.second["displayName"].as(); + + auto layout = new QVBoxLayout; + + auto tabPage = new QWidget(this); + tabPage->setLayout(layout); + + ui->centralTabWidget->addTab(tabPage, name); + + for(const auto& value : category.second){ + if(!value.second.IsMap() || !m_parameters.contains(value.first.as())){ + continue; + } + auto displayName = value.second["displayName"]; + if(!displayName.IsDefined()){ + continue; + } + auto type = value.second["type"]; + if(!type.IsDefined()){ + continue; + } + + auto controlLayout = new QHBoxLayout(); + + auto help = value.second["help"]; + + if(help.IsDefined()){ + auto labelWithTooltip = new QWidget; + labelWithTooltip->setToolTip(help.as()); + labelWithTooltip->setCursor({Qt::WhatsThisCursor}); + labelWithTooltip->setLayout(new QHBoxLayout); + + auto helpButton = new QLabel(); + helpButton->setPixmap(QIcon::fromTheme("help-hint").pixmap(16,16)); + + { + auto layout = qobject_cast(labelWithTooltip->layout()); + layout->addWidget(new QLabel(displayName.as())); + layout->addWidget(helpButton,1); + } + controlLayout->addWidget(labelWithTooltip, 0); + } else { + controlLayout->addWidget(new QLabel(displayName.as()), 0); + } + + controlLayout->addStretch(1); + + QWidget* res = nullptr; + + if(type.as() == QStringLiteral("bool")){ + res = createCheckBox(value.first.as()); + } else if (type.as() == QStringLiteral("enum")){ + res = createComboBox(value.first.as()); + } else { + controlLayout->deleteLater(); + continue; + } + res->setObjectName(value.first.as()); + + controlLayout->addWidget(res, 0); + + layout->addLayout(controlLayout); + } + } + + auto table = createRawTable(); + + connect(ui->advancedModeCheckBox, &QCheckBox::clicked, this, [table,this](bool clicked){ + if(clicked && ui->centralTabWidget->widget(ui->centralTabWidget->count()-1) != table){ + ui->centralTabWidget->addTab(table, tr("Raw")); + } else if(!clicked && ui->centralTabWidget->widget(ui->centralTabWidget->count()-1) == table) { + ui->centralTabWidget->removeTab(ui->centralTabWidget->count()-1); + } + }); +} + +void MainWindow::askForReboot() +{ + QMessageBox rebootDialog(QMessageBox::Question, + tr("Reboot"), + tr("Changes are saved. Do you want to reboot to apply changes?")); + + auto nowButton = rebootDialog.addButton(tr("Reboot now"), QMessageBox::AcceptRole); + rebootDialog.addButton(tr("Reboot later"), QMessageBox::RejectRole); + + rebootDialog.exec(); + if(rebootDialog.clickedButton()==nowButton){ + QProcess::startDetached(s_sudoProg, {"/usr/bin/systemctl", "reboot"}); + this->close(); + } +} + +void MainWindow::readSettings(const QString &fileName) +{ + if(fileName.isEmpty()){ + return; + } + + auto configValues = Configuration::fromFile(fileName); + + for(auto it = configValues.begin(); it != configValues.end(); ++it){ + if(!m_parameters.contains(it.key())){ + continue; + } + m_parameters[it.key()]=it.value(); + emit updateValue(it.key()); + } +} + +void MainWindow::writeSettings(const QString &fileName) +{ + if(fileName.isEmpty()){ + return; + } + if(!Configuration::toFile(fileName, m_parameters)){ + QMessageBox::critical(this, tr("Error Occured"), tr("Can't open file to write")); + this->close(); + } +} + + +void MainWindow::on_actionSave_triggered() +{ + auto filename = QFileDialog::getSaveFileName(this, + tr("Select File To Save"), + QDir::homePath(), + tr("Coreboot Configuration Files")+"(*.cfg)"); + writeSettings(filename); +} + + +void MainWindow::on_actionLoad_triggered() +{ + auto filename = QFileDialog::getOpenFileName(this, + tr("Select File To Load"), + QDir::homePath(), + tr("Coreboot Configuration Files")+"(*.cfg)"); + + readSettings(filename); +} + + +void MainWindow::on_saveButton_clicked() +{ + ui->centralwidget->setEnabled(false); + ui->menubar->setEnabled(false); + + pushSettings(); + + askForReboot(); + + ui->centralwidget->setEnabled(true); + ui->menubar->setEnabled(true); +} diff --git a/util/coreboot-configurator/src/application/MainWindow.h b/util/coreboot-configurator/src/application/MainWindow.h new file mode 100644 index 0000000000..bf317a814f --- /dev/null +++ b/util/coreboot-configurator/src/application/MainWindow.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +signals: + void updateValue(const QString& key); + +private slots: + void on_actionSave_triggered(void); + + void on_actionLoad_triggered(void); + + void on_saveButton_clicked(void); + +private: + void pullSettings(void); + void pushSettings(void); + + void generateUi(void); + void askForReboot(void); + + void readSettings(const QString& fileName); + void writeSettings(const QString& fileName); + + Configuration::Parameters m_parameters; + YAML::Node m_categories; + + Ui::MainWindow *ui; + + QComboBox *createComboBox(const QString &key); + QCheckBox *createCheckBox(const QString &key); + + QTableWidget *createRawTable(); +}; diff --git a/util/coreboot-configurator/src/application/MainWindow.ui b/util/coreboot-configurator/src/application/MainWindow.ui new file mode 100644 index 0000000000..0f59d80585 --- /dev/null +++ b/util/coreboot-configurator/src/application/MainWindow.ui @@ -0,0 +1,118 @@ + + + MainWindow + + + + 0 + 0 + 600 + 400 + + + + + 0 + 0 + + + + + 600 + 400 + + + + + 600 + 400 + + + + coreboot configurator + + + + + + + + + + + + Advanced mode + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Save + + + + + + + + + + + + + + 0 + 0 + 600 + 25 + + + + + File + + + + + + + Help + + + + + + + + + Save to File... + + + + + Load from File... + + + + + About... + + + + + + diff --git a/util/coreboot-configurator/src/application/NvramToolCli.cpp b/util/coreboot-configurator/src/application/NvramToolCli.cpp new file mode 100644 index 0000000000..da844a043b --- /dev/null +++ b/util/coreboot-configurator/src/application/NvramToolCli.cpp @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include + +#include "NvramToolCli.h" +#include "Util.h" + +static constexpr char s_sudoProg[] = "/usr/bin/pkexec"; +static constexpr char s_nvramToolProg[] = "/usr/sbin/nvramtool"; + +#if MOCK + +QMap NvramToolCli::readParameters(QString *error) { + return QMap({ + {"boot_option","Normal"}, + {"reboot_counter","0x0"}, + {"debug_level","Spew"}, + {"vtd","Enable"}, + {"power_profile","Performance"}, + {"wireless","Enable"}, + {"webcam","Enable"}, + {"microphone","Enable"}, + {"legacy_8254_timer","Enable"}, + {"usb_always_on","Disable"}, + {"kbl_timeout","Never"}, + {"fn_ctrl_swap","Enable"}, + {"max_charge","100%"}, + {"power_on_after_fail","Disable"}, + {"fn_lock_state","0x2"}, + {"trackpad_state","0x40"}, + {"kbl_brightness","0xc4"}, + {"kbl_state","0x22"} + }); +} + +QStringList NvramToolCli::readOptions(const QString ¶meter, QString *error){ + return (parameter=="power_profile")? + QStringList{ + "Power Saver","Balanced","Performance" + } : QStringList{}; +} + +#else + +QMap NvramToolCli::readParameters(QString *error) +{ + QProcess nvramtoolProcess; + nvramtoolProcess.start(s_sudoProg, {s_nvramToolProg, "-a"}); + + nvramtoolProcess.waitForFinished(); + + if(error) *error = nvramtoolProcess.readAllStandardError(); + + if(nvramtoolProcess.exitCode() != 0){ + return {}; + } + + return Util::parseParameters(nvramtoolProcess); +} + +QStringList NvramToolCli::readOptions(const QString ¶meter, QString *error) +{ + QStringList result; + + QProcess nvramtoolProcess; + nvramtoolProcess.start(s_sudoProg, {s_nvramToolProg, "-e", parameter}); + nvramtoolProcess.waitForFinished(); + + if(error) *error = nvramtoolProcess.readAllStandardError(); + + while (nvramtoolProcess.canReadLine()) { + result.append(nvramtoolProcess.readLine().trimmed()); + } + + return result; +} +#endif + +bool NvramToolCli::writeParameters(const QMap ¶meters, QString *error) +{ + +#if MOCK + QTextStream outStream(stdout); +#else + QProcess nvramtoolProcess; + nvramtoolProcess.start(s_sudoProg, {s_nvramToolProg, "-i"}); + nvramtoolProcess.waitForStarted(); + QTextStream outStream(&nvramtoolProcess); +#endif + for(auto it = parameters.begin(); it != parameters.end(); ++it){ + outStream << it.key() << " = " << it.value() << "\n"; + } + + outStream.flush(); +#if MOCK + return true; +#else + nvramtoolProcess.closeWriteChannel(); + nvramtoolProcess.waitForFinished(); + + if(error){ + *error = nvramtoolProcess.readAllStandardError(); + } + + return nvramtoolProcess.exitCode()==0; +#endif +} + + + +QString NvramToolCli::version() +{ + QProcess nvramtoolProcess; + nvramtoolProcess.start(s_nvramToolProg, {"-v"}); + + nvramtoolProcess.waitForFinished(); + + return nvramtoolProcess.readAll(); +} diff --git a/util/coreboot-configurator/src/application/NvramToolCli.h b/util/coreboot-configurator/src/application/NvramToolCli.h new file mode 100644 index 0000000000..3bb5d0a6ea --- /dev/null +++ b/util/coreboot-configurator/src/application/NvramToolCli.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#pragma once + +#include +#include +#include + +#include "Configuration.h" + +/* + * Namespace for convinient functions to work with nvramtool CLI utility + */ +namespace NvramToolCli { + +Configuration::Parameters readParameters(QString* error = nullptr); +QStringList readOptions(const QString& parameter, QString* error = nullptr); +bool writeParameters(const Configuration::Parameters& parameters, QString* error = nullptr); +QString version(); + +} diff --git a/util/coreboot-configurator/src/application/ToggleSwitch.cpp b/util/coreboot-configurator/src/application/ToggleSwitch.cpp new file mode 100644 index 0000000000..b0a399e01c --- /dev/null +++ b/util/coreboot-configurator/src/application/ToggleSwitch.cpp @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include + +#include "ToggleSwitch.h" +#include "ToggleSwitch.svg.h" + +const QByteArray ToggleSwitch::s_toggleOffSvgContent = ToggleSwitchSVG::s_toggledOffContent; +const QByteArray ToggleSwitch::s_toggleOnSvgContent = ToggleSwitchSVG::s_toggledOnContent; +const int ToggleSwitch::s_colorPosInToggleOn = ToggleSwitch::s_toggleOnSvgContent.indexOf("#1a73e8"); + +ToggleSwitch::ToggleSwitch(QWidget *parent) : QCheckBox(parent){ + + setFixedWidth(50); + setFixedHeight(width()/2); + + m_toggleOnSvgContentColored = s_toggleOnSvgContent; +} + +void ToggleSwitch::paintEvent(QPaintEvent *event){ + QPainter p(this); + + if(isChecked()){ + auto accent = palette().highlight().color(); + m_toggleOnSvgContentColored = m_toggleOnSvgContentColored.replace(s_colorPosInToggleOn, 7, accent.name().toLatin1()); + + m_svgr.load(m_toggleOnSvgContentColored); + } else { + m_svgr.load(s_toggleOffSvgContent); + } + + m_svgr.render(&p, this->rect()); + p.end(); +} diff --git a/util/coreboot-configurator/src/application/ToggleSwitch.h b/util/coreboot-configurator/src/application/ToggleSwitch.h new file mode 100644 index 0000000000..191dc5ef96 --- /dev/null +++ b/util/coreboot-configurator/src/application/ToggleSwitch.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#pragma once + +#include +#include +#include +#include + + +/* + * The ToggleSwitch class represents Toggle Switch widget based on QCheckBox and toggles svg with colorscheme support + */ +class ToggleSwitch : public QCheckBox { + Q_OBJECT +public: + explicit ToggleSwitch(QWidget* parent = nullptr); + +private: + QSvgRenderer m_svgr; + + static const QByteArray s_toggleOnSvgContent; + static const QByteArray s_toggleOffSvgContent; + static const int s_colorPosInToggleOn; + + QByteArray m_toggleOnSvgContentColored; + + /* QWidget interface */ +protected: + void paintEvent(QPaintEvent *event) override; + + /* QAbstractButton interface */ +protected: + bool hitButton(const QPoint &pos) const override + { + /* needs to be clickable on */ + return rect().contains(pos); + } +}; diff --git a/util/coreboot-configurator/src/application/ToggleSwitch.svg.h b/util/coreboot-configurator/src/application/ToggleSwitch.svg.h new file mode 100644 index 0000000000..4aeb12b122 --- /dev/null +++ b/util/coreboot-configurator/src/application/ToggleSwitch.svg.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#pragma once + +/* Embed SVG files into code as debian packages does weird things when svgs are included as qrc */ +namespace ToggleSwitchSVG { +static constexpr char s_toggledOnContent[] = + "\n" + "\n" + " \n" + " \n" + " \n" + " image/svg+xml\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; +static constexpr char s_toggledOffContent[] = + "\n" + " \n" + " \n" + ""; +} diff --git a/util/coreboot-configurator/src/application/Util.h b/util/coreboot-configurator/src/application/Util.h new file mode 100644 index 0000000000..55553f2981 --- /dev/null +++ b/util/coreboot-configurator/src/application/Util.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#pragma once + +#include +#include +#include + +namespace Util { + inline QMap parseParameters(QIODevice& dev){ + QString curr_line; + QMap result; + + while (!dev.atEnd()) { + curr_line = dev.readLine().trimmed(); + + auto split = curr_line.split('='); + if(split.size()!=2){ + continue; + } + + result.insert(split[0].trimmed(), split[1].trimmed()); + } + return result; + } +} diff --git a/util/coreboot-configurator/src/application/lang.qrc b/util/coreboot-configurator/src/application/lang.qrc new file mode 100644 index 0000000000..e25d9df240 --- /dev/null +++ b/util/coreboot-configurator/src/application/lang.qrc @@ -0,0 +1,3 @@ + + + diff --git a/util/coreboot-configurator/src/application/main.cpp b/util/coreboot-configurator/src/application/main.cpp new file mode 100644 index 0000000000..94b10d9fff --- /dev/null +++ b/util/coreboot-configurator/src/application/main.cpp @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include + +#include "MainWindow.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + QTranslator translator; + if (translator.load(QLocale(), QLatin1String("corebootconfigurator"), QLatin1String("_"), QLatin1String(":/lang/i18n"))){ + a.installTranslator(&translator); + } + + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/util/coreboot-configurator/src/application/meson.build b/util/coreboot-configurator/src/application/meson.build new file mode 100644 index 0000000000..cb9b50e8d1 --- /dev/null +++ b/util/coreboot-configurator/src/application/meson.build @@ -0,0 +1,35 @@ +## SPDX-License-Identifier: GPL-2.0-only + +# Documentation: https://mesonbuild.com/Qt5-module.html +qt5 = import('qt5') +qt5_dep = dependency('qt5', modules : ['Core', 'Widgets', 'Svg']) +yamlcpp_dep = dependency('yaml-cpp', version: '>= 0.5.1', required: true) + +# TODO: Translations +# lang_cpp = qt5.compile_translations(qresource: 'lang.qrc') + +generated_files = qt5.preprocess( + moc_headers : ['MainWindow.h', 'AboutDialog.h', 'ToggleSwitch.h'], + ui_files : ['MainWindow.ui', 'AboutDialog.ui'], + dependencies : [qt5_dep], + qresources : ['resources.qrc'], +) + +mock = get_option('mock') + +if mock + add_project_arguments('-DMOCK', language : 'cpp') +endif + +executable('coreboot-configurator', + 'main.cpp', + 'MainWindow.cpp', + 'AboutDialog.cpp', + 'Configuration.cpp', + 'ToggleSwitch.cpp', + 'NvramToolCli.cpp', +# lang_cpp, + generated_files, + dependencies : [qt5_dep, yamlcpp_dep], + install : true +) diff --git a/util/coreboot-configurator/src/application/qrc/categories.yaml b/util/coreboot-configurator/src/application/qrc/categories.yaml new file mode 100644 index 0000000000..21419511fe --- /dev/null +++ b/util/coreboot-configurator/src/application/qrc/categories.yaml @@ -0,0 +1,119 @@ + processor: + displayName: Processor + hyper_threading: + displayName: Hyper-Threading + type: bool + help: Enable or disable Hyper-Threading + vtd: + displayName: Intel VT-d + type: bool + help: Enable or disable Intel VT-d (virtualisation) + power_profile: + displayName: Power Profile + type: enum + help: Select whether to maximise performance, battery life or both + me_state: + displayName: Intel Management Engine + type: bool + help: Enable or disable the Intel Management Engine + + devices: + displayName: Devices + wireless: + displayName: Wireless + type: bool + help: Enable or disable the built-in wireless card + wlan: + displayName: Wireless + type: bool + help: Enable or disable the built-in wireless card + bluetooth: + displayName: Bluetooth + type: bool + help: Enable or disable the built-in bluetooth + wwan: + displayName: Mobile Network + type: bool + help: Enable or disable the built-in mobile network + ethernet1: + displayName: Ethernet 1 + type: bool + help: Enable or disable the built-in Ethernet Port 1 + ethernet2: + displayName: Ethernet 2 + type: bool + help: Enable or disable the built-in Ethernet Port 2 + ethernet3: + displayName: Ethernet 3 + type: bool + help: Enable or disable the built-in Ethernet Port 3 + webcam: + displayName: Webcam + type: bool + help: Enable or disable the built-in webcam + microphone: + displayName: Microphone + type: bool + help: Enable or disable the built-in microphone + legacy_8254_timer: + displayName: Clock Gating + type: bool + help: Enable or disable the legacy 8254 timer. Reduces power consumption when enabled but must be disabled for certain distributions such as Qubes + usb_always_on: + displayName: USB Always On + type: bool + help: Allow the USB ports to provide power to connected devices when the computer is suspended + touchpad: + displayName: Touchpad + type: bool + help: Enable or disable the built-in touchpad + trackpoint: + displayName: Trackpoint + type: bool + help: Enable or disable the built-in trackpoint + sata_mode: + displayName: SATA Mode + type: enum + help: Set the mode of the SATA controller from AHCI or Compatible + thunderbolt: + displayName: Thunderbolt + type: bool + help: Enable or disable Thunderbolt functionality + + system: + displayName: System + kbl_timeout: + displayName: Keyboard Backlight Timeout + type: enum + help: Adjust the amout of time before the keyboard backlight turns off when un-used + fn_ctrl_swap: + displayName: Fn Ctrl Reverse + type: bool + help: Swap the functions of the [Fn] and [Ctrl] keys + max_charge: + displayName: Max Charge + type: enum + help: Set the maximum level the battery will charge to + fan_mode: + displayName: Fan Mode + type: enum + help: Adjust the fan curve to priotise performance or noise levels + f1_to_f12_as_primary: + displayName: Function Lock + type: bool + help: Make the F-keys behave as if you are holding down the Fn key + + advanced: + displayName: Advanced + boot_option: + displayName: Boot Options + type: enum + help: Change the boot device in the event of a failed boot + debug_level: + displayName: Debug Level + type: enum + help: Set the verbosity of the debug output + power_on_after_fail: + displayName: Power on Behaviour + type: enum + help: Select whether to power on in the event of a power failure diff --git a/util/coreboot-configurator/src/application/qrc/star.svg b/util/coreboot-configurator/src/application/qrc/star.svg new file mode 100644 index 0000000000..3bb9802ff5 --- /dev/null +++ b/util/coreboot-configurator/src/application/qrc/star.svg @@ -0,0 +1,391 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/util/coreboot-configurator/src/application/qrc/toggle-off.svg b/util/coreboot-configurator/src/application/qrc/toggle-off.svg new file mode 100644 index 0000000000..504ea58c5f --- /dev/null +++ b/util/coreboot-configurator/src/application/qrc/toggle-off.svg @@ -0,0 +1,4 @@ + + + + diff --git a/util/coreboot-configurator/src/application/qrc/toggle-on.svg b/util/coreboot-configurator/src/application/qrc/toggle-on.svg new file mode 100644 index 0000000000..0b8e61848c --- /dev/null +++ b/util/coreboot-configurator/src/application/qrc/toggle-on.svg @@ -0,0 +1,65 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/util/coreboot-configurator/src/application/resources.qrc b/util/coreboot-configurator/src/application/resources.qrc new file mode 100644 index 0000000000..06264d63d1 --- /dev/null +++ b/util/coreboot-configurator/src/application/resources.qrc @@ -0,0 +1,12 @@ + + + qrc/toggle-off.svg + qrc/toggle-on.svg + + + qrc/categories.yaml + + + qrc/star.svg + + diff --git a/util/coreboot-configurator/src/meson.build b/util/coreboot-configurator/src/meson.build new file mode 100644 index 0000000000..cb73f0818b --- /dev/null +++ b/util/coreboot-configurator/src/meson.build @@ -0,0 +1,4 @@ +## SPDX-License-Identifier: GPL-2.0-only + +subdir('application') +subdir('resources') diff --git a/util/coreboot-configurator/src/resources/coreboot-configurator.desktop b/util/coreboot-configurator/src/resources/coreboot-configurator.desktop new file mode 100644 index 0000000000..5f17d000e4 --- /dev/null +++ b/util/coreboot-configurator/src/resources/coreboot-configurator.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=coreboot configurator +StartupWMCLass=coreboot_configurator +Exec=/usr/bin/coreboot-configurator +Icon=coreboot-configurator.png +Type=Application +Categories=Settings;System +Comment=A graphical interface to set options on devices with coreboot firmware. +Keywords=coreboot;BIOS;Firmware;uefi; diff --git a/util/coreboot-configurator/src/resources/coreboot_configurator.svg b/util/coreboot-configurator/src/resources/coreboot_configurator.svg new file mode 100644 index 0000000000..33a7229891 --- /dev/null +++ b/util/coreboot-configurator/src/resources/coreboot_configurator.svg @@ -0,0 +1,748 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/util/coreboot-configurator/src/resources/meson.build b/util/coreboot-configurator/src/resources/meson.build new file mode 100644 index 0000000000..12270ab14e --- /dev/null +++ b/util/coreboot-configurator/src/resources/meson.build @@ -0,0 +1,43 @@ +## SPDX-License-Identifier: GPL-2.0-only + +# Polkit Files +polkit_dir = join_paths(get_option('datadir'), 'polkit-1', 'actions') +polkit_sources = [ + 'org.coreboot.nvramtool.policy', + 'org.coreboot.reboot.policy', +] + +install_data(polkit_sources, + install_dir: polkit_dir) + +# Desktop Entry +desktop_dir = join_paths(get_option('datadir'), 'applications') +desktop_sources = [ + 'coreboot-configurator.desktop', +] + +install_data(desktop_sources, + install_dir: desktop_dir) + +# Icon +inkscape = find_program('inkscape') +icon_dir = join_paths(get_option('datadir'),'icons', 'hicolor') +foreach size: get_option('sizes') + target_temp_name = '@0@'.format(size) + dpi=size.to_int() * 2 + png = configure_file( + input: 'coreboot_configurator.svg', + output: target_temp_name + '.png', + command: [ + inkscape, + '--export-height=@0@'.format(size), + '--export-width=@0@'.format(size), + '--export-png=@OUTPUT@', + '@INPUT@', + ] + ) + + install_data(png, + rename: meson.project_name() + '.png', + install_dir: join_paths(icon_dir, '@0@x@1@'.format(size, size), 'apps')) +endforeach diff --git a/util/coreboot-configurator/src/resources/org.coreboot.nvramtool.policy b/util/coreboot-configurator/src/resources/org.coreboot.nvramtool.policy new file mode 100644 index 0000000000..c95bc8b9a3 --- /dev/null +++ b/util/coreboot-configurator/src/resources/org.coreboot.nvramtool.policy @@ -0,0 +1,13 @@ + + + + + Authentication is required to read and write to coreboot settings. + + auth_admin_keep + + /usr/sbin/nvramtool + + diff --git a/util/coreboot-configurator/src/resources/org.coreboot.reboot.policy b/util/coreboot-configurator/src/resources/org.coreboot.reboot.policy new file mode 100644 index 0000000000..5364c8c22c --- /dev/null +++ b/util/coreboot-configurator/src/resources/org.coreboot.reboot.policy @@ -0,0 +1,12 @@ + + + + + + yes + + /usr/sbin/reboot + + -- cgit v1.2.3