diff --git a/UEFIFind/uefifind.cpp b/UEFIFind/uefifind.cpp index 5b2d848..25e40f6 100644 --- a/UEFIFind/uefifind.cpp +++ b/UEFIFind/uefifind.cpp @@ -92,7 +92,7 @@ USTATUS UEFIFind::findFileRecursive(const UModelIndex index, const UString & hex // For patterns that cross header|body boundary, skip patterns entirely located in body, since // children search above has already found them. - if (hasChildren && mode == SEARCH_MODE_ALL && offset >= model->header(index).size()) { + if (hasChildren && mode == SEARCH_MODE_ALL && offset >= model->headerSize(index)) { offset = -1; } diff --git a/UEFITool/QHexView/src/qhexview.cpp b/UEFITool/QHexView/src/qhexview.cpp index e9a35da..af74167 100644 --- a/UEFITool/QHexView/src/qhexview.cpp +++ b/UEFITool/QHexView/src/qhexview.cpp @@ -788,6 +788,7 @@ void QHexView::drawDocument(QTextCursor& c) const { else if(m_options.linebackground.isValid() && !(line % 2)) bf.setBackground(m_options.linebackground); + bf.setLineHeight(this->lineHeight(), QTextBlockFormat::FixedHeight); c.setBlockFormat(bf); c.insertBlock({}); if(m_hexdocument->isEmpty()) diff --git a/UEFITool/ffsfinder.cpp b/UEFITool/ffsfinder.cpp index 51a2f0e..963ba67 100644 --- a/UEFITool/ffsfinder.cpp +++ b/UEFITool/ffsfinder.cpp @@ -12,6 +12,7 @@ */ #include "ffsfinder.h" +#include "../common/utility.h" #if QT_VERSION_MAJOR >= 6 #include <QRegularExpression> @@ -36,7 +37,8 @@ USTATUS FfsFinder::findHexPattern(const UModelIndex & index, const UByteArray & return U_INVALID_PARAMETER; // Check for "all substrings" pattern - if (hexPattern.count('.') == hexPattern.length()) + auto c = checkSingle(hexPattern); + if (c == '.') return U_SUCCESS; USTATUS ret = U_ITEM_NOT_FOUND; @@ -78,7 +80,7 @@ USTATUS FfsFinder::findHexPattern(const UModelIndex & index, const UByteArray & if (offset % 2 == 0) { // For patterns that cross header|body boundary, skip patterns entirely located in body, since // children search above has already found them. - if (!(hasChildren && mode == SEARCH_MODE_ALL && offset/2 >= model->header(index).size())) { + if (!(hasChildren && mode == SEARCH_MODE_ALL && offset/2 >= model->headerSize(index))) { UModelIndex parentFileIndex = model->findParentOfType(index, Types::File); UString name = model->name(index); if (model->parent(index) == parentFileIndex) { @@ -165,7 +167,8 @@ USTATUS FfsFinder::findGuidPattern(const UModelIndex & index, const UByteArray & hexPattern.append(list.at(3)).append(list.at(4)); // Check for "all substrings" pattern - if (hexPattern.count('.') == hexPattern.length()) + auto c = checkSingle(hexPattern); + if (c == '.') return U_SUCCESS; #if QT_VERSION_MAJOR >= 6 diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index f1b19b3..9f18a8e 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -23,12 +23,22 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), version(tr(PROGRAM_VERSION)), -markingEnabled(true) +changedFileFlag(false), +markingEnabled(true), +cStyleHexEnabled(true) { clipboard = QApplication::clipboard(); // Create UI ui->setupUi(this); + openedFileLabel.setToolButtonStyle(Qt::ToolButtonTextOnly); + openedFileLabel.setStyleSheet("QToolButton { border: none; background-color: transparent; }"); + openedFileLabel.setFocusPolicy(Qt::NoFocus); + ui->statusBar->addPermanentWidget(&openedFileLabel); + QFont font = ui->statusBar->font(); + font.setBold(true); + openedFileLabel.setFont(font); + connect(&openedFileLabel, SIGNAL(clicked()), this, SLOT(fileChangedResume())); searchDialog = new SearchDialog(this); hexViewDialog = new HexViewDialog(this); goToAddressDialog = new GoToAddressDialog(this); @@ -73,7 +83,10 @@ markingEnabled(true) connect(ui->actionExportDiscoveredGuids, SIGNAL(triggered()), this, SLOT(exportDiscoveredGuids())); connect(ui->actionGenerateReport, SIGNAL(triggered()), this, SLOT(generateReport())); connect(ui->actionToggleBootGuardMarking, SIGNAL(toggled(bool)), this, SLOT(toggleBootGuardMarking(bool))); + connect(ui->actionToggleCStyleHexValues, SIGNAL(toggled(bool)), this, SLOT(toggleCStyleHexValues(bool))); + connect(ui->actionExpandAll, SIGNAL(triggered()), this, SLOT(expandTree())); connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(writeSettings())); + connect(&watcher, SIGNAL(fileChanged(QString)), this, SLOT(fileChanged(QString))); // Enable Drag-and-Drop actions setAcceptDrops(true); @@ -92,6 +105,24 @@ markingEnabled(true) // Read stored settings readSettings(); + + // Update recent files list in menu + updateRecentFilesMenu(); + + // Create a QActionGroup + QActionGroup* actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); // Ensure only one action is checked at a time + // Add actions from the menu to the QActionGroup + ui->actionTrackAndIgnore->setData(TRACK_IGNORE); + ui->actionTrackAndIgnore->setChecked(ui->actionTrackAndIgnore->data() == fileTrackingState); + actionGroup->addAction(ui->actionTrackAndIgnore); + ui->actionTrackAndAsk->setData(TRACK_ASK); + ui->actionTrackAndAsk->setChecked(ui->actionTrackAndAsk->data() == fileTrackingState); + actionGroup->addAction(ui->actionTrackAndAsk); + ui->actionTrackAndReopen->setData(TRACK_REOPEN); + ui->actionTrackAndReopen->setChecked(ui->actionTrackAndReopen->data() == fileTrackingState); + actionGroup->addAction(ui->actionTrackAndReopen); + connect(actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(onTrackingAction(QAction*))); } UEFITool::~UEFITool() @@ -125,6 +156,9 @@ void UEFITool::init() // Set window title setWindowTitle(tr("UEFITool %1").arg(version)); + // Some font hint + setFont(ui->infoEdit->font()); + // Disable menus ui->actionSearch->setEnabled(false); ui->actionGoToBase->setEnabled(false); @@ -139,6 +173,7 @@ void UEFITool::init() ui->menuStoreActions->setEnabled(false); ui->menuEntryActions->setEnabled(false); ui->menuMessageActions->setEnabled(false); + ui->actionExpandAll->setEnabled(false); // Create new model ... delete model; @@ -152,11 +187,17 @@ void UEFITool::init() model->setMarkingEnabled(markingEnabled); ui->actionToggleBootGuardMarking->setChecked(markingEnabled); + // Set proper C-Style hex values view state + model->setCStyleHexEnabled(cStyleHexEnabled); + ui->actionToggleCStyleHexValues->setChecked(cStyleHexEnabled); + // Connect signals to slots connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(populateUi(const QModelIndex &))); connect(ui->structureTreeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(populateUi(const QItemSelection &))); + connect(ui->structureTreeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(setExpandAll())); + connect(ui->structureTreeView, SIGNAL(collapsed(const QModelIndex&)), this, SLOT(setExpandAll())); connect(ui->parserMessagesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*))); connect(ui->parserMessagesListWidget, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(enableMessagesCopyActions(QListWidgetItem*))); connect(ui->finderMessagesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*))); @@ -211,6 +252,38 @@ void UEFITool::updateUiForNewColorScheme(Qt::ColorScheme scheme) } #endif +void UEFITool::updateRecentFilesMenu(const QString& fileName) +{ + // Update list + if (!fileName.isEmpty()) { + recentFiles.removeAll(fileName); + recentFiles.prepend(fileName); + while (recentFiles.size() > 21) { + recentFiles.removeLast(); + } + } + + // Delete old actions + for (QAction* action : recentFileActions) { + ui->menuFile->removeAction(action); + delete action; + } + recentFileActions.clear(); + + if (!recentFiles.isEmpty()) { + // Insert new actions before "Quit" + for (const QString& path : recentFiles) { + QAction* action = new QAction(QDir::toNativeSeparators(path), this); + connect(action, SIGNAL(triggered()), this, SLOT(openRecentImageFile())); + action->setData(path); + ui->menuFile->insertAction(ui->actionQuit, action); + recentFileActions.append(action); + } + // Finally, insert a separator after the list and before "Quit" + recentFileActions.append(ui->menuFile->insertSeparator(ui->actionQuit)); + } +} + void UEFITool::populateUi(const QItemSelection &selected) { if (selected.isEmpty()) { @@ -235,7 +308,10 @@ void UEFITool::populateUi(const QModelIndex ¤t) // Enable menus ui->menuCapsuleActions->setEnabled(type == Types::Capsule); - ui->menuImageActions->setEnabled(type == Types::Image); + ui->menuImageActions->setEnabled(type == Types::Image + || (type == Types::Volume && model->hasEmptyHeader(current)) + || type == Types::FreeSpace + ); ui->menuRegionActions->setEnabled(type == Types::Region); ui->menuPaddingActions->setEnabled(type == Types::Padding); ui->menuVolumeActions->setEnabled(type == Types::Volume); @@ -300,6 +376,8 @@ void UEFITool::populateUi(const QModelIndex ¤t) //ui->actionReplaceBody->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); ui->menuMessageActions->setEnabled(false); + + setExpandAll(); } void UEFITool::search() @@ -310,7 +388,7 @@ void UEFITool::search() int index = searchDialog->ui->tabWidget->currentIndex(); if (index == 0) { // Hex pattern searchDialog->ui->hexEdit->setFocus(); - QByteArray pattern = searchDialog->ui->hexEdit->text().toLatin1().replace(" ", ""); + UByteArray pattern = searchDialog->ui->hexEdit->text().toLatin1().replace(" ", ""); if (pattern.isEmpty()) return; UINT8 mode; @@ -326,7 +404,7 @@ void UEFITool::search() else if (index == 1) { // GUID searchDialog->ui->guidEdit->setFocus(); searchDialog->ui->guidEdit->setCursorPosition(0); - QByteArray pattern = searchDialog->ui->guidEdit->text().toLatin1(); + UByteArray pattern = searchDialog->ui->guidEdit->text().toLatin1(); if (pattern.isEmpty()) return; UINT8 mode; @@ -508,7 +586,7 @@ void UEFITool::extract(const UINT8 mode) if (!index.isValid()) return; - QByteArray extracted; + UByteArray extracted; QString name; USTATUS result = ffsOps->extract(index, name, extracted, mode); if (result) { @@ -516,7 +594,7 @@ void UEFITool::extract(const UINT8 mode) return; } - name = QDir::toNativeSeparators(currentDir + QDir::separator() + name); + name = QDir::toNativeSeparators(extractDir + QDir::separator() + name); //ui->statusBar->showMessage(name); @@ -566,6 +644,8 @@ void UEFITool::extract(const UINT8 mode) outputFile.resize(0); outputFile.write(extracted); outputFile.close(); + + extractDir = QFileInfo(path).absolutePath(); } void UEFITool::rebuild() @@ -617,20 +697,185 @@ void UEFITool::saveImageFile() } +void UEFITool::askReopenImageFile() +{ + QMessageBox msgBox(QMessageBox::Question, tr("Confirmation"), + tr("Image file was changed by external program, do you want to reopen it?"), + QMessageBox::Yes | QMessageBox::No); + QCheckBox checkBox(tr("Do not ask again")); + checkBox.setChecked(TRACK_IGNORE == fileTrackingState); + msgBox.setDefaultButton(QMessageBox::Yes); + msgBox.setCheckBox(&checkBox); + + int ret = msgBox.exec(); + + if (checkBox.isChecked()) { + if (ret == QMessageBox::Yes) { + ui->actionTrackAndReopen->setChecked(true); + fileTrackingState = TRACK_REOPEN; + } + else if (ret == QMessageBox::No) { + ui->actionTrackAndIgnore->setChecked(true); + fileTrackingState = TRACK_IGNORE; + } + } + if (ret == QMessageBox::Yes) + reopenImageFile(); +} + +void UEFITool::onTrackingAction(QAction* action) +{ + if (action) { + int newTrackingState = action->data().toInt(); + if (newTrackingState != fileTrackingState) { + fileTrackingState = newTrackingState; + } + } +} + +void UEFITool::saveTreeState(const QModelIndex& index, QHash<QString, bool>& states) +{ + if (!index.isValid()) + return; + + // Save the expanded state using the item's data as the key + QString key = model->data(index, Qt::DisplayRole).toString(); + states[key] = ui->structureTreeView->isExpanded(index); + + // Recursively save child states + for (int row = 0; row < model->rowCount(index); row++) { + saveTreeState(model->index(row, 0, index), states); + } +} + +void UEFITool::restoreTreeState(const QModelIndex& index, const QHash<QString, bool>& states) +{ + if (!index.isValid()) + return; + + // Restore the expanded state using the item's data as the key + QString key = model->data(index, Qt::DisplayRole).toString(); + if (states.contains(key)) { + ui->structureTreeView->setExpanded(index, states[key]); + } + + // Recursively restore child states + for (int row = 0; row < model->rowCount(index); row++) { + restoreTreeState(model->index(row, 0, index), states); + } +} + +bool UEFITool::isAllExpanded() +{ + QAbstractItemModel* model = ui->structureTreeView->model(); + if (!model) + return false; + + QStack<QModelIndex> stack; + stack.push(QModelIndex()); + + while (!stack.isEmpty()) { + QModelIndex current = stack.pop(); + + if (current.isValid() && ui->structureTreeView->isExpanded(current) == false) { + return false; + } + + int rowCount = model->rowCount(current); + for (int i = 0; i < rowCount; ++i) { + QModelIndex child = model->index(i, 0, current); + if (child.isValid()) { + stack.push(child); + } + } + } + + return true; +} + +void UEFITool::setExpandAll() +{ + ui->actionExpandAll->setEnabled(!isAllExpanded()); +} + +void UEFITool::expandTree() +{ + ui->structureTreeView->expandAll(); +} + +void UEFITool::fileChangedResume() +{ + if (changedFileFlag) { + askReopenImageFile(); + } +} + +void UEFITool::fileChanged(const QString& path) +{ + setChangedFileFlag(true); + + switch (fileTrackingState) { + case TRACK_REOPEN : + reopenImageFile(); + case TRACK_IGNORE : + return; + } + + askReopenImageFile(); +} + +void UEFITool::setChangedFileFlag(const bool flag) +{ + QFont font(openedFileLabel.font()); + font.setItalic(flag); + openedFileLabel.setFont(font); + changedFileFlag = flag; +} + void UEFITool::openImageFile() { - QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), currentDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)")); + QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), openImageDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)")); openImageFile(path); } void UEFITool::openImageFileInNewWindow() { - QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file in new window"), currentDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)")); + QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file in new window"), openImageDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)")); if (path.trimmed().isEmpty()) return; QProcess::startDetached(currentProgramPath, QStringList(path)); } +void UEFITool::openRecentImageFile() +{ + QAction* action = qobject_cast<QAction*>(sender()); + if (action) { + QString fileName = action->data().toString(); + if (!fileName.isEmpty()) { + openImageFile(fileName); + } + } +} + +void UEFITool::reopenImageFile() +{ + if (!currentPath.isEmpty() && ui->structureTreeView->model()->hasChildren(UModelIndex())) { + QHash<QString, bool> states; + saveTreeState(model->index(0, 0), states); + QList <UModelIndex> selected = ui->structureTreeView->selectionModel()->selectedIndexes(); + UINT64 base = selected.isEmpty() ? UEFI_UPPER_INVALID_ADDRESS : model->base(selected.first()); + + openImageFile(currentPath); + + restoreTreeState(model->index(0, 0), states); + if (base < UEFI_UPPER_INVALID_ADDRESS) { + UModelIndex index = model->findByBase(base); + ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + ui->structureTreeView->scrollTo(index); + } + } +} + void UEFITool::openImageFile(QString path) { if (path.trimmed().isEmpty()) @@ -645,18 +890,23 @@ void UEFITool::openImageFile(QString path) QFile inputFile; inputFile.setFileName(path); - + QElapsedTimer timer; + timer.start(); if (!inputFile.open(QFile::ReadOnly)) { QMessageBox::critical(this, tr("Image parsing failed"), tr("Can't open input file for reading"), QMessageBox::Ok); return; } - + + watcher.removePaths(watcher.files()); + setChangedFileFlag(false); + watcher.addPath(path); + QByteArray buffer = inputFile.readAll(); inputFile.close(); - + init(); setWindowTitle(tr("UEFITool %1 - %2").arg(version).arg(fileInfo.fileName())); - + // Parse the image USTATUS result = ffsParser->parse(buffer); showParserMessages(); @@ -665,7 +915,10 @@ void UEFITool::openImageFile(QString path) return; } else { - ui->statusBar->showMessage(tr("Opened: %1").arg(fileInfo.fileName())); + ui->statusBar->showMessage(QString("Image file %1 ").arg(fileInfo.fileName()) + + (path == currentPath ? tr("reopened") : tr("opened")) + + QString(" in %1 sec").arg(timer.elapsed() / 1000.0)); + openedFileLabel.setText(QDir::toNativeSeparators(fileInfo.absoluteFilePath())); } ffsParser->outputInfo(); @@ -688,7 +941,7 @@ void UEFITool::openImageFile(QString path) // Enable goToBase and goToAddress ui->actionGoToBase->setEnabled(true); - if (ffsParser->getAddressDiff() <= 0xFFFFFFFFUL) + if (ffsParser->getAddressDiff() < UEFI_UPPER_INVALID_ADDRESS) ui->actionGoToAddress->setEnabled(true); // Enable generateReport @@ -699,9 +952,15 @@ void UEFITool::openImageFile(QString path) // Set current directory currentDir = fileInfo.absolutePath(); - + openImageDir = currentDir; + // Set current path currentPath = path; + + // Update menu + updateRecentFilesMenu(currentPath); + + ui->structureTreeView->expandToDepth(1); } void UEFITool::enableMessagesCopyActions(QListWidgetItem* item) @@ -771,6 +1030,12 @@ void UEFITool::toggleBootGuardMarking(bool enabled) markingEnabled = enabled; } +void UEFITool::toggleCStyleHexValues(bool enabled) +{ + model->setCStyleHexEnabled(enabled); + cStyleHexEnabled = enabled; +} + // Emit double click signal of QListWidget on enter/return key pressed bool UEFITool::eventFilter(QObject* obj, QEvent* event) { @@ -898,14 +1163,21 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event) return; } + QMenu* menu = nullptr; + switch (model->type(index)) { - case Types::Capsule: ui->menuCapsuleActions->exec(event->globalPos()); break; - case Types::Image: ui->menuImageActions->exec(event->globalPos()); break; - case Types::Region: ui->menuRegionActions->exec(event->globalPos()); break; - case Types::Padding: ui->menuPaddingActions->exec(event->globalPos()); break; - case Types::Volume: ui->menuVolumeActions->exec(event->globalPos()); break; - case Types::File: ui->menuFileActions->exec(event->globalPos()); break; - case Types::Section: ui->menuSectionActions->exec(event->globalPos()); break; + case Types::Capsule: menu = ui->menuCapsuleActions; break; + case Types::Image: menu = ui->menuImageActions; break; + case Types::Region: menu = ui->menuRegionActions; break; + case Types::Padding: menu = ui->menuPaddingActions; break; + case Types::Volume: + if (model->hasEmptyHeader(index)) + menu = ui->menuImageActions; + else + menu = ui->menuVolumeActions; + break; + case Types::File: menu = ui->menuFileActions; break; + case Types::Section: menu = ui->menuSectionActions; break; case Types::VssStore: case Types::Vss2Store: case Types::FdcStore: @@ -918,9 +1190,16 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event) case Types::CmdbStore: case Types::FptStore: case Types::CpdStore: - case Types::BpdtStore: ui->menuStoreActions->exec(event->globalPos()); break; - case Types::FreeSpace: break; // No menu needed for FreeSpace item - default: ui->menuEntryActions->exec(event->globalPos()); break; + case Types::BpdtStore: menu = ui->menuStoreActions; break; + case Types::FreeSpace: menu = ui->menuImageActions; break; + default: menu = ui->menuEntryActions; break; + } + + if (menu != nullptr) { + QList<QAction*> actions = menu->actions(); + QAction s = QAction(nullptr); + s.setSeparator(true); + QMenu::exec(actions << &s << ui->actionExpandAll, event->globalPos()); } } @@ -942,11 +1221,20 @@ void UEFITool::readSettings() ui->structureTreeView->setColumnWidth(3, settings.value("tree/columnWidth3", ui->structureTreeView->columnWidth(3)).toInt()); markingEnabled = settings.value("tree/markingEnabled", true).toBool(); ui->actionToggleBootGuardMarking->setChecked(markingEnabled); - + cStyleHexEnabled = settings.value("tree/cStyleHexEnabled", true).toBool(); + ui->actionToggleCStyleHexValues->setChecked(cStyleHexEnabled); + openImageDir = settings.value("paths/openImageDir", ".").toString(); + openGuidDatabaseDir = settings.value("paths/openGuidDatabaseDir", ".").toString(); + extractDir = settings.value("paths/extractDir", ".").toString(); + recentFiles = settings.value("paths/recentFiles").toStringList(); + fileTrackingState = settings.value("options/fileTracking", TRACK_IGNORE).toInt(); + if (fileTrackingState < TRACK_MIN || fileTrackingState > TRACK_MAX) + fileTrackingState = TRACK_IGNORE; + // Set monospace font QString fontName; int fontSize; -#if defined Q_OS_MACOS +#if defined Q_OS_OSX fontName = settings.value("mainWindow/fontName", QString("Menlo")).toString(); fontSize = settings.value("mainWindow/fontSize", 10).toInt(); #elif defined Q_OS_WIN @@ -975,8 +1263,14 @@ void UEFITool::writeSettings() settings.setValue("tree/columnWidth2", ui->structureTreeView->columnWidth(2)); settings.setValue("tree/columnWidth3", ui->structureTreeView->columnWidth(3)); settings.setValue("tree/markingEnabled", markingEnabled); + settings.setValue("tree/cStyleHexEnabled", cStyleHexEnabled); settings.setValue("mainWindow/fontName", currentFont.family()); settings.setValue("mainWindow/fontSize", currentFont.pointSize()); + settings.setValue("paths/openImageDir", openImageDir); + settings.setValue("paths/openGuidDatabaseDir", openGuidDatabaseDir); + settings.setValue("paths/extractDir", extractDir); + settings.setValue("paths/recentFiles", recentFiles); + settings.setValue("options/fileTracking", fileTrackingState); } void UEFITool::showFitTable() @@ -1041,11 +1335,12 @@ void UEFITool::currentTabChanged(int index) void UEFITool::loadGuidDatabase() { - QString path = QFileDialog::getOpenFileName(this, tr("Select GUID database file to load"), currentDir, tr("Comma-separated values files (*.csv);;All files (*)")); + QString path = QFileDialog::getOpenFileName(this, tr("Select GUID database file to load"), openGuidDatabaseDir, tr("Comma-separated values files (*.csv);;All files (*)")); if (!path.isEmpty()) { initGuidDatabase(path); if (!currentPath.isEmpty() && QMessageBox::Yes == QMessageBox::information(this, tr("New GUID database loaded"), tr("Apply new GUID database on the opened file?\nUnsaved changes and tree position will be lost."), QMessageBox::Yes, QMessageBox::No)) openImageFile(currentPath); + openGuidDatabaseDir = QFileInfo(path).absolutePath(); } } diff --git a/UEFITool/uefitool.h b/UEFITool/uefitool.h index 08ef908..be0980d 100644 --- a/UEFITool/uefitool.h +++ b/UEFITool/uefitool.h @@ -15,6 +15,7 @@ #define UEFITOOL_H #include <QMainWindow> +#include <QActionGroup> #include <QByteArray> #include <QClipboard> #include <QDragEnterEvent> @@ -22,6 +23,7 @@ #include <QFile> #include <QFileDialog> #include <QFileInfo> +#include <QFileSystemWatcher> #include <QFont> #include <QListWidget> #include <QMenu> @@ -32,10 +34,12 @@ #include <QProcess> #include <QSettings> #include <QSplitter> +#include <QStack> #include <QStyleFactory> #include <QString> #include <QTableWidget> #include <QTreeView> +#include <QToolButton> #include <QUrl> #include "../common/basetypes.h" @@ -75,13 +79,19 @@ private slots: void scrollTreeView(QListWidgetItem* item); // For messages void scrollTreeView(QTableWidgetItem* item); // For FIT table entries + void onTrackingAction(QAction* action); + void fileChangedResume(); + void fileChanged(const QString& path); + void setChangedFileFlag(const bool flag); void openImageFile(); void openImageFileInNewWindow(); + void openRecentImageFile(); void saveImageFile(); void search(); void goToBase(); void goToAddress(); + void expandTree(); void hexView(); void bodyHexView(); @@ -112,6 +122,8 @@ private slots: void clearMessages(); void toggleBootGuardMarking(bool enabled); + void toggleCStyleHexValues(bool enabled); + void setExpandAll(); void about(); void aboutQt(); @@ -144,23 +156,39 @@ private: GoToBaseDialog* goToBaseDialog; GoToAddressDialog* goToAddressDialog; QClipboard* clipboard; + QList<QAction*> recentFileActions; + QFileSystemWatcher watcher; + QToolButton openedFileLabel; + QStringList recentFiles; QString currentDir; QString currentPath; QString currentProgramPath; + QString openImageDir; + QString openGuidDatabaseDir; + QString extractDir; QFont currentFont; const QString version; + int fileTrackingState; + bool changedFileFlag; bool markingEnabled; + bool cStyleHexEnabled; bool eventFilter(QObject* obj, QEvent* event); void dragEnterEvent(QDragEnterEvent* event); void dropEvent(QDropEvent* event); void contextMenuEvent(QContextMenuEvent* event); + void updateRecentFilesMenu(const QString& fileName = QString()); void readSettings(); + void askReopenImageFile(); + void reopenImageFile(); void showParserMessages(); void showFinderMessages(); void showFitTable(); void showSecurityInfo(); void showBuilderMessages(); + bool isAllExpanded(); + void saveTreeState(const QModelIndex& index, QHash<QString, bool>& states); + void restoreTreeState(const QModelIndex& index, const QHash<QString, bool>& states); enum { TAB_PARSER, @@ -169,6 +197,14 @@ private: TAB_SEARCH, TAB_BUILDER }; + + enum { + TRACK_MIN = 0, + TRACK_IGNORE = TRACK_MIN, + TRACK_ASK, + TRACK_REOPEN, + TRACK_MAX = TRACK_REOPEN + }; }; #endif // UEFITOOL_H diff --git a/UEFITool/uefitool.ui b/UEFITool/uefitool.ui index 51b6248..290709e 100644 --- a/UEFITool/uefitool.ui +++ b/UEFITool/uefitool.ui @@ -48,11 +48,11 @@ <item> <widget class="QSplitter" name="messagesSplitter"> <property name="orientation"> - <enum>Qt::Vertical</enum> + <enum>Qt::Orientation::Vertical</enum> </property> <widget class="QSplitter" name="infoSplitter"> <property name="orientation"> - <enum>Qt::Horizontal</enum> + <enum>Qt::Orientation::Horizontal</enum> </property> <widget class="QGroupBox" name="structureGroupBox"> <property name="title"> @@ -196,7 +196,7 @@ <item> <widget class="QSplitter" name="splitter"> <property name="orientation"> - <enum>Qt::Horizontal</enum> + <enum>Qt::Orientation::Horizontal</enum> </property> <widget class="QTableWidget" name="fitTableWidget"/> </widget> @@ -311,17 +311,27 @@ <x>0</x> <y>0</y> <width>851</width> - <height>31</height> + <height>33</height> </rect> </property> <widget class="QMenu" name="menuFile"> <property name="title"> <string>&File</string> </property> + <widget class="QMenu" name="menuOpenedImageFileTracking"> + <property name="title"> + <string>Opened image file tracking</string> + </property> + <addaction name="actionTrackAndIgnore"/> + <addaction name="actionTrackAndAsk"/> + <addaction name="actionTrackAndReopen"/> + </widget> <addaction name="actionOpenImageFile"/> <addaction name="actionOpenImageFileInNewWindow"/> <addaction name="actionSaveImageFile"/> <addaction name="separator"/> + <addaction name="menuOpenedImageFileTracking"/> + <addaction name="separator"/> <addaction name="actionGenerateReport"/> <addaction name="separator"/> <addaction name="actionLoadGuidDatabase"/> @@ -329,6 +339,7 @@ <addaction name="actionUnloadGuidDatabase"/> <addaction name="actionExportDiscoveredGuids"/> <addaction name="separator"/> + <addaction name="separator"/> <addaction name="actionQuit"/> </widget> <widget class="QMenu" name="menuHelp"> @@ -548,6 +559,9 @@ <string>&View</string> </property> <addaction name="actionToggleBootGuardMarking"/> + <addaction name="actionToggleCStyleHexValues"/> + <addaction name="separator"/> + <addaction name="actionExpandAll"/> </widget> <addaction name="menuFile"/> <addaction name="menuAction"/> @@ -699,7 +713,7 @@ <string>F1</string> </property> <property name="menuRole"> - <enum>QAction::AboutRole</enum> + <enum>QAction::MenuRole::AboutRole</enum> </property> </action> <action name="actionAboutQt"> @@ -710,7 +724,7 @@ <string>Shift+F1</string> </property> <property name="menuRole"> - <enum>QAction::AboutQtRole</enum> + <enum>QAction::MenuRole::AboutQtRole</enum> </property> </action> <action name="actionQuit"> @@ -721,7 +735,7 @@ <string>Alt+X</string> </property> <property name="menuRole"> - <enum>QAction::QuitRole</enum> + <enum>QAction::MenuRole::QuitRole</enum> </property> </action> <action name="actionSearch"> @@ -936,6 +950,52 @@ <string>Ctrl+Alt+E</string> </property> </action> + <action name="actionToggleCStyleHexValues"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>C-style hex values</string> + </property> + <property name="toolTip"> + <string>C-style hex values</string> + </property> + <property name="shortcut"> + <string>Ctrl+Shift+C</string> + </property> + </action> + <action name="actionTrackAndIgnore"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Ignore changes</string> + </property> + </action> + <action name="actionTrackAndAsk"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Ask if changed</string> + </property> + </action> + <action name="actionTrackAndReopen"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Auto reopen if changed</string> + </property> + </action> + <action name="actionExpandAll"> + <property name="text"> + <string>Expand all</string> + </property> + <property name="toolTip"> + <string>Expand all tree items</string> + </property> + </action> </widget> <layoutdefault spacing="6" margin="11"/> <resources/> diff --git a/common/basetypes.h b/common/basetypes.h index 89c94f9..ad53567 100644 --- a/common/basetypes.h +++ b/common/basetypes.h @@ -78,6 +78,8 @@ typedef size_t USTATUS; #define U_INVALID_SYMBOL 55 #define U_ZLIB_DECOMPRESSION_FAILED 56 #define U_INVALID_STORE 57 +#define U_INVALID_PE_HEADER 58 +#define U_INVALID_TE_HEADER 59 #define U_INVALID_MANIFEST 251 #define U_UNKNOWN_MANIFEST_HEADER_VERSION 252 @@ -221,6 +223,10 @@ typedef struct EFI_TIME_ { #define TCG_HASH_ALGORITHM_ID_NULL 0x0010 #define TCG_HASH_ALGORITHM_ID_SM3 0x0012 +// Some whole image-related notional defines +#define UEFI_UPPER_INVALID_ADDRESS 0x100000000ULL +#define UEFI_LOWER_INVALID_ADDRESS (UEFI_UPPER_INVALID_ADDRESS >> 1) + // A workaround for compilers not supporting c++11 and c11 // for using PRIX64. #define __STDC_FORMAT_MACROS diff --git a/common/ffsbuilder.cpp b/common/ffsbuilder.cpp index dac3fa5..3d97358 100644 --- a/common/ffsbuilder.cpp +++ b/common/ffsbuilder.cpp @@ -39,7 +39,7 @@ USTATUS FfsBuilder::erase(const UModelIndex & index, UByteArray & erased) } } - erased = UByteArray(model->header(index).size() + model->body(index).size() + model->tail(index).size(), emptyByte); + erased = UByteArray(model->headerSize(index) + model->bodySize(index) + model->tailSize(index), emptyByte); return U_SUCCESS; } @@ -124,7 +124,7 @@ USTATUS FfsBuilder::buildCapsule(const UModelIndex & index, UByteArray & capsule // Check size of reconstructed capsule body, it must remain the same UINT32 newSize = (UINT32)capsule.size(); - UINT32 oldSize = (UINT32)model->body(index).size(); + UINT32 oldSize = (UINT32)model->bodySize(index); if (newSize > oldSize) { msg(usprintf("buildCapsule: new capsule size %Xh (%u) is bigger than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index); return U_INVALID_CAPSULE; @@ -223,7 +223,7 @@ USTATUS FfsBuilder::buildIntelImage(const UModelIndex & index, UByteArray & inte // Check size of new image, it must be same as old one UINT32 newSize = (UINT32)intelImage.size(); - UINT32 oldSize = (UINT32)model->body(index).size(); + UINT32 oldSize = (UINT32)model->bodySize(index); if (newSize > oldSize) { msg(usprintf("buildIntelImage: new image size %Xh (%u) is bigger than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index); return U_INVALID_IMAGE; @@ -294,7 +294,7 @@ USTATUS FfsBuilder::buildRawArea(const UModelIndex & index, UByteArray & rawArea // Check size of new raw area, it must be same as original one UINT32 newSize = (UINT32)rawArea.size(); - UINT32 oldSize = (UINT32)model->body(index).size(); + UINT32 oldSize = (UINT32)model->bodySize(index); if (newSize > oldSize) { msg(usprintf("buildRawArea: new area size %Xh (%u) is bigger than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index); return U_INVALID_RAW_AREA; diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 9236211..64c619c 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -41,7 +41,7 @@ // Constructor FfsParser::FfsParser(TreeModel* treeModel) : model(treeModel), -imageBase(0), addressDiff(0x100000000ULL), protectedRegionsBase(0) { +imageBase(0), addressDiff(UEFI_UPPER_INVALID_ADDRESS), protectedRegionsBase(0) { fitParser = new FitParser(treeModel, this); nvramParser = new NvramParser(treeModel, this); meParser = new MeParser(treeModel, this); @@ -85,15 +85,18 @@ USTATUS FfsParser::parse(const UByteArray & buffer) // Reset global parser state openedImage = buffer; imageBase = 0; - addressDiff = 0x100000000ULL; + addressDiff = UEFI_UPPER_INVALID_ADDRESS; + baseAddressesMap.clear(); protectedRegionsBase = 0; securityInfo = ""; protectedRanges.clear(); lastVtf = UModelIndex(); dxeCore = UModelIndex(); + model->setImage(buffer); + // Parse input buffer - USTATUS result = performFirstPass(buffer, root); + USTATUS result = performFirstPass(openedImage, root); if (result == U_SUCCESS) { if (lastVtf.isValid()) { result = performSecondPass(root); @@ -102,7 +105,7 @@ USTATUS FfsParser::parse(const UByteArray & buffer) msg(usprintf("%s: not a single Volume Top File is found, the image may be corrupted", __FUNCTION__)); } } - + addInfoRecursive(root); return result; } @@ -114,18 +117,78 @@ USTATUS FfsParser::performFirstPass(const UByteArray & buffer, UModelIndex & ind return U_INVALID_PARAMETER; } + biosRegionInfo.type = Subtypes::InvalidRegion; + // Try parsing as UEFI Capsule if (U_SUCCESS == parseCapsule(buffer, 0, UModelIndex(), index)) { return U_SUCCESS; } - + // Try parsing as some image + return parseImage(buffer, 0, UModelIndex(), index); +} + +USTATUS FfsParser::parseImage(const UByteArray& buffer, const UINT32 localOffset, const UModelIndex& parent, UModelIndex& index) +{ // Try parsing as Intel image - if (U_SUCCESS == parseIntelImage(buffer, 0, UModelIndex(), index)) { - return U_SUCCESS; + USTATUS result = parseIntelImage(buffer, localOffset, parent, index); + bool retryFindLastVtf = true; + if (U_SUCCESS != result || biosRegionInfo.type != Subtypes::BiosRegion) { + // Parse as generic image + result = parseGenericImage(buffer, localOffset, parent, index); + if (U_STORES_NOT_FOUND == result || model->rowCount(index) <= 0) { + result = parseVolumeBody(index, true); + bool notEmpty = model->rowCount(index) > 0; + if (U_SUCCESS == result && notEmpty) { + model->setName(index, "UEFI volume part"); + model->setType(index, Types::Volume); + model->setSubtype(index, Subtypes::Ffs2Volume); + } + else if (U_STORES_NOT_FOUND == result || !notEmpty) { + retryFindLastVtf = false; + model->setName(index, "Non-UEFI data"); + model->setType(index, Types::Padding); + model->setSubtype(index, Subtypes::DataPadding); + result = U_SUCCESS; + } + } } - - // Parse as generic image - return parseGenericImage(buffer, 0, UModelIndex(), index); + + if (retryFindLastVtf && !lastVtf.isValid()) { + // If lastVtf not found, we can present an alternative view of what the lastVtf and addressDiff shoud be + UINT64 preAddressDiff = UEFI_UPPER_INVALID_ADDRESS; + if (biosRegionInfo.type == Subtypes::BiosRegion) { + addressDiff = UEFI_UPPER_INVALID_ADDRESS - biosRegionInfo.length - biosRegionInfo.offset; + } + else if (!baseAddressesMap.empty()) { + if (baseAddressesMap.size() == 1) { + addressDiff = baseAddressesMap.begin()->first; + } + else { + std::pair <decltype(baseAddressesMap)::key_type, decltype(baseAddressesMap)::mapped_type> p = { 0, -1 }, p2 = p; + for (const auto& pair : baseAddressesMap) + if (pair.second > p.second) + p = pair; + baseAddressesMap.erase(p.first); + for (const auto& pair : baseAddressesMap) + if (pair.second > p2.second) + p2 = pair; + if (p.second > p2.second) + addressDiff = p.first; + } + } + + if (addressDiff < UEFI_UPPER_INVALID_ADDRESS) { + UINT64 endAddress = addressDiff + model->base(index) + model->headerSize(index) + model->bodySize(index); + if (endAddress > (UEFI_UPPER_INVALID_ADDRESS - INTEL_FIT_POINTER_OFFSET + sizeof(UINT32))) { + UModelIndex lastIndex = model->findByBase((UINT32)(endAddress - INTEL_FIT_POINTER_OFFSET - addressDiff)); + if (!model->compressed(lastIndex)) { + lastVtf = lastIndex; + } + } + } + } + + return result; } USTATUS FfsParser::parseGenericImage(const UByteArray & buffer, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) @@ -135,7 +198,11 @@ USTATUS FfsParser::parseGenericImage(const UByteArray & buffer, const UINT32 loc UString info = usprintf("Full size: %Xh (%u)", (UINT32)buffer.size(), (UINT32)buffer.size()); // Add tree item - index = model->addItem(localOffset, Types::Image, Subtypes::UefiImage, name, UString(), info, UByteArray(), buffer, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Image, Subtypes::UefiImage, + name, UString(), info, + 0, buffer.size(), 0, + Fixed, parent); // Parse the image as raw area imageBase = model->base(parent) + localOffset; @@ -176,8 +243,6 @@ USTATUS FfsParser::parseCapsule(const UByteArray & capsule, const UINT32 localOf } capsuleHeaderSize = capsuleHeader->HeaderSize; - UByteArray header = capsule.left(capsuleHeaderSize); - UByteArray body = capsule.mid(capsuleHeaderSize); UString name("UEFI capsule"); UString info = UString("Capsule GUID: ") + guidToUString(capsuleHeader->CapsuleGuid, false) + usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nImage size: %Xh (%u)\nFlags: %08Xh", @@ -187,7 +252,11 @@ USTATUS FfsParser::parseCapsule(const UByteArray & capsule, const UINT32 localOf capsuleHeader->Flags); // Add tree item - index = model->addItem(localOffset, Types::Capsule, Subtypes::UefiCapsule, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Capsule, Subtypes::UefiCapsule, + name, UString(), info, + capsuleHeaderSize, capsule.size() - capsuleHeaderSize, 0, + Fixed, parent); } // Check buffer for being Toshiba capsule header else if (capsule.startsWith(TOSHIBA_CAPSULE_GUID)) { @@ -208,8 +277,6 @@ USTATUS FfsParser::parseCapsule(const UByteArray & capsule, const UINT32 localOf } capsuleHeaderSize = capsuleHeader->HeaderSize; - UByteArray header = capsule.left(capsuleHeaderSize); - UByteArray body = capsule.mid(capsuleHeaderSize); UString name("Toshiba capsule"); UString info = UString("Capsule GUID: ") + guidToUString(capsuleHeader->CapsuleGuid, false) + usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nImage size: %Xh (%u)\nFlags: %08Xh", @@ -219,7 +286,11 @@ USTATUS FfsParser::parseCapsule(const UByteArray & capsule, const UINT32 localOf capsuleHeader->Flags); // Add tree item - index = model->addItem(localOffset, Types::Capsule, Subtypes::ToshibaCapsule, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Capsule, Subtypes::ToshibaCapsule, + name, UString(), info, + capsuleHeaderSize, capsule.size() - capsuleHeaderSize, 0, + Fixed, parent); } // Check buffer for being extended Aptio capsule header else if (capsule.startsWith(APTIO_SIGNED_CAPSULE_GUID) @@ -249,8 +320,8 @@ USTATUS FfsParser::parseCapsule(const UByteArray & capsule, const UINT32 localOf } capsuleHeaderSize = capsuleHeader->RomImageOffset; - UByteArray header = capsule.left(capsuleHeaderSize); - UByteArray body = capsule.mid(capsuleHeaderSize); +// UByteArray header = capsule.left(capsuleHeaderSize); +// UByteArray body = capsule.mid(capsuleHeaderSize); UString name("AMI Aptio capsule"); UString info = UString("Capsule GUID: ") + guidToUString(capsuleHeader->CapsuleHeader.CapsuleGuid, false) + usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nImage size: %Xh (%u)\nFlags: %08Xh", @@ -260,7 +331,11 @@ USTATUS FfsParser::parseCapsule(const UByteArray & capsule, const UINT32 localOf capsuleHeader->CapsuleHeader.Flags); // Add tree item - index = model->addItem(localOffset, Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, + name, UString(), info, + capsuleHeaderSize, capsule.size() - capsuleHeaderSize, 0, + Fixed, parent); // Show message about possible Aptio signature break if (signedCapsule) { @@ -270,16 +345,10 @@ USTATUS FfsParser::parseCapsule(const UByteArray & capsule, const UINT32 localOf // Capsule present if (capsuleHeaderSize > 0) { - UByteArray image = capsule.mid(capsuleHeaderSize); UModelIndex imageIndex; - - // Try parsing as Intel image - if (U_SUCCESS == parseIntelImage(image, capsuleHeaderSize, index, imageIndex)) { - return U_SUCCESS; - } - - // Parse as generic image - return parseGenericImage(image, capsuleHeaderSize, index, imageIndex); + + // Try parsing as some image + return parseImage(capsule.mid(capsuleHeaderSize), capsuleHeaderSize, index, imageIndex); } return U_ITEM_NOT_FOUND; @@ -306,10 +375,12 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l const FLASH_DESCRIPTOR_UPPER_MAP* upperMap = (const FLASH_DESCRIPTOR_UPPER_MAP*)((UINT8*)descriptor + FLASH_DESCRIPTOR_UPPER_MAP_BASE); // Check sanity of base values - if (descriptorMap->MasterBase > FLASH_DESCRIPTOR_MAX_BASE - || descriptorMap->MasterBase == descriptorMap->RegionBase - || descriptorMap->MasterBase == descriptorMap->ComponentBase) { - msg(usprintf("%s: invalid descriptor master base %02Xh", __FUNCTION__, descriptorMap->MasterBase)); + UINT32 masterBase = descriptorMap->MasterBase; + if (masterBase > FLASH_DESCRIPTOR_MAX_BASE) + masterBase = 8; + if (masterBase == descriptorMap->RegionBase + || masterBase == descriptorMap->ComponentBase) { + msg(usprintf("%s: invalid descriptor master base %02Xh", __FUNCTION__, masterBase)); return U_INVALID_FLASH_DESCRIPTOR; } if (descriptorMap->RegionBase > FLASH_DESCRIPTOR_MAX_BASE @@ -346,10 +417,13 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l + itemSubtypeToUString(Types::Region, me.type) + UString(" region is located outside of the opened image. If your system uses dual-chip storage, please append another part to the opened image"), index); - return U_TRUNCATED_IMAGE; + if ((UINT32)intelImage.size() > me.offset) + return U_TRUNCATED_IMAGE; + } + else { + me.data = intelImage.mid(me.offset, me.length); + regions.push_back(me); } - me.data = intelImage.mid(me.offset, me.length); - regions.push_back(me); } // BIOS region @@ -377,6 +451,7 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l index); return U_TRUNCATED_IMAGE; } + biosRegionInfo = bios; bios.data = intelImage.mid(bios.offset, bios.length); regions.push_back(bios); } @@ -486,11 +561,14 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l imageBase = model->base(parent) + localOffset; // Add Intel image tree item - index = model->addItem(localOffset, Types::Image, Subtypes::IntelImage, name, UString(), info, UByteArray(), intelImage, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Image, Subtypes::IntelImage, + name, UString(), info, + 0, intelImage.size(), 0, + Fixed, parent); // Descriptor // Get descriptor info - UByteArray body = intelImage.left(FLASH_DESCRIPTOR_SIZE); name = UString("Descriptor region"); info = usprintf("ReservedVector:\n%02X %02X %02X %02X %02X %02X %02X %02X\n" "%02X %02X %02X %02X %02X %02X %02X %02X\nFull size: %Xh (%u)", @@ -509,7 +587,7 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l // Region access settings if (descriptorVersion == 1) { - const FLASH_DESCRIPTOR_MASTER_SECTION* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION*)calculateAddress8((UINT8*)descriptor, descriptorMap->MasterBase); + const FLASH_DESCRIPTOR_MASTER_SECTION* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION*)calculateAddress8((UINT8*)descriptor, masterBase); info += UString("\nRegion access settings:"); info += usprintf("\nBIOS: %02Xh %02Xh ME: %02Xh %02Xh\nGbE: %02Xh %02Xh", masterSection->BiosRead, @@ -533,7 +611,7 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No "); } else if (descriptorVersion == 2) { - const FLASH_DESCRIPTOR_MASTER_SECTION_V2* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION_V2*)calculateAddress8((UINT8*)descriptor, descriptorMap->MasterBase); + const FLASH_DESCRIPTOR_MASTER_SECTION_V2* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION_V2*)calculateAddress8((UINT8*)descriptor, masterBase); info += UString("\nRegion access settings:"); info += usprintf("\nBIOS: %03Xh %03Xh ME: %03Xh %03Xh\nGbE: %03Xh %03Xh EC: %03Xh %03Xh", masterSection->BiosRead, @@ -594,7 +672,11 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l } // Add descriptor tree item - UModelIndex regionIndex = model->addItem(localOffset, Types::Region, Subtypes::DescriptorRegion, name, UString(), info, UByteArray(), body, UByteArray(), Fixed, index); + UModelIndex regionIndex = model->addItem( + localOffset, Types::Region, Subtypes::DescriptorRegion, + name, UString(), info, + 0, FLASH_DESCRIPTOR_SIZE, 0, + Fixed, index); // Parse regions USTATUS result = U_SUCCESS; @@ -633,15 +715,20 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l case Subtypes::OnePadding: case Subtypes::DataPadding: { // Add padding between regions - UByteArray padding = intelImage.mid(region.offset, region.length); + UINT32 paddingSize = region.offset + region.length > intelImage.size() + ? intelImage.size() - region.offset : region.length; // Get info name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", - (UINT32)padding.size(), (UINT32)padding.size()); + info = usprintf("Full size: %Xh (%u)", paddingSize, paddingSize); // Add tree item - regionIndex = model->addItem(region.offset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index); + UByteArray padding = intelImage.mid(region.offset, paddingSize); + regionIndex = model->addItem( + region.offset, Types::Padding, getPaddingType(padding), + name, UString(), info, + 0, paddingSize, 0, + Fixed, index); result = U_SUCCESS; } break; default: @@ -677,7 +764,11 @@ USTATUS FfsParser::parseGbeRegion(const UByteArray & gbe, const UINT32 localOffs version->minor); // Add tree item - index = model->addItem(localOffset, Types::Region, Subtypes::GbeRegion, name, UString(), info, UByteArray(), gbe, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Region, Subtypes::GbeRegion, + name, UString(), info, + 0, gbe.size(), 0, + Fixed, parent); return U_SUCCESS; } @@ -696,10 +787,11 @@ USTATUS FfsParser::parseMeRegion(const UByteArray & me, const UINT32 localOffset bool versionFound = true; bool emptyRegion = false; // Check for empty region - if (me.size() == me.count('\xFF') || me.size() == me.count('\x00')) { + auto c = checkSingle(me); + if (c >= 0) { // Further parsing not needed emptyRegion = true; - info += ("\nState: empty"); + info += usprintf("\nState: empty (0x%02X)", c); } else { // Search for new signature @@ -716,11 +808,13 @@ USTATUS FfsParser::parseMeRegion(const UByteArray & me, const UINT32 localOffset versionFound = false; } } - + +#if 0 // We need ME region to be shown even if it's version/structure is unknown - so we still can save ME binary part from UI // Check sanity if ((UINT32)me.size() < (UINT32)versionOffset + sizeof(ME_VERSION)) return U_INVALID_REGION; - +#endif + // Add version information if (versionFound) { const ME_VERSION* version = (const ME_VERSION*)(me.constData() + versionOffset); @@ -731,9 +825,13 @@ USTATUS FfsParser::parseMeRegion(const UByteArray & me, const UINT32 localOffset version->Build); } } - + // Add tree item - index = model->addItem(localOffset, Types::Region, Subtypes::MeRegion, name, UString(), info, UByteArray(), me, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Region, Subtypes::MeRegion, + name, UString(), info, + 0, me.size(), 0, + Fixed, parent); // Show messages if (emptyRegion) { @@ -759,8 +857,21 @@ USTATUS FfsParser::parsePdrRegion(const UByteArray & pdr, const UINT32 localOffs UString name("PDR region"); UString info = usprintf("Full size: %Xh (%u)", (UINT32)pdr.size(), (UINT32)pdr.size()); + bool emptyRegion = false; + // Check for empty region + auto c = checkSingle(pdr); + if (c >= 0) { + // Further parsing not needed + emptyRegion = true; + info += usprintf("\nState: empty (0x%02X)", c); + } + // Add tree item - index = model->addItem(localOffset, Types::Region, Subtypes::PdrRegion, name, UString(), info, UByteArray(), pdr, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Region, Subtypes::PdrRegion, + name, UString(), info, + 0, pdr.size(), 0, + Fixed, parent); // Parse PDR region as BIOS space USTATUS result = parseRawArea(index); @@ -782,14 +893,19 @@ USTATUS FfsParser::parseDevExp1Region(const UByteArray & devExp1, const UINT32 l bool emptyRegion = false; // Check for empty region - if (devExp1.size() == devExp1.count('\xFF') || devExp1.size() == devExp1.count('\x00')) { + auto c = checkSingle(devExp1); + if (c >= 0) { // Further parsing not needed emptyRegion = true; - info += ("\nState: empty"); + info += usprintf("\nState: empty (0x%02X)", c); } // Add tree item - index = model->addItem(localOffset, Types::Region, Subtypes::DevExp1Region, name, UString(), info, UByteArray(), devExp1, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Region, Subtypes::DevExp1Region, + name, UString(), info, + 0, devExp1.size(), 0, + Fixed, parent); if (!emptyRegion) { meParser->parseMeRegionBody(index); @@ -807,8 +923,21 @@ USTATUS FfsParser::parseGenericRegion(const UINT8 subtype, const UByteArray & re UString name = itemSubtypeToUString(Types::Region, subtype) + UString(" region"); UString info = usprintf("Full size: %Xh (%u)", (UINT32)region.size(), (UINT32)region.size()); + bool emptyRegion = false; + // Check for empty region + auto c = checkSingle(region); + if (c >= 0) { + // Further parsing not needed + emptyRegion = true; + info += usprintf("\nState: empty (0x%02X)", c); + } + // Add tree item - index = model->addItem(localOffset, Types::Region, subtype, name, UString(), info, UByteArray(), region, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Region, subtype, + name, UString(), info, + 0, region.size(), 0, + Fixed, parent); return U_SUCCESS; } @@ -824,7 +953,11 @@ USTATUS FfsParser::parseBiosRegion(const UByteArray & bios, const UINT32 localOf UString info = usprintf("Full size: %Xh (%u)", (UINT32)bios.size(), (UINT32)bios.size()); // Add tree item - index = model->addItem(localOffset, Types::Region, Subtypes::BiosRegion, name, UString(), info, UByteArray(), bios, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Region, Subtypes::BiosRegion, + name, UString(), info, + 0, bios.size(), 0, + Fixed, parent); return parseRawArea(index); } @@ -837,7 +970,8 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) // Get item data UByteArray data = model->body(index); - UINT32 headerSize = (UINT32)model->header(index).size(); + UINT32 headerSize = (UINT32)model->headerSize(index); + UINT32 bodySize; USTATUS result; UString name; @@ -852,7 +986,7 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) result = findNextRawAreaItem(index, 0, prevItemType, prevItemOffset, prevItemSize, prevItemAltSize); if (result) { // No need to parse further - return U_SUCCESS; + return U_STORES_NOT_FOUND; // it was U_SUCCESS in stock, but we need to know if something went wrong } // Set base of protected regions to be the first volume @@ -864,12 +998,17 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) // First item is not at the beginning of this raw area if (prevItemOffset > 0) { // Get info - UByteArray padding = data.left(prevItemOffset); + UINT32 paddingSize = prevItemOffset > data.size() ? data.size() : prevItemOffset; name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size()); + info = usprintf("Full size: %Xh (%u)", paddingSize, paddingSize); // Add tree item - model->addItem(headerSize, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index); + UByteArray padding = data.left(paddingSize); + model->addItem( + headerSize, Types::Padding, getPaddingType(padding), + name, UString(), info, + 0, paddingSize, 0, + Fixed, index); } // Search for and parse all items @@ -882,33 +1021,43 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) // Padding between items if (itemOffset > prevItemOffset + prevItemSize) { UINT32 paddingOffset = prevItemOffset + prevItemSize; - UINT32 paddingSize = itemOffset - paddingOffset; - UByteArray padding = data.mid(paddingOffset, paddingSize); + UINT32 paddingSize = itemOffset > data.size() ? data.size() - paddingOffset : itemOffset - paddingOffset; // Get info name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size()); + info = usprintf("Full size: %Xh (%u)", paddingSize, paddingSize); // Add tree item - model->addItem(headerSize + paddingOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index); + UByteArray padding = data.mid(paddingOffset, paddingSize); + model->addItem( + headerSize + paddingOffset, Types::Padding, getPaddingType(padding), + name, UString(), info, + 0, paddingSize, 0, + Fixed, index); } // Check that item is fully present in input if (itemSize > (UINT32)data.size() || itemOffset + itemSize > (UINT32)data.size()) { // Mark the rest as padding and finish parsing - UByteArray padding = data.mid(itemOffset); + UINT32 paddingSize = itemOffset > data.size() + ? 0 : data.size() - itemOffset; // Get info name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size()); + info = usprintf("Full size: %Xh (%u)", paddingSize, paddingSize); // Add tree item - UModelIndex paddingIndex = model->addItem(headerSize + itemOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index); + UByteArray padding = data.mid(paddingSize); + UModelIndex paddingIndex = model->addItem( + headerSize + paddingSize, Types::Padding, getPaddingType(padding), + name, UString(), info, + 0, paddingSize, 0, + Fixed, index); msg(usprintf("%s: one of objects inside overlaps the end of data", __FUNCTION__), paddingIndex); // Update variables prevItemOffset = itemOffset; - prevItemSize = (UINT32)padding.size(); + prevItemSize = paddingSize; break; } @@ -943,7 +1092,10 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) info = usprintf("Full size: %Xh (%u)", (UINT32)bpdtStore.size(), (UINT32)bpdtStore.size()); // Add tree item - UModelIndex bpdtIndex = model->addItem(headerSize + itemOffset, Types::BpdtStore, 0, name, UString(), info, UByteArray(), bpdtStore, UByteArray(), Fixed, index); + UModelIndex bpdtIndex = model->addItem(headerSize + itemOffset, Types::BpdtStore, 0, + name, UString(), info, + 0, bpdtStore.size(), 0, + Fixed, index); // Parse BPDT region UModelIndex bpdtPtIndex; @@ -960,16 +1112,16 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) insyde_fdm_t parsed(&ks); UINT32 storeSize = (UINT32)fdm.size(); - // Construct header and body - UByteArray header = fdm.left(parsed.data_offset()); - UByteArray body = fdm.mid(header.size(), storeSize - header.size()); + // Obtain header and body size + headerSize = parsed.data_offset(); + bodySize = storeSize - headerSize; // Add info UString name = UString("Insyde H2O FlashDeviceMap"); UString info = usprintf("Signature: HFDM\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nData offset: %Xh\nEntry size: %Xh (%u)\nEntry format: %02Xh\nRevision: %02Xh\nExtension count: %u\nFlash descriptor base address: %08Xh\nChecksum: %02Xh", storeSize, storeSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + headerSize, headerSize, + bodySize, bodySize, parsed.data_offset(), parsed.entry_size(), parsed.entry_size(), parsed.entry_format(), @@ -1004,7 +1156,11 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) } // Add header tree item - UModelIndex headerIndex = model->addItem(headerSize + itemOffset, Types::InsydeFlashDeviceMapStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); + UModelIndex headerIndex = model->addItem( + headerSize + itemOffset, Types::InsydeFlashDeviceMapStore, 0, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, index); // Add entries UINT32 entryOffset = parsed.data_offset(); @@ -1013,11 +1169,11 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) const EFI_GUID guid = readUnaligned((const EFI_GUID*)entry->guid().c_str()); name = insydeFlashDeviceMapEntryTypeGuidToUString(guid); UString text; - header = data.mid(itemOffset + entryOffset, sizeof(INSYDE_FLASH_DEVICE_MAP_ENTRY)); - body = data.mid(itemOffset + entryOffset + header.size(), parsed.entry_size() - header.size()); + headerSize = sizeof(INSYDE_FLASH_DEVICE_MAP_ENTRY); + bodySize = parsed.entry_size() - headerSize; // Add info - UINT32 entrySize = (UINT32)header.size() + (UINT32)body.size(); + UINT32 entrySize = headerSize + bodySize; info = UString("Region type: ") + guidToUString(guid, false) + "\n"; info += UString("Region id: "); for (UINT8 i = 0; i < 16; i++) { @@ -1025,8 +1181,8 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) } info += usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nRegion address: %08Xh\nRegion size: %08Xh\nAttributes: %08Xh", entrySize, entrySize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + headerSize, headerSize, + bodySize, bodySize, (UINT32)entry->region_base(), (UINT32)entry->region_size(), entry->attributes()); @@ -1045,14 +1201,18 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) range.Size = (UINT32)entry->region_size(); range.AlgorithmId = TCG_HASH_ALGORITHM_ID_SHA256; range.Type = PROTECTED_RANGE_VENDOR_HASH_INSYDE; - range.Hash = body; + range.Hash = data.mid(itemOffset + entryOffset + headerSize, bodySize); protectedRanges.push_back(range); - securityInfo += usprintf("Address: %08Xh Size: %Xh\nHash: ", range.Offset, range.Size) + UString(body.toHex().constData()) + "\n"; + securityInfo += usprintf("Address: %08Xh Size: %Xh\nHash: ", range.Offset, range.Size) + UString(range.Hash.toHex().constData()) + "\n"; } // Add tree item - model->addItem(entryOffset, Types::InsydeFlashDeviceMapEntry, 0, name, text, info, header, body, UByteArray(), Fixed, headerIndex); + model->addItem( + entryOffset, Types::InsydeFlashDeviceMapEntry, 0, + name, text, info, + headerSize, bodySize, 0, + Fixed, headerIndex); entryOffset += entrySize; } @@ -1082,14 +1242,19 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) // Padding at the end of RAW area itemOffset = prevItemOffset + prevItemSize; if ((UINT32)data.size() > itemOffset) { - UByteArray padding = data.mid(itemOffset); + UINT32 paddingSize = data.size() - itemOffset; // Get info name = UString("Padding"); - info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size()); + info = usprintf("Full size: %Xh (%u)", paddingSize, paddingSize); // Add tree item - model->addItem(headerSize + itemOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index); + UByteArray padding = data.mid(itemOffset); + model->addItem( + headerSize + itemOffset, Types::Padding, getPaddingType(padding), + name, UString(), info, + 0, paddingSize, 0, + Fixed, index); } // Parse bodies @@ -1109,9 +1274,6 @@ USTATUS FfsParser::parseRawArea(const UModelIndex & index) case Types::BpdtPartition: // Parsing already done break; - case Types::InsydeFlashDeviceMapStore: - // Parsing already done - break; case Types::Padding: // No parsing required break; @@ -1195,7 +1357,7 @@ USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 loc isMicrocodeVolume = true; headerSize = EFI_APPLE_MICROCODE_VOLUME_HEADER_SIZE; } - + // Check volume revision and alignment bool msgAlignmentBitsSet = false; bool msgUnaligned = false; @@ -1231,8 +1393,8 @@ USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 loc // Check for AppleCRC32 and UsedSpace in ZeroVector bool hasAppleCrc32 = false; UINT32 volumeSize = (UINT32)volume.size(); - UINT32 appleCrc32 = *(UINT32*)(volume.constData() + 8); - UINT32 usedSpace = *(UINT32*)(volume.constData() + 12); + const UINT32 appleCrc32 = *(const UINT32*)(volume.constData() + 8); + const UINT32 usedSpace = *(const UINT32*)(volume.constData() + 12); if (appleCrc32 != 0) { // Calculate CRC32 of the volume body UINT32 crc = (UINT32)crc32(0, (const UINT8*)(volume.constData() + volumeHeader->HeaderLength), volumeSize - volumeHeader->HeaderLength); @@ -1258,8 +1420,6 @@ USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 loc if (headerSize >= (UINT32)volume.size()) { return U_INVALID_VOLUME; } - UByteArray header = volume.left(headerSize); - UByteArray body = volume.mid(headerSize); UString name = guidToUString(volumeHeader->FileSystemGuid); UString info = usprintf("ZeroVector:\n%02X %02X %02X %02X %02X %02X %02X %02X\n" "%02X %02X %02X %02X %02X %02X %02X %02X\nSignature: _FVH\nFileSystem GUID: ", @@ -1288,7 +1448,28 @@ USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 loc extendedHeader->ExtHeaderSize, extendedHeader->ExtHeaderSize) + guidToUString(extendedHeader->FvName, false); name = guidToUString(extendedHeader->FvName); // Replace FFS GUID with volume GUID } - + + // Block size and blocks number + const EFI_FV_BLOCK_MAP_ENTRY* entry = (const EFI_FV_BLOCK_MAP_ENTRY*)(volume.constData() + sizeof(EFI_FIRMWARE_VOLUME_HEADER)); + UString infoNum = usprintf("Number of blocks: %Xh (%u)", entry->NumBlocks, entry->NumBlocks); + UString infoSize = usprintf("Block size: %Xh (%u)", entry->Length, entry->Length); + UINT32 volumeAltSize = entry->NumBlocks * entry->Length; + if (volumeSize != volumeAltSize) { + if (volumeAltSize % entry->Length == 0 && volumeSize % entry->Length == 0) { + infoNum += usprintf(", invalid, should be %Xh", volumeSize / entry->Length); + infoSize += ", valid"; + } + else if (volumeAltSize % entry->NumBlocks == 0 && volumeSize % entry->NumBlocks == 0) { + infoNum += ", valid"; + infoSize += usprintf(", invalid, should be %Xh", volumeSize / entry->NumBlocks); + } + } + else { + infoNum += ", valid"; + infoSize += ", valid"; + } + info += "\n" + infoNum + "\n" + infoSize; + // Add text UString text; if (hasAppleCrc32) @@ -1306,7 +1487,11 @@ USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 loc else if (isMicrocodeVolume) subtype = Subtypes::MicrocodeVolume; } - index = model->addItem(localOffset, Types::Volume, subtype, name, text, info, header, body, UByteArray(), Movable, parent); + index = model->addItem( + localOffset, Types::Volume, subtype, + name, text, info, + headerSize, volume.size() - headerSize, 0, + Movable, parent); // Set parsing data for created volume VOLUME_PARSING_DATA pdata = {}; @@ -1341,17 +1526,6 @@ bool FfsParser::microcodeHeaderValid(const INTEL_MICROCODE_HEADER* ucodeHeader) { bool reservedBytesValid = true; - // Check CpuFlags reserved bytes to be zero - for (UINT32 i = 0; i < sizeof(ucodeHeader->ProcessorFlagsReserved); i++) { - if (ucodeHeader->ProcessorFlagsReserved[i] != 0x00) { - reservedBytesValid = false; - break; - } - } - if (!reservedBytesValid) { - return false; - } - // Check data size to be multiple of 4 and less than 0x1000000 if (ucodeHeader->DataSize % 4 != 0 || ucodeHeader->DataSize > 0xFFFFFF) { @@ -1389,8 +1563,8 @@ bool FfsParser::microcodeHeaderValid(const INTEL_MICROCODE_HEADER* ucodeHeader) ucodeHeader->DateYear > 0x2049) { return FALSE; } - // Check HeaderVersion to be 1. - if (ucodeHeader->HeaderVersion != 1) { + // Check HeaderType to be 1. + if (ucodeHeader->HeaderType != 1) { return FALSE; } // Check LoaderRevision to be 1. @@ -1535,28 +1709,6 @@ continue_searching: {} nextItemOffset = offset; break; } - else if (readUnaligned(currentPos) == INSYDE_FLASH_DEVICE_MAP_SIGNATURE) { - // Check data size - if (restSize < sizeof(INSYDE_FLASH_DEVICE_MAP_HEADER)) - continue; - - const INSYDE_FLASH_DEVICE_MAP_HEADER *fdmHeader = (const INSYDE_FLASH_DEVICE_MAP_HEADER *)currentPos; - - if (restSize < fdmHeader->Size) - continue; - - if (fdmHeader->Revision > 4) { - msg(usprintf("%s: Insyde Flash Device Map candidate with unknown revision %u", __FUNCTION__, fdmHeader->Revision), index); - continue; - } - - // All checks passed, FDM found - nextItemType = Types::InsydeFlashDeviceMapStore; - nextItemSize = fdmHeader->Size; - nextItemAlternativeSize = fdmHeader->Size; - nextItemOffset = offset; - break; - } } // No more stores found @@ -1577,14 +1729,18 @@ USTATUS FfsParser::parseVolumeNonUefiData(const UByteArray & data, const UINT32 UString info = usprintf("Full size: %Xh (%u)", (UINT32)data.size(), (UINT32)data.size()); // Add padding tree item - UModelIndex paddingIndex = model->addItem(localOffset, Types::Padding, Subtypes::DataPadding, UString("Non-UEFI data"), UString(), info, UByteArray(), data, UByteArray(), Fixed, index); + UModelIndex paddingIndex = model->addItem( + localOffset, Types::Padding, Subtypes::DataPadding, + UString("Non-UEFI data"), UString(), + info, 0, data.size(), 0, + Fixed, index); msg(usprintf("%s: non-UEFI data found in volume's free space", __FUNCTION__), paddingIndex); // Parse contents as RAW area return parseRawArea(paddingIndex); } -USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) +USTATUS FfsParser::parseVolumeBody(const UModelIndex & index, const bool probe) { // Sanity check if (!index.isValid()) { @@ -1593,9 +1749,9 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) // Get volume header size and body UByteArray volumeBody = model->body(index); - UINT32 volumeHeaderSize = (UINT32)model->header(index).size(); + UINT32 volumeHeaderSize = (UINT32)model->headerSize(index); - // Parse NVRAM volume with a dedicated function + // Parse VSS NVRAM volumes with a dedicated function if (model->subtype(index) == Subtypes::NvramVolume) { return nvramParser->parseNvramVolumeBody(index); } @@ -1639,7 +1795,8 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) // Check that we are at the empty space UByteArray header = volumeBody.mid(fileOffset, (int)std::min(sizeof(EFI_FFS_FILE_HEADER), (size_t)volumeBodySize - fileOffset)); - if (header.count(emptyByte) == header.size()) { //Empty space + auto c = checkSingle(header, (unsigned char)emptyByte); + if (c == emptyByte) { //Empty space // Check volume usedSpace entry to be valid if (usedSpace > 0 && usedSpace == fileOffset + volumeHeaderSize) { if (model->hasEmptyParsingData(index) == false) { @@ -1653,11 +1810,12 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) // Check free space to be actually free UByteArray freeSpace = volumeBody.mid(fileOffset); - if (freeSpace.count(emptyByte) != freeSpace.size()) { + auto c = checkSingle(freeSpace, (unsigned char)emptyByte); + if (c != emptyByte) { // Search for the first non-empty byte UINT32 i; UINT32 size = (UINT32)freeSpace.size(); - const UINT8* current = (UINT8*)freeSpace.constData(); + const UINT8* current = (const UINT8*)freeSpace.constData(); for (i = 0; i < size; i++) { if (*current++ != emptyByte) { break; // Exit from parsing loop @@ -1672,13 +1830,19 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) // Add all bytes before as free space if (i > 0) { - UByteArray free = freeSpace.left(i); + if (probe) + return U_STORES_NOT_FOUND; + UINT32 freeSize = i > freeSpace.size() ? freeSpace.size() : i; // Get info - UString info = usprintf("Full size: %Xh (%u)", (UINT32)free.size(), (UINT32)free.size()); + UString info = usprintf("Full size: %Xh (%u)", freeSize, freeSize); // Add free space item - model->addItem(volumeHeaderSize + fileOffset, Types::FreeSpace, 0, UString("Volume free space"), UString(), info, UByteArray(), free, UByteArray(), Movable, index); + model->addItem( + volumeHeaderSize + fileOffset, Types::FreeSpace, 0, + UString("Volume free space"), UString(), info, + 0, freeSize, 0, + Movable, index); } // Parse non-UEFI data @@ -1689,7 +1853,11 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) UString info = usprintf("Full size: %Xh (%u)", (UINT32)freeSpace.size(), (UINT32)freeSpace.size()); // Add free space item - model->addItem(volumeHeaderSize + fileOffset, Types::FreeSpace, 0, UString("Volume free space"), UString(), info, UByteArray(), freeSpace, UByteArray(), Movable, index); + model->addItem( + volumeHeaderSize + fileOffset, Types::FreeSpace, 0, + UString("Volume free space"), UString(), info, + 0, freeSpace.size(), 0, + Movable, index); } break; // Exit from parsing loop @@ -1699,6 +1867,8 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) // Check that the remaining space can still have a file in it if (volumeBodySize - fileOffset < sizeof(EFI_FFS_FILE_HEADER) // Remaining space is smaller than the smallest possible file || volumeBodySize - fileOffset < fileSize) { // Remaining space is smaller than non-empty file size + if (probe) + return U_STORES_NOT_FOUND; // Parse non-UEFI data parseVolumeNonUefiData(volumeBody.mid(fileOffset), volumeHeaderSize + fileOffset, index); @@ -1707,7 +1877,7 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) // Parse current file's header UModelIndex fileIndex; - USTATUS result = parseFileHeader(volumeBody.mid(fileOffset, fileSize), volumeHeaderSize + fileOffset, index, fileIndex); + USTATUS result = parseFileHeader(volumeBody.mid(fileOffset, fileSize), volumeHeaderSize + fileOffset, index, fileIndex, probe); if (result) { msg(usprintf("%s: file header parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index); } @@ -1729,7 +1899,7 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) } // Get current file GUID - UByteArray currentGuid(model->header(current).constData(), sizeof(EFI_GUID)); + const UByteArray currentGuid(model->header(current).constData(), sizeof(EFI_GUID)); // Check files after current for having an equal GUID for (int j = i + 1; j < model->rowCount(index); j++) { @@ -1741,11 +1911,11 @@ USTATUS FfsParser::parseVolumeBody(const UModelIndex & index) } // Get another file GUID - UByteArray anotherGuid(model->header(another).constData(), sizeof(EFI_GUID)); + const UByteArray anotherGuid(model->header(another).constData(), sizeof(EFI_GUID)); // Check GUIDs for being equal if (currentGuid == anotherGuid) { - msg(usprintf("%s: file with duplicate GUID ", __FUNCTION__) + guidToUString(readUnaligned((EFI_GUID*)(anotherGuid.data()))), another); + msg(usprintf("%s: file with duplicate GUID ", __FUNCTION__) + guidToUString(readUnaligned((const EFI_GUID*)(anotherGuid.constData()))), another); } } } @@ -1808,7 +1978,7 @@ UINT32 FfsParser::getFileSize(const UByteArray & volume, const UINT32 fileOffset return 0; } -USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool probe) { // Sanity check if (file.isEmpty()) { @@ -1834,21 +2004,20 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf } // Get file header - UByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER)); - EFI_FFS_FILE_HEADER* tempFileHeader = (EFI_FFS_FILE_HEADER*)header.data(); - if (tempFileHeader->Attributes & FFS_ATTRIB_LARGE_FILE) { + UINT32 headerSize = sizeof(EFI_FFS_FILE_HEADER); + const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)file.constData(); + if (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE) { if (ffsVersion == 2 && volumeRevision == 2) { - if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER2_LENOVO)) + if (headerSize < sizeof(EFI_FFS_FILE_HEADER2_LENOVO)) return U_INVALID_FILE; - header = file.left(sizeof(EFI_FFS_FILE_HEADER2_LENOVO)); + headerSize = sizeof(EFI_FFS_FILE_HEADER2_LENOVO); } if (ffsVersion == 3) { - if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER2)) + if (headerSize < sizeof(EFI_FFS_FILE_HEADER2)) return U_INVALID_FILE; - header = file.left(sizeof(EFI_FFS_FILE_HEADER2)); + headerSize = sizeof(EFI_FFS_FILE_HEADER2); } } - const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)header.constData(); // Check file alignment bool msgUnalignedFile = false; @@ -1858,7 +2027,7 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf } UINT32 alignment = (UINT32)(1UL << alignmentPower); - if ((localOffset + header.size()) % alignment) { + if ((localOffset + headerSize) % alignment) { msgUnalignedFile = true; } @@ -1869,24 +2038,24 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf } // Get file body - UByteArray body = file.mid(header.size()); - + UINT32 bodySize = file.size() - headerSize; + UINT32 tailSize = 0; + // Check for file tail presence - UByteArray tail; bool msgInvalidTailValue = false; + UINT16 tailValue; if (volumeRevision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) { - //Check file tail; - UINT16 tailValue = *(UINT16*)body.right(sizeof(UINT16)).constData(); + // Get tail and remove it from file body + bodySize -= sizeof(UINT16); + tailSize += sizeof(UINT16); + // Check file tail; + tailValue = *(const UINT16*)(file.constData() + headerSize + bodySize); if (fileHeader->IntegrityCheck.TailReference != (UINT16)~tailValue) msgInvalidTailValue = true; - - // Get tail and remove it from file body - tail = body.right(sizeof(UINT16)); - body = body.left(body.size() - sizeof(UINT16)); } - + // Check header checksum - UINT8 calculatedHeader = 0x100 - (calculateSum8((const UINT8*)header.constData(), (UINT32)header.size()) - fileHeader->IntegrityCheck.Checksum.Header - fileHeader->IntegrityCheck.Checksum.File - fileHeader->State); + UINT8 calculatedHeader = 0x100 - (calculateSum8((const UINT8*)fileHeader, headerSize) - fileHeader->IntegrityCheck.Checksum.Header - fileHeader->IntegrityCheck.Checksum.File - fileHeader->State); bool msgInvalidHeaderChecksum = false; if (fileHeader->IntegrityCheck.Checksum.Header != calculatedHeader) { msgInvalidHeaderChecksum = true; @@ -1897,7 +2066,7 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf bool msgInvalidDataChecksum = false; UINT8 calculatedData = 0; if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { - calculatedData = calculateChecksum8((const UINT8*)body.constData(), (UINT32)body.size()); + calculatedData = calculateChecksum8((const UINT8*)(file.constData() + headerSize), bodySize); } // Data checksum must be one of predefined values else if (volumeRevision == 1) { @@ -1917,6 +2086,25 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf msgUnknownType = true; }; + // If probing, more strict error + if (probe) { + int errScores = 0; + if (msgUnalignedFile) + errScores += 1; + if (msgFileAlignmentIsGreaterThanVolumeAlignment) + errScores += 1; + if (msgInvalidHeaderChecksum) + errScores += 2; + if (msgInvalidDataChecksum) + errScores += 2; + if (msgInvalidTailValue) + errScores += 1; + if (msgUnknownType) + errScores += 3; + if (errScores > 2) + return U_INVALID_FILE; + } + // Get info UString name; UString info; @@ -1930,10 +2118,10 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf usprintf("\nType: %02Xh\nAttributes: %02Xh\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nTail size: %Xh (%u)\nState: %02Xh", fileHeader->Type, fileHeader->Attributes, - (UINT32)(header.size() + body.size() + tail.size()), (UINT32)(header.size() + body.size() + tail.size()), - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), - (UINT32)tail.size(), (UINT32)tail.size(), + headerSize + bodySize + tailSize, headerSize + bodySize + tailSize, + headerSize, headerSize, + bodySize, bodySize, + tailSize, tailSize, fileHeader->State) + usprintf("\nHeader checksum: %02Xh", fileHeader->IntegrityCheck.Checksum.Header) + (msgInvalidHeaderChecksum ? usprintf(", invalid, should be %02Xh", calculatedHeader) : UString(", valid")) + usprintf("\nData checksum: %02Xh", fileHeader->IntegrityCheck.Checksum.File) + (msgInvalidDataChecksum ? usprintf(", invalid, should be %02Xh", calculatedData) : UString(", valid")); @@ -1961,7 +2149,11 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf ItemFixedState fixed = (ItemFixedState)((fileHeader->Attributes & FFS_ATTRIB_FIXED) != 0); // Add tree item - index = model->addItem(localOffset, Types::File, fileHeader->Type, name, text, info, header, body, tail, fixed, parent); + index = model->addItem( + localOffset, Types::File, fileHeader->Type, + name, text, info, + headerSize, bodySize, tailSize, + fixed, parent); // Set parsing data for created file FILE_PARSING_DATA pdata = {}; @@ -1989,7 +2181,7 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf if (msgInvalidDataChecksum) msg(usprintf("%s: invalid data checksum %02Xh, should be %02Xh", __FUNCTION__, fileHeader->IntegrityCheck.Checksum.File, calculatedData), index); if (msgInvalidTailValue) - msg(usprintf("%s: invalid tail value %04Xh", __FUNCTION__, *(const UINT16*)tail.constData()), index); + msg(usprintf("%s: invalid tail value %04Xh", __FUNCTION__, tailValue), index); if (msgUnknownType) msg(usprintf("%s: unknown file type %02Xh", __FUNCTION__, fileHeader->Type), index); @@ -2038,7 +2230,7 @@ USTATUS FfsParser::parseFileBody(const UModelIndex & index) // Parse raw files as raw areas if (model->subtype(index) == EFI_FV_FILETYPE_RAW || model->subtype(index) == EFI_FV_FILETYPE_ALL) { - UByteArray fileGuid = UByteArray(model->header(index).constData(), sizeof(EFI_GUID)); + const UByteArray fileGuid(model->header(index).constData(), sizeof(EFI_GUID)); // Parse NVAR store if (fileGuid == NVRAM_NVAR_STORE_FILE_GUID) { @@ -2081,7 +2273,9 @@ USTATUS FfsParser::parseFileBody(const UModelIndex & index) return U_SUCCESS; } - return parseRawArea(index); + if (parseSections(model->body(index), index, false) != U_SUCCESS) { + return parseRawArea(index); + } } // Parse sections @@ -2107,7 +2301,8 @@ USTATUS FfsParser::parsePadFileBody(const UModelIndex & index) } // Check if the while padding file is empty - if (body.size() == body.count(emptyByte)) + auto c = checkSingle(body, (unsigned char)emptyByte); + if (c == emptyByte) return U_SUCCESS; // Search for the first non-empty byte @@ -2120,7 +2315,7 @@ USTATUS FfsParser::parsePadFileBody(const UModelIndex & index) } // Add all bytes before as free space... - UINT32 headerSize = (UINT32)model->header(index).size(); + UINT32 headerSize = (UINT32)model->headerSize(index); if (nonEmptyByteOffset >= 8) { // Align free space to 8 bytes boundary if (nonEmptyByteOffset != ALIGN8(nonEmptyByteOffset)) @@ -2132,7 +2327,11 @@ USTATUS FfsParser::parsePadFileBody(const UModelIndex & index) UString info = usprintf("Full size: %Xh (%u)", (UINT32)free.size(), (UINT32)free.size()); // Add tree item - model->addItem(headerSize, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), free, UByteArray(), Movable, index); + model->addItem( + headerSize, Types::FreeSpace, 0, + UString("Free space"), UString(), info, + 0, free.size(), 0, + Movable, index); } else { nonEmptyByteOffset = 0; @@ -2148,7 +2347,11 @@ USTATUS FfsParser::parsePadFileBody(const UModelIndex & index) UString info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size()); // Add tree item - (void)model->addItem(headerSize + nonEmptyByteOffset, Types::StartupApDataEntry, Subtypes::x86128kStartupApDataEntry, UString("Startup AP data"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index); + model->addItem( + headerSize + nonEmptyByteOffset, Types::StartupApDataEntry, Subtypes::x86128kStartupApDataEntry, + UString("Startup AP data"), UString(), info, + 0, padding.size(), 0, + Fixed, index); // Rename the file model->setName(index, UString("Startup AP data padding file")); @@ -2161,7 +2364,11 @@ USTATUS FfsParser::parsePadFileBody(const UModelIndex & index) UString info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size()); // Add tree item - UModelIndex dataIndex = model->addItem(headerSize + nonEmptyByteOffset, Types::Padding, Subtypes::DataPadding, UString("Non-UEFI data"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index); + UModelIndex dataIndex = model->addItem( + headerSize + nonEmptyByteOffset, Types::Padding, Subtypes::DataPadding, + UString("Non-UEFI data"), UString(), info, + 0, padding.size(), 0, + Fixed, index); // Show message msg(usprintf("%s: non-UEFI data found in padding file", __FUNCTION__), dataIndex); @@ -2184,7 +2391,7 @@ USTATUS FfsParser::parseSections(const UByteArray & sections, const UModelIndex // Search for and parse all sections UINT32 bodySize = (UINT32)sections.size(); - UINT32 headerSize = (UINT32)model->header(index).size(); + UINT32 headerSize = (UINT32)model->headerSize(index); UINT32 sectionOffset = 0; USTATUS result = U_SUCCESS; @@ -2209,13 +2416,17 @@ USTATUS FfsParser::parseSections(const UByteArray & sections, const UModelIndex // Final parsing if (insertIntoTree) { // Add padding to fill the rest of sections - UByteArray padding = sections.mid(sectionOffset); + UINT32 paddingSize = sections.size() - sectionOffset; // Get info - UString info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size()); + UString info = usprintf("Full size: %Xh (%u)", paddingSize, paddingSize); // Add tree item - UModelIndex dataIndex = model->addItem(headerSize + sectionOffset, Types::Padding, Subtypes::DataPadding, UString("Non-UEFI data"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index); + UModelIndex dataIndex = model->addItem( + headerSize + sectionOffset, Types::Padding, Subtypes::DataPadding, + UString("Non-UEFI data"), UString(), info, + 0, paddingSize, 0, + Fixed, index); // Show message msg(usprintf("%s: non-UEFI data found in sections area", __FUNCTION__), dataIndex); @@ -2315,7 +2526,8 @@ USTATUS FfsParser::parseSectionHeader(const UByteArray & section, const UINT32 l // Unknown default: USTATUS result = parseCommonSectionHeader(section, localOffset, parent, index, insertIntoTree); - msg(usprintf("%s: section with unknown type %02Xh", __FUNCTION__, sectionHeader->Type), index); + if (insertIntoTree) + msg(usprintf("%s: section with unknown type %02Xh", __FUNCTION__, sectionHeader->Type), index); return result; } } @@ -2348,8 +2560,7 @@ USTATUS FfsParser::parseCommonSectionHeader(const UByteArray & section, const UI return U_INVALID_SECTION; } - UByteArray header = section.left(headerSize); - UByteArray body = section.mid(headerSize); + UINT32 bodySize = section.size() - headerSize; // Get info UString name = sectionTypeToUString(type) + UString(" section"); @@ -2357,11 +2568,15 @@ USTATUS FfsParser::parseCommonSectionHeader(const UByteArray & section, const UI type, (UINT32)section.size(), (UINT32)section.size(), headerSize, headerSize, - (UINT32)body.size(), (UINT32)body.size()); + bodySize, bodySize); // Add tree item if (insertIntoTree) { - index = model->addItem(localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem( + localOffset, Types::Section, type, + name, UString(), info, + headerSize, bodySize, 0, + Movable, parent); } return U_SUCCESS; @@ -2409,8 +2624,7 @@ USTATUS FfsParser::parseCompressedSectionHeader(const UByteArray & section, cons return U_INVALID_SECTION; } - UByteArray header = section.left(headerSize); - UByteArray body = section.mid(headerSize); + UINT32 bodySize = section.size() - headerSize; // Get info UString name = sectionTypeToUString(sectionHeader->Type) + UString(" section"); @@ -2418,13 +2632,17 @@ USTATUS FfsParser::parseCompressedSectionHeader(const UByteArray & section, cons sectionHeader->Type, (UINT32)section.size(), (UINT32)section.size(), headerSize, headerSize, - (UINT32)body.size(), (UINT32)body.size(), + bodySize, bodySize, compressionType, uncompressedLength, uncompressedLength); // Add tree item if (insertIntoTree) { - index = model->addItem(localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem( + localOffset, Types::Section, sectionHeader->Type, + name, UString(), info, + headerSize, bodySize, 0, + Movable, parent); // Set section parsing data COMPRESSED_SECTION_PARSING_DATA pdata = {}; @@ -2514,7 +2732,6 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI } else if (baGuid == EFI_GUIDED_SECTION_LZMA || baGuid == EFI_GUIDED_SECTION_LZMA_HP - || baGuid == EFI_GUIDED_SECTION_LZMA_MS || baGuid == EFI_GUIDED_SECTION_LZMAF86 || baGuid == EFI_GUIDED_SECTION_TIANO || baGuid == EFI_GUIDED_SECTION_GZIP) { @@ -2602,8 +2819,7 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI msgProcessingRequiredAttributeOnUnknownGuidedSection = true; } - UByteArray header = section.left(dataOffset); - UByteArray body = section.mid(dataOffset); + UINT32 bodySize = section.size() - dataOffset; // Get info UString name = guidToUString(guid); @@ -2611,8 +2827,8 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI usprintf("\nType: %02Xh\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nAttributes: %04Xh", sectionHeader->Type, (UINT32)section.size(), (UINT32)section.size(), - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + dataOffset, dataOffset, + bodySize, bodySize, attributes); // Append additional info @@ -2620,7 +2836,11 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI // Add tree item if (insertIntoTree) { - index = model->addItem(localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem( + localOffset, Types::Section, sectionHeader->Type, + name, UString(), info, + dataOffset, bodySize, 0, + Movable, parent); // Set parsing data GUIDED_SECTION_PARSING_DATA pdata = {}; @@ -2692,21 +2912,24 @@ USTATUS FfsParser::parseFreeformGuidedSectionHeader(const UByteArray & section, if ((UINT32)section.size() < headerSize) return U_INVALID_SECTION; - UByteArray header = section.left(headerSize); - UByteArray body = section.mid(headerSize); + UINT32 bodySize = section.size() - headerSize; // Get info UString name = sectionTypeToUString(type) + (" section"); UString info = usprintf("Type: %02Xh\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nSubtype GUID: ", type, (UINT32)section.size(), (UINT32)section.size(), - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size()) + headerSize, headerSize, + bodySize, bodySize) + guidToUString(guid, false); // Add tree item if (insertIntoTree) { - index = model->addItem(localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem( + localOffset, Types::Section, type, + name, UString(), info, + headerSize, bodySize, 0, + Movable, parent); // Set parsing data FREEFORM_GUIDED_SECTION_PARSING_DATA pdata = {}; @@ -2759,21 +2982,24 @@ USTATUS FfsParser::parseVersionSectionHeader(const UByteArray & section, const U if ((UINT32)section.size() < headerSize) return U_INVALID_SECTION; - UByteArray header = section.left(headerSize); - UByteArray body = section.mid(headerSize); + UINT32 bodySize = section.size() - headerSize; // Get info UString name = sectionTypeToUString(type) + (" section"); UString info = usprintf("Type: %02Xh\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nBuild number: %u", type, (UINT32)section.size(), (UINT32)section.size(), - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + headerSize, headerSize, + bodySize, bodySize, buildNumber); // Add tree item if (insertIntoTree) { - index = model->addItem(localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem( + localOffset, Types::Section, type, + name, UString(), info, + headerSize, bodySize, 0, + Movable, parent); } return U_SUCCESS; @@ -2818,21 +3044,24 @@ USTATUS FfsParser::parsePostcodeSectionHeader(const UByteArray & section, const if ((UINT32)section.size() < headerSize) return U_INVALID_SECTION; - UByteArray header = section.left(headerSize); - UByteArray body = section.mid(headerSize); + UINT32 bodySize = section.size() - headerSize; // Get info UString name = sectionTypeToUString(type) + (" section"); UString info = usprintf("Type: %02Xh\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nPostcode: %Xh", type, (UINT32)section.size(), (UINT32)section.size(), - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + headerSize, headerSize, + bodySize, bodySize, postCode); // Add tree item if (insertIntoTree) { - index = model->addItem(localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent); + index = model->addItem( + localOffset, Types::Section, sectionHeader->Type, + name, UString(), info, + headerSize, bodySize, 0, + Movable, parent); } return U_SUCCESS; @@ -2883,7 +3112,7 @@ USTATUS FfsParser::parseCompressedSectionBody(const UModelIndex & index) // Obtain required information from parsing data UINT8 compressionType = EFI_NOT_COMPRESSED; - UINT32 uncompressedSize = (UINT32)model->body(index).size(); + UINT32 uncompressedSize = (UINT32)model->bodySize(index); if (model->hasEmptyParsingData(index) == false) { UByteArray data = model->parsingData(index); const COMPRESSED_SECTION_PARSING_DATA* pdata = (const COMPRESSED_SECTION_PARSING_DATA*)data.constData(); @@ -3004,8 +3233,7 @@ USTATUS FfsParser::parseGuidedSectionBody(const UModelIndex & index) } // LZMA compressed section else if (baGuid == EFI_GUIDED_SECTION_LZMA - || baGuid == EFI_GUIDED_SECTION_LZMA_HP - || baGuid == EFI_GUIDED_SECTION_LZMA_MS) { + || baGuid == EFI_GUIDED_SECTION_LZMA_HP) { USTATUS result = decompress(model->body(index), EFI_CUSTOMIZED_COMPRESSION, algorithm, dictionarySize, processed, efiDecompressed); if (result) { msg(usprintf("%s: decompression failed with error ", __FUNCTION__) + errorCodeToUString(result), index); @@ -3303,13 +3531,28 @@ USTATUS FfsParser::parseRawSectionBody(const UModelIndex & index) // Parse AMI vendor hash file return parseVendorHashFile(parentFileGuid, index); } - + else if (nvramParser->parseNvarStore(index, true) == U_SUCCESS) { + // Rename parent file + model->setName(index, UString(sectionTypeToUString(EFI_SECTION_RAW) + " section with NVAR store")); + return U_SUCCESS; + } + else if (parsePeImageSectionBody(index, true) == U_SUCCESS) { + // Rename parent file + model->setName(index, UString(sectionTypeToUString(EFI_SECTION_RAW) + " section with PE32 image")); + return U_SUCCESS; + } + else if (parseTeImageSectionBody(index, true) == U_SUCCESS) { + // Rename parent file + model->setName(index, UString(sectionTypeToUString(EFI_SECTION_RAW) + " section with TE image")); + return U_SUCCESS; + } + // Parse as raw area return parseRawArea(index); } -USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index) +USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index, const bool probe) { // Sanity check if (!index.isValid()) @@ -3318,6 +3561,8 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index) // Get section body UByteArray body = model->body(index); if ((UINT32)body.size() < sizeof(EFI_IMAGE_DOS_HEADER)) { + if (probe) + return U_INVALID_PE_HEADER; msg(usprintf("%s: section body size is smaller than DOS header size", __FUNCTION__), index); return U_SUCCESS; } @@ -3325,6 +3570,8 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index) UString info; const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)body.constData(); if (dosHeader->e_magic != EFI_IMAGE_DOS_SIGNATURE) { + if (probe) + return U_INVALID_PE_HEADER; info += usprintf("\nDOS signature: %04Xh, invalid", dosHeader->e_magic); msg(usprintf("%s: PE32 image with invalid DOS signature", __FUNCTION__), index); model->addInfo(index, info); @@ -3333,6 +3580,8 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index) const EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(body.constData() + dosHeader->e_lfanew); if (body.size() < (UINT8*)peHeader - (UINT8*)dosHeader) { + if (probe) + return U_INVALID_PE_HEADER; info += UString("\nDOS header: invalid"); msg(usprintf("%s: PE32 image with invalid DOS header", __FUNCTION__), index); model->addInfo(index, info); @@ -3340,6 +3589,8 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index) } if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) { + if (probe) + return U_INVALID_PE_HEADER; info += usprintf("\nPE signature: %08Xh, invalid", peHeader->Signature); msg(usprintf("%s: PE32 image with invalid PE signature", __FUNCTION__), index); model->addInfo(index, info); @@ -3348,6 +3599,8 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index) const EFI_IMAGE_FILE_HEADER* imageFileHeader = (const EFI_IMAGE_FILE_HEADER*)(peHeader + 1); if (body.size() < (UINT8*)imageFileHeader - (UINT8*)dosHeader) { + if (probe) + return U_INVALID_PE_HEADER; info += UString("\nPE header: invalid"); msg(usprintf("%s: PE32 image with invalid PE header", __FUNCTION__), index); model->addInfo(index, info); @@ -3365,6 +3618,8 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index) EFI_IMAGE_OPTIONAL_HEADER_POINTERS_UNION optionalHeader = {}; optionalHeader.H32 = (const EFI_IMAGE_OPTIONAL_HEADER32*)(imageFileHeader + 1); if (body.size() < (UINT8*)optionalHeader.H32 - (UINT8*)dosHeader) { + if (probe) + return U_INVALID_PE_HEADER; info += UString("\nPE optional header: invalid"); msg(usprintf("%s: PE32 image with invalid PE optional header", __FUNCTION__), index); model->addInfo(index, info); @@ -3378,6 +3633,11 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index) optionalHeader.H32->AddressOfEntryPoint, optionalHeader.H32->BaseOfCode, optionalHeader.H32->ImageBase); + if (!model->compressed(index)) { + UINT64 addr = (UINT64)optionalHeader.H32->ImageBase - model->base(index) - model->headerSize(index); + if (addr > UEFI_LOWER_INVALID_ADDRESS && addr < UEFI_UPPER_INVALID_ADDRESS) + baseAddressesMap[addr]++; + } } else if (optionalHeader.H32->Magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { info += usprintf("\nOptional header signature: %04Xh\nSubsystem: %04Xh\nAddress of entry point: %Xh\nBase of code: %Xh\nImage base: %" PRIX64 "h", @@ -3386,8 +3646,15 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index) optionalHeader.H64->AddressOfEntryPoint, optionalHeader.H64->BaseOfCode, optionalHeader.H64->ImageBase); + if (!model->compressed(index)) { + UINT64 addr = (UINT64)optionalHeader.H32->ImageBase - model->base(index) - model->headerSize(index); + if (addr > UEFI_LOWER_INVALID_ADDRESS && addr < UEFI_UPPER_INVALID_ADDRESS) + baseAddressesMap[addr]++; + } } else { + if (probe) + return U_INVALID_PE_HEADER; info += usprintf("\nOptional header signature: %04Xh, unknown", optionalHeader.H32->Magic); msg(usprintf("%s: PE32 image with invalid optional PE header signature", __FUNCTION__), index); } @@ -3397,7 +3664,7 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index) } -USTATUS FfsParser::parseTeImageSectionBody(const UModelIndex & index) +USTATUS FfsParser::parseTeImageSectionBody(const UModelIndex & index, const bool probe) { // Check sanity if (!index.isValid()) @@ -3406,6 +3673,8 @@ USTATUS FfsParser::parseTeImageSectionBody(const UModelIndex & index) // Get section body UByteArray body = model->body(index); if ((UINT32)body.size() < sizeof(EFI_IMAGE_TE_HEADER)) { + if (probe) + return U_INVALID_TE_HEADER; msg(usprintf("%s: section body size is smaller than TE header size", __FUNCTION__), index); return U_SUCCESS; } @@ -3413,6 +3682,8 @@ USTATUS FfsParser::parseTeImageSectionBody(const UModelIndex & index) UString info; const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)body.constData(); if (teHeader->Signature != EFI_IMAGE_TE_SIGNATURE) { + if (probe) + return U_INVALID_TE_HEADER; info += usprintf("\nSignature: %04Xh, invalid", teHeader->Signature); msg(usprintf("%s: TE image with invalid TE signature", __FUNCTION__), index); } @@ -3428,6 +3699,12 @@ USTATUS FfsParser::parseTeImageSectionBody(const UModelIndex & index) teHeader->AddressOfEntryPoint, teHeader->ImageBase, teHeader->ImageBase + teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER)); + if (!model->compressed(index)) { + UINT64 addr = (UINT64)teHeader->ImageBase + teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER) + - model->base(index) - model->headerSize(index); + if (addr > UEFI_LOWER_INVALID_ADDRESS && addr < UEFI_UPPER_INVALID_ADDRESS) + baseAddressesMap[addr]++; + } } // Update parsing data @@ -3457,8 +3734,9 @@ USTATUS FfsParser::performSecondPass(const UModelIndex & index) } // Calculate address difference - const UINT32 vtfSize = (UINT32)(model->header(lastVtf).size() + model->body(lastVtf).size() + model->tail(lastVtf).size()); - addressDiff = 0xFFFFFFFFULL - model->base(lastVtf) - vtfSize + 1; + const UINT32 vtfSize = (UINT32)(model->headerSize(lastVtf) + model->bodySize(lastVtf) + model->tailSize(lastVtf)); + if (addressDiff >= UEFI_UPPER_INVALID_ADDRESS) + addressDiff = UEFI_UPPER_INVALID_ADDRESS - model->base(lastVtf) - vtfSize; // Parse reset vector data parseResetVectorData(); @@ -3531,7 +3809,7 @@ USTATUS FfsParser::checkTeImageBase(const UModelIndex & index) if (originalImageBase != 0 || adjustedImageBase != 0) { // Check data memory address to be equal to either OriginalImageBase or AdjustedImageBase UINT64 address = addressDiff + model->base(index); - UINT32 base = (UINT32)(address + model->header(index).size()); + UINT32 base = (UINT32)(address + model->headerSize(index)); if (originalImageBase == base) { imageBaseType = EFI_IMAGE_TE_BASE_ORIGINAL; @@ -3586,11 +3864,14 @@ USTATUS FfsParser::addInfoRecursive(const UModelIndex & index) // Add current base if the element is not compressed // or it's compressed, but its parent isn't - if ((!model->compressed(index)) || (index.parent().isValid() && !model->compressed(index.parent()))) { + if (!model->compressed(index) || (index.parent().isValid() && !model->compressed(index.parent()))) { // Add physical address of the whole item or its header and data portions separately UINT64 address = addressDiff + model->base(index); - if (address <= 0xFFFFFFFFUL) { - UINT32 headerSize = (UINT32)model->header(index).size(); + if (model->type(index) != Types::Capsule && address < UEFI_UPPER_INVALID_ADDRESS + && ((biosRegionInfo.type != Subtypes::BiosRegion && address >= UEFI_LOWER_INVALID_ADDRESS) + || (biosRegionInfo.type == Subtypes::BiosRegion && address >= (UEFI_UPPER_INVALID_ADDRESS - biosRegionInfo.length)))) + { + UINT32 headerSize = (UINT32)model->headerSize(index); if (headerSize) { model->addInfo(index, usprintf("Data address: %08Xh\n", (UINT32)address + headerSize),false); model->addInfo(index, usprintf("Header address: %08Xh\n", (UINT32)address), false); @@ -3700,7 +3981,7 @@ USTATUS FfsParser::checkProtectedRanges(const UModelIndex & index) else { try { protectedRanges[i].Offset = model->base(dxeRootVolumeIndex); - protectedRanges[i].Size = (UINT32)(model->header(dxeRootVolumeIndex).size() + model->body(dxeRootVolumeIndex).size() + model->tail(dxeRootVolumeIndex).size()); + protectedRanges[i].Size = (UINT32)(model->headerSize(dxeRootVolumeIndex) + model->bodySize(dxeRootVolumeIndex) + model->tailSize(dxeRootVolumeIndex)); protectedParts = openedImage.mid(protectedRanges[i].Offset, protectedRanges[i].Size); // Calculate the hash @@ -3908,26 +4189,6 @@ USTATUS FfsParser::checkProtectedRanges(const UModelIndex & index) // Do nothing, this range is likely not found in the image } } - else if (protectedRanges[i].Type == PROTECTED_RANGE_VENDOR_HASH_INSYDE) { - try { - protectedRanges[i].Offset -= (UINT32)addressDiff; - protectedParts = openedImage.mid(protectedRanges[i].Offset, protectedRanges[i].Size); - - UByteArray digest(SHA256_HASH_SIZE, '\x00'); - sha256(protectedParts.constData(), protectedParts.size(), digest.data()); - - if (digest != protectedRanges[i].Hash) { - msg(usprintf("%s: Insyde protected range [%Xh:%Xh] hash mismatch, opened image may refuse to boot", __FUNCTION__, - protectedRanges[i].Offset, protectedRanges[i].Offset + protectedRanges[i].Size), - model->findByBase(protectedRanges[i].Offset)); - } - - markProtectedRangeRecursive(index, protectedRanges[i]); - } - catch(...) { - // Do nothing, this range is likely not found in the image - } - } } return U_SUCCESS; @@ -3946,7 +4207,7 @@ USTATUS FfsParser::markProtectedRangeRecursive(const UModelIndex & index, const // Mark normal items else { UINT32 currentOffset = model->base(index); - UINT32 currentSize = (UINT32)(model->header(index).size() + model->body(index).size() + model->tail(index).size()); + UINT32 currentSize = (UINT32)(model->headerSize(index) + model->bodySize(index) + model->tailSize(index)); if (std::min(currentOffset + currentSize, range.Offset + range.Size) > std::max(currentOffset, range.Offset)) { if (range.Offset <= currentOffset && currentOffset + currentSize <= range.Offset + range.Size) { // Mark as fully in range @@ -4148,8 +4409,8 @@ USTATUS FfsParser::parseVendorHashFile(const UByteArray & fileGuid, const UModel USTATUS FfsParser::parseMicrocodeVolumeBody(const UModelIndex & index) { - const UINT32 headerSize = (UINT32)model->header(index).size(); - const UINT32 bodySize = (UINT32)model->body(index).size(); + const UINT32 headerSize = (UINT32)model->headerSize(index); + const UINT32 bodySize = (UINT32)model->bodySize(index); UINT32 offset = 0; USTATUS result = U_SUCCESS; @@ -4159,7 +4420,8 @@ USTATUS FfsParser::parseMicrocodeVolumeBody(const UModelIndex & index) UByteArray ucode = model->body(index).mid(offset); // Check for empty area - if (ucode.size() == ucode.count('\xFF') || ucode.size() == ucode.count('\x00')) { + auto c = checkSingle(ucode); + if (c == 0 || c == 0xFF) { result = U_INVALID_MICROCODE; } else { @@ -4174,13 +4436,17 @@ USTATUS FfsParser::parseMicrocodeVolumeBody(const UModelIndex & index) UString info = usprintf("Full size: %Xh (%u)", (UINT32)ucode.size(), (UINT32)ucode.size()); // Add tree item - model->addItem(headerSize + offset, Types::Padding, getPaddingType(ucode), name, UString(), info, UByteArray(), ucode, UByteArray(), Fixed, index); + model->addItem( + headerSize + offset, Types::Padding, getPaddingType(ucode), + name, UString(), info, + 0, ucode.size(), 0, + Fixed, index); } return U_SUCCESS; } // Get to next candidate - offset += model->header(currentMicrocode).size() + model->body(currentMicrocode).size() + model->tail(currentMicrocode).size(); + offset += model->headerSize(currentMicrocode) + model->bodySize(currentMicrocode) + model->tailSize(currentMicrocode); if (offset >= bodySize) break; } @@ -4268,15 +4534,15 @@ USTATUS FfsParser::parseIntelMicrocodeHeader(const UByteArray & microcode, const // Recalculate checksum after patching tempUcodeHeader->Checksum = 0; - tempUcodeHeader->ProcessorFlags = entry->ProcessorFlags; + tempUcodeHeader->PlatformIds = entry->PlatformIds; tempUcodeHeader->ProcessorSignature = entry->ProcessorSignature; UINT32 entryCalculated = calculateChecksum32((const UINT32*)tempMicrocode.constData(), sizeof(INTEL_MICROCODE_HEADER) + dataSize); - - extendedHeaderInfo += usprintf("\nCPU signature #%u: %08Xh\nCPU flags #%u: %02Xh\nChecksum #%u: %08Xh, ", - i + 1, entry->ProcessorSignature, - i + 1, entry->ProcessorFlags, - i + 1, entry->Checksum) - + (entry->Checksum == entryCalculated ? UString("valid") : usprintf("invalid, should be %08Xh", entryCalculated)); + + extendedHeaderInfo += usprintf("\nCPU signature #%u: %08Xh\nCPU platform Id #%u: %08Xh\nChecksum #%u: %08Xh, ", + i + 1, entry->ProcessorSignature, + i + 1, entry->PlatformIds, + i + 1, entry->Checksum) + + (entry->Checksum == entryCalculated ? UString("valid") : usprintf("invalid, should be %08Xh", entryCalculated)); } } else { @@ -4288,26 +4554,31 @@ USTATUS FfsParser::parseIntelMicrocodeHeader(const UByteArray & microcode, const } // Get microcode binary - UByteArray microcodeBinary = microcode.left(ucodeHeader->TotalSize); + UINT32 microcodeBinarySize = microcode.size() < ucodeHeader->TotalSize ? microcode.size() : ucodeHeader->TotalSize; // Add info UString name("Intel microcode"); UString info = usprintf("Full size: %Xh (%u)\nHeader size: 0h (0u)\nBody size: %Xh (%u)\nTail size: 0h (0u)\n" - "Date: %02X.%02X.%04x\nCPU signature: %08Xh\nRevision: %08Xh\nCPU flags: %02Xh\nChecksum: %08Xh, ", - (UINT32)microcodeBinary.size(), (UINT32)microcodeBinary.size(), - (UINT32)microcodeBinary.size(), (UINT32)microcodeBinary.size(), + "Date: %02X.%02X.%04x\nCPU signature: %08Xh\nRevision: %08Xh\nMinimal update revision: %08Xh\nCPU platform Id: %08Xh\nChecksum: %08Xh, ", + microcodeBinarySize, microcodeBinarySize, + microcodeBinarySize, microcodeBinarySize, ucodeHeader->DateDay, ucodeHeader->DateMonth, ucodeHeader->DateYear, ucodeHeader->ProcessorSignature, ucodeHeader->UpdateRevision, - ucodeHeader->ProcessorFlags, + ucodeHeader->UpdateRevisionMin, + ucodeHeader->PlatformIds, ucodeHeader->Checksum) + (ucodeHeader->Checksum == calculated ? UString("valid") : usprintf("invalid, should be %08Xh", calculated)) + extendedHeaderInfo; // Add tree item - index = model->addItem(localOffset, Types::Microcode, Subtypes::IntelMicrocode, name, UString(), info, UByteArray(), microcodeBinary, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::Microcode, Subtypes::IntelMicrocode, + name, UString(), info, + 0, microcodeBinarySize, 0, + Fixed, parent); if (msgInvalidChecksum) msg(usprintf("%s: invalid microcode checksum %08Xh, should be %08Xh", __FUNCTION__, ucodeHeader->Checksum, calculated), index); if (msgUnknownOrDamagedMicrocodeTail) @@ -4339,15 +4610,14 @@ USTATUS FfsParser::parseBpdtRegion(const UByteArray & region, const UINT32 local } // Get info - UByteArray header = region.left(sizeof(BPDT_HEADER)); - UByteArray body = region.mid(sizeof(BPDT_HEADER), ptBodySize); + UINT32 headerSize = sizeof(BPDT_HEADER); UString name = UString("BPDT partition table"); UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\n" "Number of entries: %u\nVersion: %02Xh\nRedundancyFlag: %Xh\n" "IFWI version: %Xh\nFITC version: %u.%u.%u.%u", ptSize, ptSize, - (UINT32)header.size(), (UINT32)header.size(), + headerSize, headerSize, ptBodySize, ptBodySize, ptHeader->NumEntries, ptHeader->HeaderVersion, @@ -4356,7 +4626,11 @@ USTATUS FfsParser::parseBpdtRegion(const UByteArray & region, const UINT32 local ptHeader->FitcMajor, ptHeader->FitcMinor, ptHeader->FitcHotfix, ptHeader->FitcBuild); // Add tree item - index = model->addItem(localOffset, Types::BpdtStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::BpdtStore, 0, + name, UString(), info, + headerSize, ptBodySize, 0, + Fixed, parent); // Adjust offset UINT32 offset = sizeof(BPDT_HEADER); @@ -4382,7 +4656,11 @@ USTATUS FfsParser::parseBpdtRegion(const UByteArray & region, const UINT32 local UString("\nUMA cacheable: ") + (ptEntry->UmaCacheable ? "Yes" : "No"); // Add tree item - UModelIndex entryIndex = model->addItem(localOffset + offset, Types::BpdtEntry, 0, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(BPDT_ENTRY)), UByteArray(), Fixed, index); + UModelIndex entryIndex = model->addItem( + localOffset + offset, Types::BpdtEntry, 0, + name, UString(), info, + 0, sizeof(BPDT_ENTRY), 0, + Fixed, index); // Adjust offset offset += sizeof(BPDT_ENTRY); @@ -4496,7 +4774,11 @@ make_partition_table_consistent: UString text = bpdtEntryTypeToUString(partitions[i].ptEntry.Type); // Add tree item - UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset, Types::BpdtPartition, 0, name, text, info, UByteArray(), partition, UByteArray(), Fixed, parent); + UModelIndex partitionIndex = model->addItem( + localOffset + partitions[i].ptEntry.Offset, Types::BpdtPartition, 0, + name, text, info, + 0, partition.size(), 0, + Fixed, parent); // Special case of S-BPDT if (partitions[i].ptEntry.Type == BPDT_ENTRY_TYPE_S_BPDT) { @@ -4525,7 +4807,11 @@ make_partition_table_consistent: (UINT32)padding.size(), (UINT32)padding.size()); // Add tree item - model->addItem(localOffset + partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent); + model->addItem( + localOffset + partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(padding), + name, UString(), info, + 0, padding.size(), 0, + Fixed, parent); } } @@ -4540,7 +4826,11 @@ make_partition_table_consistent: (UINT32)padding.size(), (UINT32)padding.size()); // Add tree item - model->addItem(localOffset + partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent); + model->addItem( + localOffset + partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size, Types::Padding, getPaddingType(padding), + name, UString(), info, + 0, padding.size(), 0, + Fixed, parent); } return U_SUCCESS; @@ -4580,40 +4870,45 @@ USTATUS FfsParser::parseCpdRegion(const UByteArray & region, const UINT32 localO } // Get info - UByteArray header = region.left(ptHeaderSize); - UByteArray body = region.mid(ptHeaderSize, ptBodySize); UString name = usprintf("CPD partition table"); UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u\n" "Header version: %u\nEntry version: %u", ptSize, ptSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + ptHeaderSize, ptHeaderSize, + ptBodySize, ptBodySize, cpdHeader->NumEntries, cpdHeader->HeaderVersion, cpdHeader->EntryVersion); // Add tree item - index = model->addItem(localOffset, Types::CpdStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem( + localOffset, Types::CpdStore, 0, + name, UString(), info, + ptHeaderSize, ptBodySize, 0, + Fixed, parent); // Add partition table entries std::vector<CPD_PARTITION_INFO> partitions; UINT32 offset = ptHeaderSize; - const CPD_ENTRY* firstCpdEntry = (const CPD_ENTRY*)(body.constData()); + const CPD_ENTRY* firstCpdEntry = (const CPD_ENTRY*)(region.constData() + ptHeaderSize); for (UINT32 i = 0; i < cpdHeader->NumEntries; i++) { // Populate entry header const CPD_ENTRY* cpdEntry = firstCpdEntry + i; - UByteArray entry((const char*)cpdEntry, sizeof(CPD_ENTRY)); // Get info name = usprintf("%.12s", cpdEntry->EntryName); info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ", - (UINT32)entry.size(), (UINT32)entry.size(), + sizeof(CPD_ENTRY), sizeof(CPD_ENTRY), cpdEntry->Offset.Offset, cpdEntry->Length) + (cpdEntry->Offset.HuffmanCompressed ? "Yes" : "No"); // Add tree item - UModelIndex entryIndex = model->addItem(offset, Types::CpdEntry, 0, name, UString(), info, UByteArray(), entry, UByteArray(), Fixed, index); + UModelIndex entryIndex = model->addItem( + offset, Types::CpdEntry, 0, + name, UString(), info, + 0, sizeof(CPD_ENTRY), 0, + Fixed, index); // Adjust offset offset += sizeof(CPD_ENTRY); @@ -4639,7 +4934,11 @@ USTATUS FfsParser::parseCpdRegion(const UByteArray & region, const UINT32 localO (UINT32)partition.size(), (UINT32)partition.size()); // Add tree item - model->addItem(localOffset + ptSize, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + model->addItem( + localOffset + ptSize, Types::Padding, getPaddingType(partition), + name, UString(), info, + 0, partition.size(), 0, + Fixed, parent); return U_SUCCESS; } @@ -4815,15 +5114,15 @@ make_partition_table_consistent: && partitions[i].ptEntry.Length >= sizeof(CPD_MANIFEST_HEADER)) { const CPD_MANIFEST_HEADER* manifestHeader = (const CPD_MANIFEST_HEADER*) partition.constData(); if (manifestHeader->HeaderId == ME_MANIFEST_HEADER_ID) { - UByteArray header = partition.left(manifestHeader->HeaderLength * sizeof(UINT32)); - UByteArray body = partition.mid(manifestHeader->HeaderLength * sizeof(UINT32)); + UINT32 headerSize = manifestHeader->HeaderLength * sizeof(UINT32); + UINT32 bodySize = partition.size() - headerSize; info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)" "\nHeader type: %u\nHeader length: %Xh (%u)\nHeader version: %Xh\nFlags: %08Xh\nVendor: %Xh\n" "Date: %Xh\nSize: %Xh (%u)\nVersion: %u.%u.%u.%u\nSecurity version number: %u\nModulus size: %Xh (%u)\nExponent size: %Xh (%u)", (UINT32)partition.size(), (UINT32)partition.size(), - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + headerSize, headerSize, + bodySize, bodySize, manifestHeader->HeaderType, manifestHeader->HeaderLength * (UINT32)sizeof(UINT32), manifestHeader->HeaderLength * (UINT32)sizeof(UINT32), manifestHeader->HeaderVersion, @@ -4837,12 +5136,17 @@ make_partition_table_consistent: manifestHeader->ExponentSize * (UINT32)sizeof(UINT32), manifestHeader->ExponentSize * (UINT32)sizeof(UINT32)); // Add tree item - UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::ManifestCpdPartition, name, UString(), info, header, body, UByteArray(), Fixed, parent); + UModelIndex partitionIndex = model->addItem( + localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::ManifestCpdPartition, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, parent); // Parse data as extensions area - // Add the header size as a local offset - // Since the body starts after the header length - parseCpdExtensionsArea(partitionIndex, (UINT32)header.size()); + // Add the header size as a local offset + // Since the body starts after the + // header length + parseCpdExtensionsArea(partitionIndex, headerSize); } } } @@ -4858,7 +5162,11 @@ make_partition_table_consistent: info += UString("\nMetadata hash: ") + UString(hash.toHex().constData()); // Add three item - UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::MetadataCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + UModelIndex partitionIndex = model->addItem( + localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::MetadataCpdPartition, + name, UString(), info, + 0, partition.size(), 0, + Fixed, parent); // Parse data as extensions area parseCpdExtensionsArea(partitionIndex, 0); @@ -4874,7 +5182,11 @@ make_partition_table_consistent: sha256(partition.constData(), partition.size(), hash.data()); info += UString("\nHash: ") + UString(hash.toHex().constData()); - UModelIndex codeIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::CodeCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + UModelIndex codeIndex = model->addItem( + localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::CodeCpdPartition, + name, UString(), info, + 0, partition.size(), 0, + Fixed, parent); (void)parseRawArea(codeIndex); } } @@ -4886,7 +5198,11 @@ make_partition_table_consistent: info = usprintf("Full size: %Xh (%u)", (UINT32)partition.size(), (UINT32)partition.size()); // Add tree item - model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + model->addItem( + localOffset + partitions[i].ptEntry.Offset.Offset, Types::Padding, getPaddingType(partition), + name, UString(), info, + 0, partition.size(), 0, + Fixed, parent); } else { msg(usprintf("%s: CPD partition of unknown type found", __FUNCTION__), parent); @@ -4917,17 +5233,17 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index, const UINT3 // Parse Signed Package Info a bit further UModelIndex extIndex; if (extHeader->Type == CPD_EXT_TYPE_SIGNED_PACKAGE_INFO) { - UByteArray header = partition.left(sizeof(CPD_EXT_SIGNED_PACKAGE_INFO)); - UByteArray data = partition.mid(header.size()); + UINT32 headerSize = sizeof(CPD_EXT_SIGNED_PACKAGE_INFO); + UINT32 bodySize = partition.size() - headerSize; - const CPD_EXT_SIGNED_PACKAGE_INFO* infoHeader = (const CPD_EXT_SIGNED_PACKAGE_INFO*)header.constData(); + const CPD_EXT_SIGNED_PACKAGE_INFO* infoHeader = (const CPD_EXT_SIGNED_PACKAGE_INFO*)partition.constData(); info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %Xh\n" "Package name: %.4s\nVersion control number: %Xh\nSecurity version number: %Xh\n" "Usage bitmap: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", (UINT32)partition.size(), (UINT32)partition.size(), - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + headerSize, headerSize, + bodySize, bodySize, infoHeader->ExtensionType, infoHeader->PackageName, infoHeader->Vcn, @@ -4938,7 +5254,11 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index, const UINT3 infoHeader->UsageBitmap[12], infoHeader->UsageBitmap[13], infoHeader->UsageBitmap[14], infoHeader->UsageBitmap[15]); // Add tree item - extIndex = model->addItem(offset + localOffset, Types::CpdExtension, 0, name, UString(), info, header, data, UByteArray(), Fixed, index); + extIndex = model->addItem( + offset + localOffset, Types::CpdExtension, 0, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, index); parseSignedPackageInfoData(extIndex); } // Parse IFWI Partition Manifest a bit further @@ -4981,7 +5301,11 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index, const UINT3 + UString("\nPartition hash: ") + UString(hash.toHex().constData()); // Add tree item - extIndex = model->addItem(offset + localOffset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); + extIndex = model->addItem( + offset + localOffset, Types::CpdExtension, 0, + name, UString(), info, + 0, partition.size(), 0, + Fixed, index); if (msgHashSizeMismatch) { msg(usprintf("%s: IFWI Partition Manifest hash size is %u, maximum allowed is %u, truncated", __FUNCTION__, attrHeader->HashSize, (UINT32)sizeof(attrHeader->CompletePartitionHash)), extIndex); } @@ -5006,12 +5330,20 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index, const UINT3 attrHeader->GlobalModuleId) + UString(hash.toHex().constData()); // Add tree item - extIndex = model->addItem(offset + localOffset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); + extIndex = model->addItem( + offset + localOffset, Types::CpdExtension, 0, + name, UString(), info, + 0, partition.size(), 0, + Fixed, index); } // Parse everything else else { // Add tree item, if needed - extIndex = model->addItem(offset + localOffset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index); + extIndex = model->addItem( + offset + localOffset, Types::CpdExtension, 0, + name, UString(), info, + 0, partition.size(), 0, + Fixed, index); } // There needs to be a more generic way to do it, but it is fine for now @@ -5045,7 +5377,7 @@ USTATUS FfsParser::parseSignedPackageInfoData(const UModelIndex & index) const CPD_EXT_SIGNED_PACKAGE_INFO_MODULE* moduleHeader = (const CPD_EXT_SIGNED_PACKAGE_INFO_MODULE*)(body.constData() + offset); if (sizeof(CPD_EXT_SIGNED_PACKAGE_INFO_MODULE) <= ((UINT32)body.size() - offset)) { // TODO: check sanity of moduleHeader->HashSize - UByteArray module((const char*)moduleHeader, CpdExtSignedPkgMetadataHashOffset + moduleHeader->HashSize); + UINT32 moduleSize = CpdExtSignedPkgMetadataHashOffset + moduleHeader->HashSize; UString name = usprintf("%.12s", moduleHeader->Name); // This hash is stored reversed @@ -5054,14 +5386,18 @@ USTATUS FfsParser::parseSignedPackageInfoData(const UModelIndex & index) std::reverse(hash.begin(), hash.end()); UString info = usprintf("Full size: %Xh (%u)\nType: %Xh\nHash algorithm: %Xh\nHash size: %Xh (%u)\nMetadata size: %Xh (%u)\nMetadata hash: ", - (UINT32)module.size(), (UINT32)module.size(), + moduleSize, moduleSize, moduleHeader->Type, moduleHeader->HashAlgorithm, moduleHeader->HashSize, moduleHeader->HashSize, moduleHeader->MetadataSize, moduleHeader->MetadataSize) + UString(hash.toHex().constData()); // Add tree otem - model->addItem(offset, Types::CpdSpiEntry, 0, name, UString(), info, UByteArray(), module, UByteArray(), Fixed, index); - offset += module.size(); + model->addItem( + offset, Types::CpdSpiEntry, 0, + name, UString(), info, + 0, moduleSize, 0, + Fixed, index); + offset += moduleSize; } else break; // TODO: add padding at the end diff --git a/common/ffsparser.h b/common/ffsparser.h index ef7ccc4..fcf6038 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -14,6 +14,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define FFSPARSER_H #include <vector> +#include <unordered_map> #include "basetypes.h" #include "ustring.h" @@ -115,6 +116,8 @@ private: UModelIndex lastVtf; UINT32 imageBase; UINT64 addressDiff; + REGION_INFO biosRegionInfo; + std::unordered_map<UINT64, int> baseAddressesMap; UString securityInfo; @@ -126,6 +129,7 @@ private: USTATUS performFirstPass(const UByteArray & imageFile, UModelIndex & index); USTATUS parseCapsule(const UByteArray & capsule, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseImage(const UByteArray& buffer, const UINT32 localOffset, const UModelIndex& parent, UModelIndex& index); USTATUS parseIntelImage(const UByteArray & intelImage, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); USTATUS parseGenericImage(const UByteArray & intelImage, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); @@ -136,9 +140,9 @@ private: USTATUS parseRawArea(const UModelIndex & index); USTATUS parseVolumeHeader(const UByteArray & volume, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); - USTATUS parseVolumeBody(const UModelIndex & index); + USTATUS parseVolumeBody(const UModelIndex & index, const bool probe = false); USTATUS parseMicrocodeVolumeBody(const UModelIndex & index); - USTATUS parseFileHeader(const UByteArray & file, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseFileHeader(const UByteArray & file, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool probe = false); USTATUS parseFileBody(const UModelIndex & index); USTATUS parseSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree); USTATUS parseSectionBody(const UModelIndex & index); @@ -167,8 +171,8 @@ private: USTATUS parseDepexSectionBody(const UModelIndex & index); USTATUS parseUiSectionBody(const UModelIndex & index); USTATUS parseRawSectionBody(const UModelIndex & index); - USTATUS parsePeImageSectionBody(const UModelIndex & index); - USTATUS parseTeImageSectionBody(const UModelIndex & index); + USTATUS parsePeImageSectionBody(const UModelIndex & index, const bool probe = false); + USTATUS parseTeImageSectionBody(const UModelIndex & index, const bool probe = false); USTATUS parseAprioriRawSection(const UByteArray & body, UString & parsed); USTATUS findNextRawAreaItem(const UModelIndex & index, const UINT32 localOffset, UINT8 & nextItemType, UINT32 & nextItemOffset, UINT32 & nextItemSize, UINT32 & nextItemAlternativeSize); diff --git a/common/fitparser.cpp b/common/fitparser.cpp index 641d07f..f40385c 100644 --- a/common/fitparser.cpp +++ b/common/fitparser.cpp @@ -200,7 +200,7 @@ USTATUS FitParser::parseFit(const UModelIndex & index) return U_SUCCESS; } -void FitParser::findFitRecursive(const UModelIndex & index, UModelIndex & found, UINT32 & fitOffset) +void FitParser::findFitRecursive(const UModelIndex& index, UModelIndex & found, UINT32 & fitOffset) { // Sanity check if (!index.isValid()) { @@ -217,28 +217,30 @@ void FitParser::findFitRecursive(const UModelIndex & index, UModelIndex & found, } } - // Check for all FIT signatures in item body - UByteArray lastVtfBody = model->body(ffsParser->lastVtf); + // Check for all FIT signatures in item body - we want to log 'em all + UByteArray lastVtfBody = model->header(ffsParser->lastVtf) + model->body(ffsParser->lastVtf) + model->tail(ffsParser->lastVtf); UINT64 fitSignatureValue = INTEL_FIT_SIGNATURE; UByteArray fitSignature((const char*)&fitSignatureValue, sizeof(fitSignatureValue)); - UINT32 storedFitAddress = *(const UINT32*)(lastVtfBody.constData() + lastVtfBody.size() - INTEL_FIT_POINTER_OFFSET); + UINT32 storedFitAddress = *(const UINT32*)(lastVtfBody.constData() + UEFI_UPPER_INVALID_ADDRESS - INTEL_FIT_POINTER_OFFSET + - model->base(ffsParser->lastVtf) - ffsParser->addressDiff); for (INT32 offset = (INT32)model->body(index).indexOf(fitSignature); offset >= 0; offset = (INT32)model->body(index).indexOf(fitSignature, offset + 1)) { // FIT candidate found, calculate its physical address - UINT32 fitAddress = (UINT32)(model->base(index) + (UINT32)ffsParser->addressDiff + model->header(index).size() + (UINT32)offset); + UINT32 fitAddress = (UINT32)(model->base(index) + (UINT32)ffsParser->addressDiff + model->headerSize(index) + (UINT32)offset); // Check FIT address to be stored in the last VTF if (fitAddress == storedFitAddress) { // Valid FIT table must have at least two entries - if ((UINT32)model->body(index).size() < offset + 2*sizeof(INTEL_FIT_ENTRY)) { + if ((UINT32)model->bodySize(index) < offset + 2*sizeof(INTEL_FIT_ENTRY)) { msg(usprintf("%s: FIT table candidate found, too small to contain real FIT", __FUNCTION__), index); } else { // Real FIT found found = index; fitOffset = offset; - msg(usprintf("%s: real FIT table found at physical address %08Xh", __FUNCTION__, fitAddress), found); + msg(usprintf("%s: real FIT table found at physical address %08Xh (offset %Xh)", __FUNCTION__, + fitAddress, fitOffset), found); break; } } @@ -709,8 +711,8 @@ USTATUS FitParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolic else { // Add postIbbHash protected range UByteArray postIbbHash(ibbs_body->post_ibb_hash()->hash().data(), ibbs_body->post_ibb_hash()->len_hash()); - if (postIbbHash.count('\x00') != postIbbHash.size() - && postIbbHash.count('\xFF') != postIbbHash.size()) { + auto c = checkSingle(postIbbHash); + if (c != 0 && c != 0xFF) { PROTECTED_RANGE range = {}; range.Type = PROTECTED_RANGE_INTEL_BOOT_GUARD_POST_IBB; range.AlgorithmId = ibbs_body->post_ibb_hash()->hash_algorithm_id(); @@ -990,8 +992,8 @@ USTATUS FitParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolic else { // Add postIbbHash protected range UByteArray postIbbHash(ibbs_body->post_ibb_digest()->hash().data(), ibbs_body->post_ibb_digest()->len_hash()); - if (postIbbHash.count('\x00') != postIbbHash.size() - && postIbbHash.count('\xFF') != postIbbHash.size()) { + auto c = checkSingle(postIbbHash); + if (c != 0 && c != 0xFF) { PROTECTED_RANGE range = {}; range.Type = PROTECTED_RANGE_INTEL_BOOT_GUARD_POST_IBB; range.AlgorithmId = ibbs_body->post_ibb_digest()->hash_algorithm_id(); @@ -1021,8 +1023,8 @@ USTATUS FitParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolic // Add ObbHash protected range UByteArray obbHash(ibbs_body->obb_digest()->hash().data(), ibbs_body->obb_digest()->len_hash()); - if (obbHash.count('\x00') != obbHash.size() - && obbHash.count('\xFF') != obbHash.size()) { + auto c = checkSingle(obbHash); + if (c != 0 && c != 0xFF) { PROTECTED_RANGE range = {}; range.Type = PROTECTED_RANGE_INTEL_BOOT_GUARD_OBB; range.AlgorithmId = ibbs_body->obb_digest()->hash_algorithm_id(); diff --git a/common/intel_microcode.h b/common/intel_microcode.h index c6a3d89..04354ce 100644 --- a/common/intel_microcode.h +++ b/common/intel_microcode.h @@ -21,7 +21,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // This structure is described in Section 9.11.1 of the Intel Software Developer manual Volume 3A Part 1 typedef struct INTEL_MICROCODE_HEADER_ { - UINT32 HeaderVersion; // 0x00000001 + UINT32 HeaderType; // 0x00000001 for Microcode (we do not need to support IFS yet) UINT32 UpdateRevision; UINT16 DateYear; // BCD UINT8 DateDay; // BCD @@ -31,8 +31,7 @@ typedef struct INTEL_MICROCODE_HEADER_ { // Checksum is correct when the summation of all the DWORDs (including the extended Processor Signature Table) // that comprise the microcode update result in 00000000H. UINT32 LoaderRevision; // 0x00000001 - UINT8 ProcessorFlags; - UINT8 ProcessorFlagsReserved[3]; // Zeroes + UINT32 PlatformIds; // Platform Ids UINT32 DataSize; // Specifies the size of the encrypted data in bytes, and must be a multiple of DWORDs. // If this value is 00000000H, then the microcode update encrypted data is 2000 bytes (or 500 DWORDs). // Sane values are less than 0x1000000 @@ -40,7 +39,9 @@ typedef struct INTEL_MICROCODE_HEADER_ { // It is the summation of the header size, the encrypted data size and the size of the optional extended signature table. // This value is always a multiple of 1024 according to the spec, but Intel already breached it several times. // Sane values are less than 0x1000000 - UINT8 Reserved[12]; // Zeroes + UINT32 MetadataSize; // Reserved in Microcode headers + UINT32 UpdateRevisionMin; // Minimum required version for OS Kernel Late Loading + UINT32 Reserved; // Zeroes } INTEL_MICROCODE_HEADER; #define INTEL_MICROCODE_REAL_DATA_SIZE_ON_ZERO 2000 @@ -57,8 +58,8 @@ typedef struct INTEL_MICROCODE_EXTENDED_HEADER_ { typedef struct INTEL_MICROCODE_EXTENDED_HEADER_ENTRY_ { UINT32 ProcessorSignature; - UINT32 ProcessorFlags; - UINT32 Checksum; // To calculate the Checksum, substitute the Primary Processor Signature entry and the Processor Flags entry with the corresponding Extended Patch entry. + UINT32 PlatformIds; + UINT32 Checksum; // To calculate the Checksum, substitute the Primary Processor Signature entry and the Platform Ids entry with the corresponding Extended Patch entry. // Delete the Extended Processor Signature Table entries. // Checksum is correct when the summation of all DWORDs that comprise the created Extended Processor Patch results in 00000000H. } INTEL_MICROCODE_EXTENDED_HEADER_ENTRY; diff --git a/common/meparser.cpp b/common/meparser.cpp index aea1ae0..622159d 100755 --- a/common/meparser.cpp +++ b/common/meparser.cpp @@ -126,8 +126,7 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & } // Get info - UByteArray header = region.left(romBypassVectorSize + sizeof(FPT_HEADER)); - UByteArray body = region.mid(header.size(), ptBodySize); + UINT32 headerSize = romBypassVectorSize + sizeof(FPT_HEADER); UString name = UString("FPT partition table"); UString info; @@ -139,7 +138,7 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nROM bypass vector: %s\nNumber of entries: %u\nHeader version: %02Xh\nEntry version: %02Xh\n" "Header length: %02Xh\nFlags: %Xh\nTicks to add: %04Xh\nTokens to add: %04Xh\nSPS Flags: %Xh\nFITC version: %u.%u.%u.%u\nCRC32 Checksum: %08Xh", ptSize, ptSize, - (UINT32)header.size(), (UINT32)header.size(), + headerSize, headerSize, ptBodySize, ptBodySize, (romBypassVectorSize ? "present" : "absent"), ptHeader21->NumEntries, @@ -159,7 +158,7 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nROM bypass vector: %s\nNumber of entries: %u\nHeader version: %02Xh\nEntry version: %02Xh\n" "Header length: %02Xh\nFlash cycle life: %04Xh\nFlash cycle limit: %04Xh\nUMA size: %Xh\nFlags: %Xh\nFITC version: %u.%u.%u.%u\nChecksum: %02Xh", ptSize, ptSize, - (UINT32)header.size(), (UINT32)header.size(), + headerSize, headerSize, ptBodySize, ptBodySize, (romBypassVectorSize ? "present" : "absent"), ptHeader->NumEntries, @@ -176,11 +175,15 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & } // Add tree item - index = model->addItem(0, Types::FptStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent); + index = model->addItem( + 0, Types::FptStore, 0, + name, UString(), info, + headerSize, ptBodySize, 0, + Fixed, parent); // Add partition table entries std::vector<FPT_PARTITION_INFO> partitions; - UINT32 offset = (UINT32)header.size(); + UINT32 offset = headerSize; UINT32 numEntries = ptHeader->NumEntries; const FPT_HEADER_ENTRY* firstPtEntry = (const FPT_HEADER_ENTRY*)(region.constData() + offset); for (UINT32 i = 0; i < numEntries; i++) { @@ -197,7 +200,11 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & // Add tree item const UINT8 type = (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Size != 0 && ptEntry->EntryValid != 0xFF) ? Subtypes::ValidFptEntry : Subtypes::InvalidFptEntry; - UModelIndex entryIndex = model->addItem(offset, Types::FptEntry, type, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(FPT_HEADER_ENTRY)), UByteArray(), Fixed, index); + UModelIndex entryIndex = model->addItem( + offset, Types::FptEntry, type, + name, UString(), info, + 0, sizeof(FPT_HEADER_ENTRY), 0, + Fixed, index); // Adjust offset offset += sizeof(FPT_HEADER_ENTRY); @@ -311,7 +318,11 @@ make_partition_table_consistent: // Add tree item UINT8 type = Subtypes::CodeFptPartition + partitions[i].ptEntry.Type; - partitionIndex = model->addItem(partitions[i].ptEntry.Offset, Types::FptPartition, type, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + partitionIndex = model->addItem( + partitions[i].ptEntry.Offset, Types::FptPartition, type, + name, UString(), info, + 0, partition.size(), 0, + Fixed, parent); if (type == Subtypes::CodeFptPartition && partition.size() >= (int) sizeof(UINT32) && readUnaligned((const UINT32*)partition.constData()) == CPD_SIGNATURE) { // Parse code partition contents UModelIndex cpdIndex; @@ -324,7 +335,11 @@ make_partition_table_consistent: info = usprintf("Full size: %Xh (%u)", (UINT32)partition.size(), (UINT32)partition.size()); // Add tree item - model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + model->addItem( + partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), + name, UString(), info, + 0, partition.size(), 0, + Fixed, parent); } } @@ -343,7 +358,7 @@ USTATUS MeParser::parseIfwi16Region(const UByteArray & region, const UModelIndex // Add header UINT32 ptSize = sizeof(IFWI_16_LAYOUT_HEADER); - UByteArray header = region.left(ptSize); + UINT32 headerSize = ptSize > region.size() ? region.size() : ptSize; UString name = UString("IFWI 1.6 header"); UString info = usprintf("Full size: %Xh (%u)\n" @@ -354,7 +369,7 @@ USTATUS MeParser::parseIfwi16Region(const UByteArray & region, const UModelIndex "Boot4 partition offset: %Xh\nBoot4 partition size: %Xh\n" "Boot5 partition offset: %Xh\nBoot5 partition size: %Xh\n" "Checksum: %" PRIX64 "h", - (UINT32)header.size(), (UINT32)header.size(), + headerSize, headerSize, ifwiHeader->DataPartition.Offset, ifwiHeader->DataPartition.Size, ifwiHeader->BootPartition[0].Offset, ifwiHeader->BootPartition[0].Size, ifwiHeader->BootPartition[1].Offset, ifwiHeader->BootPartition[1].Size, @@ -363,7 +378,11 @@ USTATUS MeParser::parseIfwi16Region(const UByteArray & region, const UModelIndex ifwiHeader->BootPartition[4].Offset, ifwiHeader->BootPartition[4].Size, ifwiHeader->Checksum); // Add tree item - index = model->addItem(0, Types::IfwiHeader, 0, name, UString(), info, UByteArray(), header, UByteArray(), Fixed, parent); + index = model->addItem( + 0, Types::IfwiHeader, 0, + name, UString(), info, + 0, headerSize, 0, + Fixed, parent); std::vector<IFWI_PARTITION_INFO> partitions; // Add data partition @@ -474,7 +493,11 @@ make_partition_table_consistent: info = usprintf("Full size: %Xh (%u)\n", (UINT32)partition.size(), (UINT32)partition.size()); // Add tree item - partitionIndex = model->addItem(partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + partitionIndex = model->addItem( + partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype, + name, UString(), info, + 0, partition.size(), 0, + Fixed, parent); // Parse partition further if (partitions[i].subtype == Subtypes::DataIfwiPartition) { @@ -493,7 +516,11 @@ make_partition_table_consistent: info = usprintf("Full size: %Xh (%u)", (UINT32)partition.size(), (UINT32)partition.size()); // Add tree item - model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + model->addItem( + partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), + name, UString(), info, + 0, partition.size(), 0, + Fixed, parent); } } @@ -513,7 +540,7 @@ USTATUS MeParser::parseIfwi17Region(const UByteArray & region, const UModelIndex // Add header UINT32 ptSize = sizeof(IFWI_17_LAYOUT_HEADER); - UByteArray header = region.left(ptSize); + UINT32 headerSize = ptSize > region.size() ? region.size() : ptSize; UString name = UString("IFWI 1.7 header"); UString info = usprintf("Full size: %Xh (%u)\n" @@ -527,7 +554,7 @@ USTATUS MeParser::parseIfwi17Region(const UByteArray & region, const UModelIndex "Boot4 partition offset: %Xh\nBoot4 partition size: %Xh\n" "Boot5 partition offset: %Xh\nBoot5 partition size: %Xh\n" "Temp page offset: %Xh\nTemp page size: %Xh\n", - (UINT32)header.size(), (UINT32)header.size(), + headerSize, headerSize, ifwiHeader->Flags, ifwiHeader->Reserved, ifwiHeader->Checksum, @@ -539,7 +566,11 @@ USTATUS MeParser::parseIfwi17Region(const UByteArray & region, const UModelIndex ifwiHeader->BootPartition[4].Offset, ifwiHeader->BootPartition[4].Size, ifwiHeader->TempPage.Offset, ifwiHeader->TempPage.Size); // Add tree item - index = model->addItem(0, Types::IfwiHeader, 0, name, UString(), info, UByteArray(), header, UByteArray(), Fixed, parent); + index = model->addItem( + 0, Types::IfwiHeader, 0, + name, UString(), info, + 0, headerSize, 0, + Fixed, parent); std::vector<IFWI_PARTITION_INFO> partitions; // Add data partition @@ -662,7 +693,11 @@ make_partition_table_consistent: info = usprintf("Full size: %Xh (%u)\n", (UINT32)partition.size(), (UINT32)partition.size()); // Add tree item - partitionIndex = model->addItem(partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + partitionIndex = model->addItem( + partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype, + name, UString(), info, + 0, partition.size(), 0, + Fixed, parent); // Parse partition further if (partitions[i].subtype == Subtypes::DataIfwiPartition) { @@ -688,7 +723,11 @@ make_partition_table_consistent: info = usprintf("Full size: %Xh (%u)", (UINT32)partition.size(), (UINT32)partition.size()); // Add tree item - model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent); + model->addItem( + partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), + name, UString(), info, + 0, partition.size(), 0, + Fixed, parent); } } diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp index d7c4791..3cfa04b 100644 --- a/common/nvramparser.cpp +++ b/common/nvramparser.cpp @@ -40,7 +40,7 @@ #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #endif -USTATUS NvramParser::parseNvarStore(const UModelIndex & index) +USTATUS NvramParser::parseNvarStore(const UModelIndex & index, const bool probe) { // Sanity check if (!index.isValid()) @@ -50,7 +50,7 @@ USTATUS NvramParser::parseNvarStore(const UModelIndex & index) // Nothing to parse in an empty store if (nvar.isEmpty()) - return U_SUCCESS; + return probe ? U_STORES_NOT_FOUND : U_SUCCESS; // Obtain required fields from parsing data UINT8 emptyByte = 0xFF; @@ -59,9 +59,9 @@ USTATUS NvramParser::parseNvarStore(const UModelIndex & index) const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData(); emptyByte = pdata->emptyByte; } - + try { - const UINT32 localOffset = (UINT32)model->header(index).size(); + const UINT32 localOffset = (UINT32)model->headerSize(index); umemstream is(nvar.constData(), nvar.size()); kaitai::kstream ks(&is); ami_nvar_t parsed(&ks); @@ -74,9 +74,6 @@ USTATUS NvramParser::parseNvarStore(const UModelIndex & index) UString text; UString info; UString guid; - UByteArray header; - UByteArray body; - UByteArray tail; // This is a terminating entry, needs special processing if (entry->_is_null_signature_rest()) { @@ -89,30 +86,47 @@ USTATUS NvramParser::parseNvarStore(const UModelIndex & index) // Get info UString info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size()); - if ((UINT32)padding.count(emptyByte) == unparsedSize) { // Free space + auto c = checkSingle(padding, (unsigned char)emptyByte); + if (c == emptyByte && padding.size() == unparsedSize) { // Free space + if (probe && nvar.size() == unparsedSize) + return U_STORES_NOT_FOUND; // Add tree item - model->addItem(localOffset + entry->offset(), Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index); + model->addItem( + localOffset + entry->offset(), Types::FreeSpace, 0, + UString("Free space"), UString(), info, + 0, padding.size(), 0, + Fixed, index); } else { // Nothing is parsed yet, but the file is not empty if (entry->offset() == 0) { - msg(usprintf("%s: file can't be parsed as NVAR variable store", __FUNCTION__), index); - return U_SUCCESS; + if (!probe) + msg(usprintf("%s: file can't be parsed as NVAR variable store", __FUNCTION__), index); + return U_INVALID_FILE; } // Add tree item - model->addItem(localOffset + entry->offset(), Types::Padding, getPaddingType(padding), UString("Padding"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index); + model->addItem( + localOffset + entry->offset(), Types::Padding, getPaddingType(padding), + UString("Padding"), UString(), info, + 0, padding.size(), 0, + Fixed, index); } // Add GUID store area - UByteArray guidArea = nvar.right(guidAreaSize); + if (guidAreaSize > nvar.size()) + guidAreaSize = nvar.size(); // Get info name = UString("GUID store"); info = usprintf("Full size: %Xh (%u)\nGUIDs in store: %u", - (UINT32)guidArea.size(), (UINT32)guidArea.size(), + guidAreaSize, guidAreaSize, guidsInStore); // Add tree item - model->addItem((UINT32)(localOffset + entry->offset() + padding.size()), Types::NvarGuidStore, 0, name, UString(), info, UByteArray(), guidArea, UByteArray(), Fixed, index); + model->addItem( + (UINT32)(localOffset + entry->offset() + padding.size()), Types::NvarGuidStore, 0, + name, UString(), info, + 0, guidAreaSize, 0, + Fixed, index); return U_SUCCESS; } @@ -210,9 +224,9 @@ USTATUS NvramParser::parseNvarStore(const UModelIndex & index) processing_done: // This feels hacky, but I haven't found a way to ask Kaitai for raw bytes - header = nvar.mid(entry->offset(), sizeof(NVAR_ENTRY_HEADER) + entry_body->data_start_offset()); - body = nvar.mid(entry->offset() + sizeof(NVAR_ENTRY_HEADER) + entry_body->data_start_offset(), entry_body->data_size()); - tail = nvar.mid(entry->end_offset() - entry_body->extended_header_size(), entry_body->extended_header_size()); + UINT32 headerSize = sizeof(NVAR_ENTRY_HEADER) + entry_body->data_start_offset(); + UByteArray nvarData = nvar.mid(entry->offset(), headerSize + entry_body->data_size()) + + nvar.mid(entry->end_offset() - entry_body->extended_header_size(), entry_body->extended_header_size()); // Add GUID info for valid entries if (!guid.isEmpty()) @@ -225,12 +239,12 @@ processing_done: // Add header, body and extended data info info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nTail size: %Xh (%u)", entry->size(), entry->size(), - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), - (UINT32)tail.size(), (UINT32)tail.size()); + headerSize, headerSize, + entry_body->data_size(), entry_body->data_size(), + entry_body->extended_header_size(), entry_body->extended_header_size()); // Add attributes info - const NVAR_ENTRY_HEADER entryHeader = readUnaligned((NVAR_ENTRY_HEADER*)header.constData()); + const NVAR_ENTRY_HEADER entryHeader = readUnaligned((NVAR_ENTRY_HEADER*)nvarData.constData()); info += usprintf("\nAttributes: %02Xh", entryHeader.Attributes); // Translate attributes to text @@ -246,17 +260,16 @@ processing_done: info += usprintf("\nExtended header size: %Xh (%u)", entry_body->extended_header_size(), entry_body->extended_header_size()); - const UINT8 extendedAttributes = *tail.constData(); + const UINT8 extendedAttributes = *(nvar.constData() + entry->end_offset() - entry_body->extended_header_size());//tail.constData(); info += usprintf("\nExtended attributes: %02Xh (", extendedAttributes) + nvarExtendedAttributesToUString(extendedAttributes) + UString(")"); // Add checksum if (!entry_body->_is_null_extended_header_checksum()) { UINT8 calculatedChecksum = 0; - UByteArray wholeBody = body + tail; // Include entry body - UINT8* start = (UINT8*)wholeBody.constData(); - for (UINT8* p = start; p < start + wholeBody.size(); p++) { + UINT8* start = (UINT8*)nvarData.constData(); + for (UINT8* p = start; p < start + headerSize + entry_body->data_size(); p++) { calculatedChecksum += *p; } // Include entry size and flags @@ -282,7 +295,11 @@ processing_done: } // Add tree item - UModelIndex varIndex = model->addItem(localOffset + entry->offset(), Types::NvarEntry, subtype, name, text, info, header, body, tail, Fixed, index); + UModelIndex varIndex = model->addItem( + localOffset + entry->offset(), Types::NvarEntry, subtype, + name, text, info, + headerSize, entry_body->data_size(), entry_body->extended_header_size(), + Fixed, index); currentEntryIndex++; // Set parsing data @@ -290,23 +307,25 @@ processing_done: // Try parsing the entry data as NVAR storage if it begins with NVAR signature if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry) - && body.size() >= 4 && readUnaligned((const UINT32*)body.constData()) == NVRAM_NVAR_ENTRY_SIGNATURE) + && entry_body->data_size() >= 4 && readUnaligned((const UINT32*)(nvarData.constData() + headerSize)) == NVRAM_NVAR_ENTRY_SIGNATURE) (void)parseNvarStore(varIndex); } } catch (...) { + if (!probe) + msg(usprintf("%s: unable to parse AMI NVAR storage", __FUNCTION__), index); return U_INVALID_STORE; } return U_SUCCESS; } -USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 fdcStoreSizeOverride) +USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex& index, const UINT32 fdcStoreSizeOverride) { // Sanity check if (!index.isValid()) return U_INVALID_PARAMETER; - + // Obtain required fields from parsing data UINT8 emptyByte = 0xFF; if (model->hasEmptyParsingData(index) == false) { @@ -314,10 +333,10 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData(); emptyByte = pdata->emptyByte; } - + // Get local offset - const UINT32 localOffset = (UINT32)model->header(index).size(); - + const UINT32 localOffset = (UINT32)model->headerSize(index); + // Get item data UByteArray volumeBody = model->body(index); const UINT32 volumeBodySize = (UINT32)volumeBody.size(); @@ -326,18 +345,18 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 UByteArray outerPadding; UINT32 previousStoreEndOffset = 0; for (UINT32 storeOffset = 0; - storeOffset < volumeBodySize; - storeOffset++) { + storeOffset < volumeBodySize; + storeOffset++) { UString name, text, info; - UByteArray header, body; - + UINT32 headerSize, bodySize; + // VSS try { if (volumeBodySize - storeOffset < sizeof(VSS_VARIABLE_STORE_HEADER)) { // No need to parse further, the rest of the volume is too small goto not_vss; } - + // Perform initial sanity check const VSS_VARIABLE_STORE_HEADER* storeHeader = (const VSS_VARIABLE_STORE_HEADER*)(volumeBody.constData() + storeOffset); if ((storeHeader->Signature != NVRAM_VSS_STORE_SIGNATURE @@ -348,10 +367,10 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 goto not_vss; } UINT32 storeSize = MIN(volumeBodySize - storeOffset, storeHeader->Size); //TODO: consider this check to become hard bail as it was before - + // This copy is required for possible FDC workaround UByteArray vss = volumeBody.mid(storeOffset, storeSize); - + // Check if we are here to parse a special case of FDC store with size override UINT32 originalStoreSize = 0; bool fdcHeaderSizeOverrideRequired = (fdcStoreSizeOverride > 0 && storeHeader->Signature == NVRAM_VSS_STORE_SIGNATURE && storeHeader->Size == 0xFFFFFFFF); @@ -360,12 +379,12 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 originalStoreSize = vssHeader->Size; vssHeader->Size = fdcStoreSizeOverride; } - + // Try parsing VSS store candidate umemstream is(vss.constData(), vss.size()); kaitai::kstream ks(&is); edk2_vss_t parsed(&ks); - + // Restore original store size, if needed if (fdcHeaderSizeOverrideRequired) { VSS_VARIABLE_STORE_HEADER* vssHeader = (VSS_VARIABLE_STORE_HEADER*)vss.data(); @@ -376,14 +395,18 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 // Check if we need to add a padding before it if (!outerPadding.isEmpty()) { UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); outerPadding.clear(); } - // Construct header and body - header = vss.left(parsed.len_vss_store_header()); - body = vss.mid(header.size(), storeSize - header.size()); - + // Obtain header and body size + headerSize = parsed.len_vss_store_header(); + bodySize = storeSize - headerSize; + // Add info if (parsed.signature() == NVRAM_APPLE_SVS_STORE_SIGNATURE) { name = UString("Apple SVS store"); @@ -394,25 +417,29 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 else { name = UString("VSS store"); } - + info = usprintf("Signature: %Xh (", parsed.signature()) + fourCC(parsed.signature()) + UString(")\n"); info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nFormat: %02Xh\nState: %02Xh\nReserved: %02Xh\nReserved1: %04Xh", - storeSize , storeSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), - parsed.format(), - parsed.state(), - parsed.reserved(), - parsed.reserved1()); - + storeSize, storeSize, + headerSize, headerSize, + bodySize, bodySize, + parsed.format(), + parsed.state(), + parsed.reserved(), + parsed.reserved1()); + // Add header tree item - UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::VssStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); - + UModelIndex headerIndex = model->addItem( + localOffset + storeOffset, Types::VssStore, 0, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, index); + // Add variables UINT32 entryOffset = parsed.len_vss_store_header(); - for (const auto & variable : *parsed.body()->variables()) { + for (const auto& variable : *parsed.body()->variables()) { UINT8 subtype; - + // This is the terminating entry, needs special processing if (variable->_is_null_signature_last()) { // Add free space or padding after all variables, if needed @@ -420,39 +447,48 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 UByteArray freeSpace = vss.mid(entryOffset, storeSize - entryOffset); // Add info info = usprintf("Full size: %Xh (%u)", (UINT32)freeSpace.size(), (UINT32)freeSpace.size()); - + // Check that remaining unparsed bytes are actually empty - if (freeSpace.count(emptyByte) == freeSpace.size()) { // Free space + auto c = checkSingle(freeSpace, (unsigned char)emptyByte); + if (c == emptyByte) { // Free space // Add tree item - model->addItem(entryOffset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + model->addItem( + entryOffset, Types::FreeSpace, 0, + UString("Free space"), UString(), info, + 0, freeSpace.size(), 0, + Fixed, headerIndex); } else { // Add tree item - model->addItem(entryOffset, Types::Padding, getPaddingType(freeSpace), UString("Padding"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + model->addItem( + entryOffset, Types::Padding, getPaddingType(freeSpace), + UString("Padding"), UString(), info, + 0, freeSpace.size(), 0, + Fixed, headerIndex); } } break; } - + // This is a normal entry UINT32 variableSize; if (variable->is_intel_legacy()) { // Intel legacy subtype = Subtypes::IntelVssEntry; // Needs some additional parsing of variable->intel_legacy_data to separate the name from the value text = uFromUcs2(variable->intel_legacy_data().c_str()); - UINT32 textLengthInBytes = (UINT32)text.length()*2+2; - header = vss.mid(entryOffset, variable->len_intel_legacy_header() + textLengthInBytes); - body = vss.mid(entryOffset + header.size(), variable->len_total() - variable->len_intel_legacy_header() - textLengthInBytes); - variableSize = (UINT32)(header.size() + body.size()); + UINT32 textLengthInBytes = (UINT32)text.length() * 2 + 2; + headerSize = variable->len_intel_legacy_header() + textLengthInBytes; + bodySize = variable->len_total() - headerSize; + variableSize = headerSize + bodySize; const EFI_GUID variableGuid = readUnaligned((const EFI_GUID*)(variable->vendor_guid().c_str())); name = guidToUString(variableGuid); info = UString("Variable GUID: ") + guidToUString(variableGuid, false) + "\n"; } else if (variable->is_auth()) { // Authenticated subtype = Subtypes::AuthVssEntry; - header = vss.mid(entryOffset, variable->len_auth_header() + variable->len_name_auth()); - body = vss.mid(entryOffset + header.size(), variable->len_data_auth()); - variableSize = (UINT32)(header.size() + body.size()); + headerSize = variable->len_auth_header() + variable->len_name_auth(); + bodySize = variable->len_data_auth(); + variableSize = headerSize + bodySize; const EFI_GUID variableGuid = readUnaligned((const EFI_GUID*)(variable->vendor_guid().c_str())); name = guidToUString(variableGuid); text = uFromUcs2(variable->name_auth().c_str()); @@ -460,9 +496,9 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 } else if (!variable->_is_null_apple_data_crc32()) { // Apple CRC32 subtype = Subtypes::AppleVssEntry; - header = vss.mid(entryOffset, variable->len_apple_header() + variable->len_name()); - body = vss.mid(entryOffset + header.size(), variable->len_data()); - variableSize = (UINT32)(header.size() + body.size()); + headerSize = variable->len_apple_header() + variable->len_name(); + bodySize = variable->len_data(); + variableSize = headerSize + bodySize; const EFI_GUID variableGuid = readUnaligned((const EFI_GUID*)(variable->vendor_guid().c_str())); name = guidToUString(variableGuid); text = uFromUcs2(variable->name().c_str()); @@ -470,79 +506,84 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 } else { // Standard subtype = Subtypes::StandardVssEntry; - header = vss.mid(entryOffset, variable->len_standard_header() + variable->len_name()); - body = vss.mid(entryOffset + header.size(), variable->len_data()); - variableSize = (UINT32)(header.size() + body.size()); + headerSize = variable->len_standard_header() + variable->len_name(); + bodySize = variable->len_data(); + variableSize = headerSize + bodySize; const EFI_GUID variableGuid = readUnaligned((const EFI_GUID*)(variable->vendor_guid().c_str())); name = guidToUString(variableGuid); text = uFromUcs2(variable->name().c_str()); info = UString("Variable GUID: ") + guidToUString(variableGuid, false) + "\n"; } - + // Override variable type to Invalid, if needed if (!variable->is_valid()) { subtype = Subtypes::InvalidVssEntry; name = UString("Invalid"); text.clear(); } - + const UINT32 variableAttributes = variable->attributes()->non_volatile() - + (variable->attributes()->boot_service() << 1) - + (variable->attributes()->runtime() << 2) - + (variable->attributes()->hw_error_record() << 3) - + (variable->attributes()->auth_write() << 4) - + (variable->attributes()->time_based_auth() << 5) - + (variable->attributes()->append_write() << 6) - + (UINT32)(variable->attributes()->reserved() << 7) - + (UINT32)(variable->attributes()->apple_data_checksum() << 31); - + + (variable->attributes()->boot_service() << 1) + + (variable->attributes()->runtime() << 2) + + (variable->attributes()->hw_error_record() << 3) + + (variable->attributes()->auth_write() << 4) + + (variable->attributes()->time_based_auth() << 5) + + (variable->attributes()->append_write() << 6) + + (UINT32)(variable->attributes()->reserved() << 7) + + (UINT32)(variable->attributes()->apple_data_checksum() << 31); + // Add generic info info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nReserved: %02Xh\nAttributes: %08Xh (", variableSize, variableSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + headerSize, headerSize, + bodySize, bodySize, variable->state(), variable->reserved(), variableAttributes) + vssAttributesToUString(variableAttributes) + UString(")"); - + // Add specific info if (variable->is_auth()) { UINT64 monotonicCounter = (UINT64)variable->len_name() + ((UINT64)variable->len_data() << 32); info += usprintf("\nMonotonic counter: %" PRIX64 "h\nTimestamp: ", monotonicCounter) + efiTimeToUString(*(const EFI_TIME*)variable->timestamp().c_str()) - + usprintf("\nPubKey index: %u", variable->pubkey_index()); + + usprintf("\nPubKey index: %u", variable->pubkey_index()); } else if (!variable->_is_null_apple_data_crc32()) { // Calculate CRC32 of the variable data - UINT32 calculatedCrc32 = (UINT32)crc32(0, (const UINT8*)body.constData(), (uInt)body.size()); - + UINT32 calculatedCrc32 = (UINT32)crc32(0, (const UINT8*)(vss.constData() + entryOffset + headerSize), bodySize); + info += usprintf("\nData checksum: %08Xh", variable->apple_data_crc32()) + - (variable->apple_data_crc32() != calculatedCrc32 ? usprintf(", invalid, should be %08Xh", calculatedCrc32) : UString(", valid")); + (variable->apple_data_crc32() != calculatedCrc32 ? usprintf(", invalid, should be %08Xh", calculatedCrc32) : UString(", valid")); } - + // Add tree item - model->addItem(entryOffset, Types::VssEntry, subtype, name, text, info, header, body, UByteArray(), Fixed, headerIndex); - + model->addItem( + entryOffset, Types::VssEntry, subtype, + name, text, info, + headerSize, bodySize, 0, + Fixed, headerIndex); + entryOffset += variableSize; } - + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { - // Parsing failed, try something else } -not_vss: + catch (...) { + // Parsing failed, try something else + } + not_vss: // VSS2 try { if (volumeBodySize - storeOffset < sizeof(VSS2_VARIABLE_STORE_HEADER)) { // No need to parse further, the rest of the volume is too small goto not_vss2; } - + // Perform initial sanity check const VSS2_VARIABLE_STORE_HEADER* storeHeader = (const VSS2_VARIABLE_STORE_HEADER*)(volumeBody.constData() + storeOffset); UByteArray guid = UByteArray((const char*)&storeHeader->Signature, sizeof(EFI_GUID)); - + if ((guid != NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID && guid != NVRAM_VSS2_STORE_GUID && guid != NVRAM_FDC_STORE_GUID) @@ -551,10 +592,10 @@ not_vss: goto not_vss2; } UINT32 storeSize = MIN(volumeBodySize - storeOffset, storeHeader->Size); - + // This copy is required for possible FDC workaround UByteArray vss2 = volumeBody.mid(storeOffset, storeSize); - + // Check if we are here to parse a special case of FDC store with size override UINT32 originalStoreSize = 0; bool fdcHeaderSizeOverrideRequired = (fdcStoreSizeOverride > 0 && guid == NVRAM_FDC_STORE_GUID && storeHeader->Size == 0xFFFFFFFF); @@ -563,30 +604,34 @@ not_vss: originalStoreSize = vss2Header->Size; vss2Header->Size = fdcStoreSizeOverride; } - + // Try parsing VSS store candidate umemstream is(vss2.constData(), vss2.size()); kaitai::kstream ks(&is); edk2_vss2_t parsed(&ks); - + // Restore original store size, if needed if (fdcHeaderSizeOverrideRequired) { VSS2_VARIABLE_STORE_HEADER* vss2Header = (VSS2_VARIABLE_STORE_HEADER*)vss2.data(); vss2Header->Size = originalStoreSize; } - + // VSS2 store at current offset parsed correctly // Check if we need to add a padding before it if (!outerPadding.isEmpty()) { info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); outerPadding.clear(); } - // Construct header and body - header = vss2.left(parsed.len_vss2_store_header()); - body = vss2.mid(header.size(), storeSize - header.size()); - + // Obtain header and body size + headerSize = parsed.len_vss2_store_header(); + bodySize = storeSize - headerSize; + // Add info name = UString("VSS2 store"); if (guid == NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID) { @@ -598,24 +643,28 @@ not_vss: else { info = UString("Signature: DDCF3617-3275-4164-98B6-FE85707FFE7D\n"); } - + info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nFormat: %02Xh\nState: %02Xh\nReserved: %02Xh\nReserved1: %04Xh", - storeSize, storeSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), - parsed.format(), - parsed.state(), - parsed.reserved(), - parsed.reserved1()); - + storeSize, storeSize, + headerSize, headerSize, + bodySize, bodySize, + parsed.format(), + parsed.state(), + parsed.reserved(), + parsed.reserved1()); + // Add header tree item - UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::Vss2Store, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); - + UModelIndex headerIndex = model->addItem( + localOffset + storeOffset, Types::Vss2Store, 0, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, index); + // Add variables UINT32 entryOffset = parsed.len_vss2_store_header(); - for (const auto & variable : *parsed.body()->variables()) { + for (const auto& variable : *parsed.body()->variables()) { UINT8 subtype; - + // This is the terminating entry, needs special processing if (variable->_is_null_signature_last()) { // Add free space or padding after all variables, if needed @@ -623,28 +672,37 @@ not_vss: UByteArray freeSpace = vss2.mid(entryOffset, storeSize - entryOffset); // Add info info = usprintf("Full size: %Xh (%u)", (UINT32)freeSpace.size(), (UINT32)freeSpace.size()); - + // Check that remaining unparsed bytes are actually empty - if (freeSpace.count(emptyByte) == freeSpace.size()) { // Free space + auto c = checkSingle(freeSpace, (unsigned char)emptyByte); + if (c == emptyByte) { // Free space // Add tree item - model->addItem(entryOffset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + model->addItem( + entryOffset, Types::FreeSpace, 0, + UString("Free space"), UString(), info, + 0, freeSpace.size(), 0, + Fixed, headerIndex); } else { // Add tree item - model->addItem(entryOffset, Types::Padding, getPaddingType(freeSpace), UString("Padding"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + model->addItem( + entryOffset, Types::Padding, getPaddingType(freeSpace), + UString("Padding"), UString(), info, + 0, freeSpace.size(), 0, + Fixed, headerIndex); } } break; } - + // This is a normal entry UINT32 variableSize; UINT32 alignmentSize; if (variable->is_auth()) { // Authenticated subtype = Subtypes::AuthVssEntry; - header = vss2.mid(entryOffset, variable->len_auth_header() + variable->len_name_auth()); - body = vss2.mid(entryOffset + header.size(), variable->len_data_auth()); - variableSize = (UINT32)(header.size() + body.size()); + headerSize = variable->len_auth_header() + variable->len_name_auth(); + bodySize = variable->len_data_auth(); + variableSize = headerSize + bodySize; alignmentSize = variable->len_alignment_padding_auth(); const EFI_GUID variableGuid = readUnaligned((const EFI_GUID*)(variable->vendor_guid().c_str())); name = guidToUString(variableGuid); @@ -653,66 +711,71 @@ not_vss: } else { // Standard subtype = Subtypes::StandardVssEntry; - header = vss2.mid(entryOffset, variable->len_standard_header() + variable->len_name()); - body = vss2.mid(entryOffset + header.size(), variable->len_data()); - variableSize = (UINT32)(header.size() + body.size()); + headerSize = variable->len_standard_header() + variable->len_name(); + bodySize = variable->len_data(); + variableSize = headerSize + bodySize; alignmentSize = variable->len_alignment_padding(); const EFI_GUID variableGuid = readUnaligned((const EFI_GUID*)(variable->vendor_guid().c_str())); name = guidToUString(variableGuid); text = uFromUcs2(variable->name().c_str()); info = UString("Variable GUID: ") + guidToUString(variableGuid, false) + "\n"; } - + // Override variable type to Invalid if needed if (!variable->is_valid()) { subtype = Subtypes::InvalidVssEntry; name = UString("Invalid"); text.clear(); } - + const UINT32 variableAttributes = variable->attributes()->non_volatile() - + (variable->attributes()->boot_service() << 1) - + (variable->attributes()->runtime() << 2) - + (variable->attributes()->hw_error_record() << 3) - + (variable->attributes()->auth_write() << 4) - + (variable->attributes()->time_based_auth() << 5) - + (variable->attributes()->append_write() << 6) - + (UINT32)(variable->attributes()->reserved() << 7); - + + (variable->attributes()->boot_service() << 1) + + (variable->attributes()->runtime() << 2) + + (variable->attributes()->hw_error_record() << 3) + + (variable->attributes()->auth_write() << 4) + + (variable->attributes()->time_based_auth() << 5) + + (variable->attributes()->append_write() << 6) + + (UINT32)(variable->attributes()->reserved() << 7); + // Add generic info info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nReserved: %02Xh\nAttributes: %08Xh (", variableSize, variableSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + headerSize, headerSize, + bodySize, bodySize, variable->state(), variable->reserved(), variableAttributes) + vssAttributesToUString(variableAttributes) + UString(")"); - + // Add specific info if (variable->is_auth()) { UINT64 monotonicCounter = (UINT64)variable->len_name() + ((UINT64)variable->len_data() << 32); info += usprintf("\nMonotonic counter: %" PRIX64 "h\nTimestamp: ", monotonicCounter) + efiTimeToUString(*(const EFI_TIME*)variable->timestamp().c_str()) - + usprintf("\nPubKey index: %u", variable->pubkey_index()); + + usprintf("\nPubKey index: %u", variable->pubkey_index()); } - + // Add tree item - model->addItem(entryOffset, Types::VssEntry, subtype, name, text, info, header, body, UByteArray(), Fixed, headerIndex); - + model->addItem( + entryOffset, Types::VssEntry, subtype, + name, text, info, + headerSize, bodySize, 0, + Fixed, headerIndex); + entryOffset += (variableSize + alignmentSize); } - + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { - // Parsing failed, try something else } -not_vss2: + catch (...) { + // Parsing failed, try something else + } + not_vss2: // Do not try any other parsers if we are here for FDC store parsing if (fdcStoreSizeOverride != 0) { continue; } - + // FTW try { if (volumeBodySize - storeOffset < sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32)) { @@ -743,20 +806,20 @@ not_vss2: goto not_ftw; } storeSize = MIN(volumeBodySize - storeOffset, storeSize); - + umemstream is(volumeBody.constData() + storeOffset, storeSize); kaitai::kstream ks(&is); edk2_ftw_t parsed(&ks); - + // Construct header and calculate header checksum - UINT32 headerSize; UINT32 calculatedCrc; + UByteArray crcHeader = volumeBody.mid(storeOffset, std::max( + sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32), sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64))); if (parsed._is_null_len_write_queue_64()) { headerSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32); - header = volumeBody.mid(storeOffset, headerSize); - + + // Check block header checksum - UByteArray crcHeader = header; EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)crcHeader.data(); crcFtwBlockHeader->Crc = emptyByte ? 0xFFFFFFFF : 0; crcFtwBlockHeader->State = emptyByte ? 0xFF : 0; @@ -764,47 +827,52 @@ not_vss2: } else { headerSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64); - header = volumeBody.mid(storeOffset, headerSize); - + // Check block header checksum - UByteArray crcHeader = header; EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)crcHeader.data(); crcFtwBlockHeader->Crc = emptyByte ? 0xFFFFFFFF : 0; crcFtwBlockHeader->State = emptyByte ? 0xFF : 0; calculatedCrc = (UINT32)crc32(0, (const UINT8*)crcFtwBlockHeader, (UINT32)headerSize); } - + bodySize = storeSize - headerSize; + // FTW store at current offset parsed correctly // Check if we need to add a padding before it if (!outerPadding.isEmpty()) { UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); outerPadding.clear(); } - - // Construct body - body = volumeBody.mid(storeOffset + header.size(), storeSize - header.size()); - + // Add info name = UString("FTW store"); info = UString("Signature: ") + guidToUString(*(const EFI_GUID*)guid.constData(), false); info += usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nHeader CRC32: %08Xh", - (UINT32)storeSize, (UINT32)storeSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + storeSize, storeSize, + headerSize, headerSize, + bodySize, bodySize, parsed.state(), parsed.crc()) + (parsed.crc() != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); - + // Add header tree item - model->addItem(localOffset + storeOffset, Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); - + model->addItem( + localOffset + storeOffset, Types::FtwStore, 0, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, index); + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { + } + catch (...) { // Parsing failed, try something else } -not_ftw: + not_ftw: // Insyde FDC try { if (volumeBodySize - storeOffset < sizeof(INSYDE_FDC_STORE_HEADER)) { @@ -818,43 +886,52 @@ not_ftw: goto not_fdc; } UINT32 storeSize = MIN(volumeBodySize - storeOffset, storeHeader->Size); - + umemstream is(volumeBody.constData() + storeOffset, storeSize); kaitai::kstream ks(&is); insyde_fdc_t parsed(&ks); - + // Insyde FDC store at current offset parsed correctly // Check if we need to add a padding before it if (!outerPadding.isEmpty()) { UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); outerPadding.clear(); } - - // Construct header and body - header = volumeBody.mid(storeOffset, sizeof(INSYDE_FDC_STORE_HEADER)); - body = volumeBody.mid(storeOffset + header.size(), storeSize - header.size()); - + + // Obtain header and body size + headerSize = sizeof(INSYDE_FDC_STORE_HEADER); + bodySize = storeSize - headerSize; + // Add info name = UString("Insyde FDC store"); info = usprintf("Signature: _FDC\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)", - storeSize, storeSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size()); - + storeSize, storeSize, + headerSize, headerSize, + bodySize, bodySize); + // Add header tree item - UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); - + UModelIndex headerIndex = model->addItem( + localOffset + storeOffset, Types::FdcStore, 0, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, index); + // Parse FDC body as normal VSS/VSS2 storage with size override - parseNvramVolumeBody(headerIndex, (UINT32)body.size()); - + parseNvramVolumeBody(headerIndex, bodySize); + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { + } + catch (...) { // Parsing failed, try something else } -not_fdc: + not_fdc: // Apple SysF try { if (volumeBodySize - storeOffset < sizeof(APPLE_SYSF_STORE_HEADER)) { @@ -869,26 +946,30 @@ not_fdc: goto not_sysf; } UINT32 storeSize = MIN(volumeBodySize - storeOffset, storeHeader->Size); - + umemstream is(volumeBody.constData() + storeOffset, storeSize); kaitai::kstream ks(&is); apple_sysf_t parsed(&ks); - + // Apple SysF/Diag store at current offset parsed correctly // Check if we need to add a padding before it if (!outerPadding.isEmpty()) { info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); outerPadding.clear(); } - - // Construct header and body - header = volumeBody.mid(storeOffset, sizeof(APPLE_SYSF_STORE_HEADER)); - body = volumeBody.mid(storeOffset + header.size(), storeSize - header.size()); - + + // Obtain header and body size + headerSize = sizeof(APPLE_SYSF_STORE_HEADER); + bodySize = storeSize - headerSize; + // Check store checksum UINT32 calculatedCrc = (UINT32)crc32(0, (const UINT8*)(volumeBody.constData() + storeOffset), storeSize - sizeof(UINT32)); - + // Add info if (storeHeader->Signature == NVRAM_APPLE_SYSF_STORE_SIGNATURE) { name = UString("Apple SysF store"); @@ -899,21 +980,25 @@ not_fdc: info = UString("Signature: Gaid\n"); } info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nUnknown: %02Xh\nUnknown1: %08Xh\nCRC32: %08Xh", - storeSize, storeSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), - parsed.unknown(), - parsed.unknown1(), - parsed.crc()) + (parsed.crc() != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); - + storeSize, storeSize, + headerSize, headerSize, + bodySize, bodySize, + parsed.unknown(), + parsed.unknown1(), + parsed.crc()) + (parsed.crc() != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid")); + // Add header tree item - UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::SysFStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); - + UModelIndex headerIndex = model->addItem( + localOffset + storeOffset, Types::SysFStore, 0, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, index); + // Add variables UINT32 entryOffset = sizeof(APPLE_SYSF_STORE_HEADER); - for (const auto & variable : *parsed.body()->variables()) { + for (const auto& variable : *parsed.body()->variables()) { UINT8 subtype; - + if (variable->invalid_flag()) { subtype = Subtypes::InvalidSysFEntry; name = UString("Invalid"); @@ -922,51 +1007,66 @@ not_fdc: subtype = Subtypes::NormalSysFEntry; name = usprintf("%s", variable->name().c_str()); } - + if (variable->len_name() == 3 && variable->name() == "EOF") { - header = volumeBody.mid(storeOffset + entryOffset, 4); + headerSize = 4; + ///??? Where is the body? } else { - header = volumeBody.mid(storeOffset + entryOffset, sizeof(UINT8) + (UINT32)variable->len_name() + sizeof(UINT16)); - body = volumeBody.mid(storeOffset + entryOffset + header.size(), (UINT32)variable->len_data()); + headerSize = sizeof(UINT8) + (UINT32)variable->len_name() + sizeof(UINT16); + bodySize = (UINT32)variable->len_data(); } // Add generic info - UINT32 variableSize = (UINT32)header.size() + (UINT32)body.size(); + UINT32 variableSize = headerSize + bodySize; info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\n", - variableSize, variableSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size()); - + variableSize, variableSize, + headerSize, headerSize, + bodySize, bodySize); + // Add tree item - model->addItem(entryOffset, Types::SysFEntry, subtype, name, UString(), info, header, body, UByteArray(), Fixed, headerIndex); - + model->addItem( + entryOffset, Types::SysFEntry, subtype, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, headerIndex); + entryOffset += variableSize; } - + // Add free space or padding after all variables, if needed if (entryOffset < storeSize) { UByteArray freeSpace = volumeBody.mid(storeOffset + entryOffset, storeSize - entryOffset); // Add info info = usprintf("Full size: %Xh (%u)", (UINT32)freeSpace.size(), (UINT32)freeSpace.size()); - + // Check that remaining unparsed bytes are actually zeroes - if (freeSpace.count('\x00') == freeSpace.size() - 4) { // Free space, 4 last bytes are always CRC32 + auto c = checkSingle(freeSpace.left(freeSpace.size() - 4), 0); // Free space, 4 last bytes are always CRC32 + if (c == 0) { // Add tree item - model->addItem(entryOffset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + model->addItem( + entryOffset, Types::FreeSpace, 0, + UString("Free space"), UString(), info, + 0, freeSpace.size(), 0, + Fixed, headerIndex); } else { // Add tree item - model->addItem(entryOffset, Types::Padding, getPaddingType(freeSpace), UString("Padding"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + model->addItem( + entryOffset, Types::Padding, getPaddingType(freeSpace), + UString("Padding"), UString(), info, + 0, freeSpace.size(), 0, + Fixed, headerIndex); } } - + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { + } + catch (...) { // Parsing failed, try something else } -not_sysf: + not_sysf: // Phoenix Flash Map try { if (volumeBodySize - storeOffset < sizeof(PHOENIX_FLASH_MAP_HEADER)) { @@ -981,40 +1081,47 @@ not_sysf: goto not_flm; } UINT32 storeSize = sizeof(PHOENIX_FLASH_MAP_HEADER) + storeHeader->NumEntries * sizeof(PHOENIX_FLASH_MAP_ENTRY); - + umemstream is(volumeBody.constData() + storeOffset, storeSize); kaitai::kstream ks(&is); phoenix_flm_t parsed(&ks); - + // Phoenix FlashMap store at current offset parsed correctly // Check if we need to add a padding before it if (!outerPadding.isEmpty()) { info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); outerPadding.clear(); } - + // Construct header and body - header = volumeBody.left(storeOffset + sizeof(PHOENIX_FLASH_MAP_HEADER)); - body = volumeBody.mid(storeOffset + header.size(), storeSize - header.size()); - + headerSize = sizeof(PHOENIX_FLASH_MAP_HEADER); ///??? was erroneous "header = volumeBody.left(storeOffset + sizeof(PHOENIX_FLASH_MAP_HEADER))" in original + bodySize = storeSize - headerSize; // Add info name = UString("Phoenix SCT flash map"); info = usprintf("Signature: _FLASH_MAP\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nEntries: %u\nReserved: %08Xh", - storeSize, storeSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), - parsed.num_entries(), - parsed.reserved()); - + storeSize, storeSize, + headerSize, headerSize, + bodySize, bodySize, + parsed.num_entries(), + parsed.reserved()); + // Add header tree item - UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::PhoenixFlashMapStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); - + UModelIndex headerIndex = model->addItem( + localOffset + storeOffset, Types::PhoenixFlashMapStore, 0, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, index); + // Add entries UINT32 entryOffset = sizeof(PHOENIX_FLASH_MAP_HEADER); - for (const auto & entry : *parsed.entries()) { + for (const auto& entry : *parsed.entries()) { UINT8 subtype; - + if (entry->data_type() == NVRAM_PHOENIX_FLASH_MAP_ENTRY_DATA_TYPE_VOLUME) { subtype = Subtypes::VolumeFlashMapEntry; } @@ -1024,36 +1131,41 @@ not_sysf: else { subtype = Subtypes::UnknownFlashMapEntry; } - + const EFI_GUID guid = readUnaligned((const EFI_GUID*)entry->guid().c_str()); name = guidToUString(guid); text = phoenixFlashMapGuidToUString(guid); - header = volumeBody.mid(storeOffset + entryOffset, sizeof(PHOENIX_FLASH_MAP_ENTRY)); + headerSize = sizeof(PHOENIX_FLASH_MAP_ENTRY); // Add info - UINT32 entrySize = (UINT32)header.size(); + UINT32 entrySize = headerSize; info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: 0h (0)\nData type: %04Xh\nEntry type: %04Xh\nSize: %08Xh\nOffset: %08Xh\nPhysical address: %" PRIX64 "h", entrySize, entrySize, - (UINT32)header.size(), (UINT32)header.size(), + headerSize, headerSize, entry->data_type(), entry->entry_type(), entry->size(), entry->offset(), entry->physical_address()); - + // Add tree item - model->addItem(entryOffset, Types::PhoenixFlashMapEntry, subtype, name, text, info, header, UByteArray(), UByteArray(), Fixed, headerIndex); - + model->addItem( + entryOffset, Types::PhoenixFlashMapEntry, subtype, + name, text, info, + headerSize, 0, 0, + Fixed, headerIndex); + entryOffset += entrySize; } - + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { + } + catch (...) { // Parsing failed, try something else } -not_flm: + not_flm: // Phoenix EVSA store try { if (volumeBodySize - storeOffset < sizeof(EVSA_STORE_ENTRY)) { @@ -1069,48 +1181,56 @@ not_flm: goto not_evsa; } UINT32 storeSize = MIN(volumeBodySize - storeOffset, storeHeader->StoreSize); - + umemstream is(volumeBody.constData() + storeOffset, storeSize); kaitai::kstream ks(&is); phoenix_evsa_t parsed(&ks); - + // Phoenix EVSA store at current offset parsed correctly // Check if we need to add a padding before it if (!outerPadding.isEmpty()) { info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); outerPadding.clear(); } - - // Construct header and body - header = volumeBody.mid(storeOffset, sizeof(EVSA_STORE_ENTRY)); - body = volumeBody.mid(storeOffset + header.size(), storeSize - header.size()); - + + // Obtain header and body size + headerSize = sizeof(EVSA_STORE_ENTRY); + bodySize = storeSize - headerSize; + // Calculate header checksum UINT8 calculated = calculateChecksum8(((const UINT8*)storeHeader) + 2, storeHeader->Header.Size - 2); - + // Add info name = UString("Phoenix EVSA store"); info = usprintf("Signature: EVSA\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nAttributes: %08Xh\nReserved: %08Xh\nChecksum: %02Xh", storeSize, storeSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + headerSize, headerSize, + bodySize, bodySize, parsed.attributes(), parsed.reserved(), parsed.checksum()) - + (parsed.checksum() != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")); - + + (parsed.checksum() != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")); + // Add header tree item - UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::EvsaStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); - + UModelIndex headerIndex = model->addItem( + localOffset + storeOffset, Types::EvsaStore, 0, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, index); + // Add entries std::map<UINT16, EFI_GUID> guidMap; std::map<UINT16, UString> nameMap; UINT32 entryOffset = parsed.len_evsa_store_header(); - for (const auto & entry : *parsed.body()->entries()) { + for (const auto& entry : *parsed.body()->entries()) { UINT8 subtype; UINT32 entrySize; - + // This is the terminating entry, needs special processing if (entry->_is_null_checksum()) { // Add free space or padding after all variables, if needed @@ -1118,114 +1238,127 @@ not_flm: UByteArray freeSpace = volumeBody.mid(storeOffset + entryOffset, storeSize - entryOffset); // Add info info = usprintf("Full size: %Xh (%u)", (UINT32)freeSpace.size(), (UINT32)freeSpace.size()); - + // Check that remaining unparsed bytes are actually empty - if (freeSpace.count(emptyByte) == freeSpace.size()) { // Free space + auto c = checkSingle(freeSpace, (unsigned char)emptyByte); + if (c == emptyByte) { // Free space // Add tree item - model->addItem(entryOffset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + model->addItem( + entryOffset, Types::FreeSpace, 0, + UString("Free space"), UString(), info, + 0, freeSpace.size(), 0, + Fixed, headerIndex); } else { // Add tree item - model->addItem(entryOffset, Types::Padding, getPaddingType(freeSpace), UString("Padding"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + model->addItem( + entryOffset, Types::Padding, getPaddingType(freeSpace), + UString("Padding"), UString(), info, + 0, freeSpace.size(), 0, + Fixed, headerIndex); } } break; } - + const EVSA_ENTRY_HEADER* entryHeader = (const EVSA_ENTRY_HEADER*)(volumeBody.constData() + storeOffset + entryOffset); calculated = calculateChecksum8(((const UINT8*)entryHeader) + 2, entryHeader->Size - 2); - + // GUID entry if (entry->entry_type() == NVRAM_EVSA_ENTRY_TYPE_GUID1 || entry->entry_type() == NVRAM_EVSA_ENTRY_TYPE_GUID2) { const phoenix_evsa_t::evsa_guid_t* guidEntry = (const phoenix_evsa_t::evsa_guid_t*)(entry->body()); - header = volumeBody.mid(storeOffset + entryOffset, sizeof(EVSA_GUID_ENTRY)); - body = volumeBody.mid(storeOffset + entryOffset + sizeof(EVSA_GUID_ENTRY), entry->len_evsa_entry() - header.size()); - entrySize = (UINT32)(header.size() + body.size()); + headerSize = sizeof(EVSA_GUID_ENTRY); + bodySize = entry->len_evsa_entry() - headerSize; + entrySize = headerSize + bodySize; EFI_GUID guid = *(const EFI_GUID*)(guidEntry->guid().c_str()); name = guidToUString(guid); info = UString("GUID: ") + guidToUString(guid, false) - + usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh", - entrySize, entrySize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), - entry->entry_type(), - entry->checksum()) - + (entry->checksum() != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) - + usprintf("\nGuidId: %04Xh", guidEntry->guid_id()); + + usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh", + entrySize, entrySize, + headerSize, headerSize, + bodySize, bodySize, + entry->entry_type(), + entry->checksum()) + + (entry->checksum() != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) + + usprintf("\nGuidId: %04Xh", guidEntry->guid_id()); subtype = Subtypes::GuidEvsaEntry; guidMap.insert(std::pair<UINT16, EFI_GUID>(guidEntry->guid_id(), guid)); } // Name entry else if (entry->entry_type() == NVRAM_EVSA_ENTRY_TYPE_NAME1 || entry->entry_type() == NVRAM_EVSA_ENTRY_TYPE_NAME2) { const phoenix_evsa_t::evsa_name_t* nameEntry = (const phoenix_evsa_t::evsa_name_t*)(entry->body()); - header = volumeBody.mid(storeOffset + entryOffset, sizeof(EVSA_NAME_ENTRY)); - body = volumeBody.mid(storeOffset + entryOffset + sizeof(EVSA_NAME_ENTRY), entry->len_evsa_entry() - header.size()); - entrySize = (UINT32)(header.size() + body.size()); - name = uFromUcs2(body.constData()); + headerSize = sizeof(EVSA_NAME_ENTRY); + bodySize = entry->len_evsa_entry() - headerSize; + entrySize = headerSize + bodySize; + name = uFromUcs2(volumeBody.constData() + storeOffset + entryOffset + headerSize); info = UString("Name: ") + name - + usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh", - entrySize, entrySize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), - entry->entry_type(), - entry->checksum()) - + (entry->checksum() != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) - + usprintf("\nVarId: %04Xh", nameEntry->var_id()); + + usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh", + entrySize, entrySize, + headerSize, headerSize, + bodySize, bodySize, + entry->entry_type(), + entry->checksum()) + + (entry->checksum() != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) + + usprintf("\nVarId: %04Xh", nameEntry->var_id()); subtype = Subtypes::NameEvsaEntry; nameMap.insert(std::pair<UINT16, UString>(nameEntry->var_id(), name)); } // Data entry else if (entry->entry_type() == NVRAM_EVSA_ENTRY_TYPE_DATA1 - || entry->entry_type() == NVRAM_EVSA_ENTRY_TYPE_DATA2 - || entry->entry_type() == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) { + || entry->entry_type() == NVRAM_EVSA_ENTRY_TYPE_DATA2 + || entry->entry_type() == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) { phoenix_evsa_t::evsa_data_t* dataEntry = (phoenix_evsa_t::evsa_data_t*)(entry->body()); if (dataEntry->_is_null_len_data_ext()) { - header = volumeBody.mid(storeOffset + entryOffset, sizeof(EVSA_DATA_ENTRY)); - body = volumeBody.mid(storeOffset + entryOffset + sizeof(EVSA_DATA_ENTRY), entry->len_evsa_entry() - header.size()); + headerSize = sizeof(EVSA_DATA_ENTRY); + bodySize = entry->len_evsa_entry() - headerSize; } else { - header = volumeBody.mid(storeOffset + entryOffset, sizeof(EVSA_DATA_ENTRY_EXTENDED)); - body = volumeBody.mid(storeOffset + entryOffset + sizeof(EVSA_DATA_ENTRY_EXTENDED), dataEntry->len_data_ext()); + headerSize = sizeof(EVSA_DATA_ENTRY_EXTENDED); + bodySize = dataEntry->len_data_ext(); } - entrySize = (UINT32)(header.size() + body.size()); + entrySize = headerSize + bodySize; name = UString("Data"); subtype = Subtypes::DataEvsaEntry; - + const UINT32 attributes = dataEntry->attributes()->non_volatile() - + (dataEntry->attributes()->boot_service() << 1) - + (dataEntry->attributes()->runtime() << 2) - + (dataEntry->attributes()->hw_error_record() << 3) - + (dataEntry->attributes()->auth_write() << 4) - + (dataEntry->attributes()->time_based_auth() << 5) - + (dataEntry->attributes()->append_write() << 6) - + (UINT32)(dataEntry->attributes()->reserved() << 7) - + (dataEntry->attributes()->extended_header() << 28) - + (UINT32)(dataEntry->attributes()->reserved1() << 29); - + + (dataEntry->attributes()->boot_service() << 1) + + (dataEntry->attributes()->runtime() << 2) + + (dataEntry->attributes()->hw_error_record() << 3) + + (dataEntry->attributes()->auth_write() << 4) + + (dataEntry->attributes()->time_based_auth() << 5) + + (dataEntry->attributes()->append_write() << 6) + + (UINT32)(dataEntry->attributes()->reserved() << 7) + + (dataEntry->attributes()->extended_header() << 28) + + (UINT32)(dataEntry->attributes()->reserved1() << 29); + info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh", entrySize, entrySize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), + headerSize, headerSize, + bodySize, bodySize, entry->entry_type(), entry->checksum()) - + (entry->checksum() != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) - + usprintf("\nVarId: %04Xh\nGuidId: %04Xh\nAttributes: %08Xh (", - dataEntry->var_id(), - dataEntry->guid_id(), - attributes) - + evsaAttributesToUString(attributes) + UString(")"); + + (entry->checksum() != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid")) + + usprintf("\nVarId: %04Xh\nGuidId: %04Xh\nAttributes: %08Xh (", + dataEntry->var_id(), + dataEntry->guid_id(), + attributes) + + evsaAttributesToUString(attributes) + UString(")"); } - + // Add tree item - model->addItem(entryOffset, Types::EvsaEntry, subtype, name, text, info, header, body, UByteArray(), Fixed, headerIndex); - + model->addItem( + entryOffset, Types::EvsaEntry, subtype, + name, text, info, + headerSize, bodySize, 0, + Fixed, headerIndex); + entryOffset += entrySize; } - + // Reparse all data variables to detect invalid ones and assign name and test to valid ones for (int i = 0; i < model->rowCount(headerIndex); i++) { UModelIndex current = headerIndex.model()->index(i, 0, headerIndex); - + if (model->subtype(current) == Subtypes::DataEvsaEntry) { UByteArray header = model->header(current); const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)header.constData(); @@ -1235,7 +1368,7 @@ not_flm: UString name; if (nameMap.count(dataHeader->VarId)) name = nameMap[dataHeader->VarId]; - + // Check for variable validity if (guid.isEmpty() && name.isEmpty()) { // Both name and guid aren't found model->setSubtype(current, Subtypes::InvalidEvsaEntry); @@ -1269,14 +1402,15 @@ not_flm: } } } - + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { + } + catch (...) { // Parsing failed, try something else } - not_evsa: + not_evsa: // Phoenix CMDB store try { if (volumeBodySize - storeOffset < NVRAM_PHOENIX_CMDB_SIZE) { @@ -1290,36 +1424,45 @@ not_flm: goto not_cmdb; } UINT32 storeSize = NVRAM_PHOENIX_CMDB_SIZE; - + // CMDB store at current offset parsed correctly // Check if we need to add a padding before it if (!outerPadding.isEmpty()) { info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); outerPadding.clear(); } - - // Construct header and body - header = volumeBody.mid(storeOffset, storeHeader->TotalSize); - body = volumeBody.mid(storeOffset + header.size(), storeSize - header.size()); - + + // Obtain header and body size + headerSize = storeHeader->TotalSize; + bodySize = storeSize - headerSize; + // Add info name = UString("Phoenix CMDB store"); info = usprintf("Signature: CMDB\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)", storeSize, storeSize, - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size()); - + headerSize, headerSize, + bodySize, bodySize); + // Add tree item - model->addItem(localOffset + storeOffset, Types::CmdbStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); - + model->addItem( + localOffset + storeOffset, Types::CmdbStore, 0, + name, UString(), info, + headerSize, bodySize, 0, + Fixed, index); + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { + } + catch (...) { // Parsing failed, try something else } -not_cmdb: + not_cmdb: // SLIC PubKey try { if (volumeBodySize - storeOffset < sizeof(OEM_ACTIVATION_PUBKEY)) { @@ -1335,22 +1478,26 @@ not_cmdb: goto not_pubkey; } UINT32 storeSize = sizeof(OEM_ACTIVATION_PUBKEY); - + umemstream is(volumeBody.constData() + storeOffset, storeSize); kaitai::kstream ks(&is); ms_slic_pubkey_t parsed(&ks); - + // SLIC PubKey at current offset parsed correctly // Check if we need to add a padding before it if (!outerPadding.isEmpty()) { info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); outerPadding.clear(); } - - // Construct header - header = volumeBody.mid(storeOffset, storeSize); - + + // Obtain header size + headerSize = storeSize; + // Add info name = UString("SLIC pubkey"); info = usprintf("Type: 0h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: 0h (0)\n" @@ -1362,17 +1509,22 @@ not_cmdb: parsed.algorithm(), parsed.bit_length(), parsed.exponent()); - + // Add tree item - model->addItem(localOffset + storeOffset, Types::SlicData, Subtypes::PubkeySlicData, name, UString(), info, header, UByteArray(), UByteArray(), Fixed, index); - + model->addItem( + localOffset + storeOffset, Types::SlicData, Subtypes::PubkeySlicData, + name, UString(), info, + headerSize, 0, 0, + Fixed, index); + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { + } + catch (...) { // Parsing failed, try something else } -not_pubkey: + not_pubkey: // SLIC marker try { if (volumeBodySize - storeOffset < sizeof(OEM_ACTIVATION_MARKER)) { @@ -1395,22 +1547,26 @@ not_pubkey: } } UINT32 storeSize = sizeof(OEM_ACTIVATION_MARKER); - + umemstream is(volumeBody.constData() + storeOffset, storeSize); kaitai::kstream ks(&is); ms_slic_marker_t parsed(&ks); - + // SLIC marker at current offset parsed correctly // Check if we need to add a padding before it if (!outerPadding.isEmpty()) { info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); outerPadding.clear(); } - - // Construct header - header = volumeBody.mid(storeOffset, storeSize); - + + // Obtain header size + headerSize = storeSize; + // Add info name = UString("SLIC marker"); info = usprintf("Type: 1h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: 0h (0)\n" @@ -1421,98 +1577,109 @@ not_pubkey: parsed.oem_id().c_str(), parsed.oem_table_id().c_str(), parsed.slic_version()); - + // Add tree item - model->addItem(localOffset + storeOffset, Types::SlicData, Subtypes::MarkerSlicData, name, UString(), info, header, UByteArray(), UByteArray(), Fixed, index); - + model->addItem( + localOffset + storeOffset, Types::SlicData, Subtypes::MarkerSlicData, + name, UString(), info, + headerSize, 0, 0, + Fixed, index); + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { + } + catch (...) { // Parsing failed, try something else } -not_marker: + not_marker: // Intel uCode try { // Check data size if (volumeBodySize - storeOffset < sizeof(INTEL_MICROCODE_HEADER)) { goto not_ucode; } - + const UINT32 currentUint32 = readUnaligned((const UINT32*)(volumeBody.constData() + storeOffset)); if (currentUint32 != INTEL_MICROCODE_HEADER_VERSION_1) { goto not_ucode; } - + // Check microcode header candidate const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)(volumeBody.constData() + storeOffset); if (FALSE == ffsParser->microcodeHeaderValid(ucodeHeader)) { goto not_ucode; } - + // Check candidate size if (ucodeHeader->TotalSize == 0) { goto not_ucode; } - + // We still have enough data left to fit the whole TotalSize UINT32 storeSize = ucodeHeader->TotalSize; if (volumeBodySize - storeOffset < storeSize) { goto not_ucode; } - + // All checks passed, microcode found // Check if we need to add a padding before it if (!outerPadding.isEmpty()) { info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); outerPadding.clear(); } - + // Parse microcode header UByteArray ucode = volumeBody.mid(storeOffset); UModelIndex ucodeIndex; if (U_SUCCESS != ffsParser->parseIntelMicrocodeHeader(ucode, localOffset + storeOffset, index, ucodeIndex)) { goto not_ucode; } - + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { + } + catch (...) { // Parsing failed, try something else } -not_ucode: + not_ucode: // FFS volume try { // Check data size if (volumeBodySize - storeOffset < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) { goto not_ffs_volume; } - + // Check volume header candidate const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(volumeBody.constData() + storeOffset); if (volumeHeader->Signature != EFI_FV_SIGNATURE) { goto not_ffs_volume; } - + // All checks passed, volume found UByteArray volume = volumeBody.mid(storeOffset); UModelIndex volumeIndex; if (U_SUCCESS != ffsParser->parseVolumeHeader(volume, localOffset + storeOffset, index, volumeIndex)) { goto not_ffs_volume; } - + (VOID)ffsParser->parseVolumeBody(volumeIndex); - UINT32 storeSize = (UINT32)(model->header(volumeIndex).size() + model->body(volumeIndex).size()); - + UINT32 storeSize = (UINT32)(model->headerSize(volumeIndex) + model->bodySize(volumeIndex)); + storeOffset += storeSize - 1; previousStoreEndOffset = storeOffset + 1; continue; - } catch (...) { + } + catch (...) { // Parsing failed, try something else } -not_ffs_volume: + not_ffs_volume: // Padding if (storeOffset < volumeBodySize) { outerPadding += volumeBody[storeOffset]; @@ -1523,15 +1690,24 @@ not_ffs_volume: if (!outerPadding.isEmpty()) { // Add info UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); - + // Check that remaining unparsed bytes are actually empty - if (outerPadding.count(emptyByte) == outerPadding.size()) { + auto c = checkSingle(outerPadding); + if (c == emptyByte) { // Add tree item - model->addItem(localOffset + previousStoreEndOffset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::FreeSpace, 0, + UString("Free space"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); } else { // Add tree item - model->addItem(localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + model->addItem( + localOffset + previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), + UString("Padding"), UString(), info, + 0, outerPadding.size(), 0, + Fixed, index); } } diff --git a/common/nvramparser.h b/common/nvramparser.h index a64e795..1716250 100644 --- a/common/nvramparser.h +++ b/common/nvramparser.h @@ -37,8 +37,8 @@ public: void clearMessages() { messagesVector.clear(); } // NVRAM parsing - USTATUS parseNvramVolumeBody(const UModelIndex & index, const UINT32 fdcStoreSizeOverride = 0); - USTATUS parseNvarStore(const UModelIndex & index); + USTATUS parseNvramVolumeBody(const UModelIndex& index, const UINT32 fdcStoreSizeOverride = 0); + USTATUS parseNvarStore(const UModelIndex & index, const bool probe = false); private: TreeModel *model; @@ -63,7 +63,7 @@ public: // NVRAM parsing USTATUS parseNvramVolumeBody(const UModelIndex &) { return U_SUCCESS; } - USTATUS parseNvarStore(const UModelIndex &) { return U_SUCCESS; } + USTATUS parseNvarStore(const UModelIndex &, const bool probe = false) { return U_SUCCESS; } }; #endif // U_ENABLE_NVRAM_PARSING_SUPPORT #endif // NVRAMPARSER_H diff --git a/common/printf/printf.c b/common/printf/printf.c new file mode 100644 index 0000000..77cfa1c --- /dev/null +++ b/common/printf/printf.c @@ -0,0 +1,1509 @@ +/** + * @author (c) Eyal Rozenberg <eyalroz1@gmx.com> + * 2021-2023, Haifa, Palestine/Israel + * @author (c) Marco Paland (info@paland.com) + * 2014-2019, PALANDesign Hannover, Germany + * + * @note Others have made smaller contributions to this file: see the + * contributors page at https://github.com/eyalroz/printf/graphs/contributors + * or ask one of the authors. The original code for exponential specifiers was + * contributed by Martijn Jasperse <m.jasperse@gmail.com>. + * + * @brief Small stand-alone implementation of the printf family of functions + * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with + * limited resources. + * + * @note the implementations are thread-safe; re-entrant; use no functions from + * the standard library; and do not dynamically allocate any memory. + * + * @license The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H=1 ...) to include the +// printf_config.h header file +#if PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +#include "printf/printf.h" + +#ifdef __cplusplus +#include <cstdint> +#include <climits> +#else +#include <stdint.h> +#include <limits.h> +#include <stdbool.h> +#endif // __cplusplus + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD +# define printf_ printf +# define sprintf_ sprintf +# define vsprintf_ vsprintf +# define snprintf_ snprintf +# define vsnprintf_ vsnprintf +# define vprintf_ vprintf +#endif + + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +#ifndef PRINTF_INTEGER_BUFFER_SIZE +#define PRINTF_INTEGER_BUFFER_SIZE 32 +#endif + +// size of the fixed (on-stack) buffer for printing individual decimal numbers. +// this must be big enough to hold one converted floating-point value including +// padded zeros. +#ifndef PRINTF_DECIMAL_BUFFER_SIZE +#define PRINTF_DECIMAL_BUFFER_SIZE 32 +#endif + +// Support for the decimal notation floating point conversion specifiers (%f, %F) +#ifndef PRINTF_SUPPORT_DECIMAL_SPECIFIERS +#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 1 +#endif + +// Support for the exponential notation floating point conversion specifiers (%e, %g, %E, %G) +#ifndef PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS +#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1 +#endif + +// Support for the length write-back specifier (%n) +#ifndef PRINTF_SUPPORT_WRITEBACK_SPECIFIER +#define PRINTF_SUPPORT_WRITEBACK_SPECIFIER 1 +#endif + +// Default precision for the floating point conversion specifiers (the C standard sets this at 6) +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6 +#endif + +// Default choice of type to use for internal floating-point computations +#ifndef PRINTF_USE_DOUBLE_INTERNALLY +#define PRINTF_USE_DOUBLE_INTERNALLY 1 +#endif + +// According to the C languages standard, printf() and related functions must be able to print any +// integral number in floating-point notation, regardless of length, when using the %f specifier - +// possibly hundreds of characters, potentially overflowing your buffers. In this implementation, +// all values beyond this threshold are switched to exponential notation. +#ifndef PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL +#define PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9 +#endif + +// Support for the long long integral types (with the ll, z and t length modifiers for specifiers +// %d,%i,%o,%x,%X,%u, and with the %p specifier). +#ifndef PRINTF_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG 1 +#endif + +// The number of terms in a Taylor series expansion of log_10(x) to +// use for approximation - including the power-zero term (i.e. the +// value at the point of expansion). +#ifndef PRINTF_LOG10_TAYLOR_TERMS +#define PRINTF_LOG10_TAYLOR_TERMS 4 +#endif + +#if PRINTF_LOG10_TAYLOR_TERMS <= 1 +#error "At least one non-constant Taylor expansion is necessary for the log10() calculation" +#endif + +// Be extra-safe, and don't assume format specifiers are completed correctly +// before the format string end. +#ifndef PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER +#define PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER 1 +#endif + +#define PRINTF_PREFER_DECIMAL false +#define PRINTF_PREFER_EXPONENTIAL true + +/////////////////////////////////////////////////////////////////////////////// + +// The following will convert the number-of-digits into an exponential-notation literal +#define PRINTF_CONCATENATE(s1, s2) s1##s2 +#define PRINTF_EXPAND_THEN_CONCATENATE(s1, s2) PRINTF_CONCATENATE(s1, s2) +#define PRINTF_FLOAT_NOTATION_THRESHOLD ((floating_point_t) PRINTF_EXPAND_THEN_CONCATENATE(1e,PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL)) + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_INT (1U << 8U) + // Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS +#define FLAGS_LONG (1U << 9U) +#define FLAGS_LONG_LONG (1U << 10U) +#define FLAGS_PRECISION (1U << 11U) +#define FLAGS_ADAPT_EXP (1U << 12U) +#define FLAGS_POINTER (1U << 13U) + // Note: Similar, but not identical, effect as FLAGS_HASH +#define FLAGS_SIGNED (1U << 14U) +#define FLAGS_LONG_DOUBLE (1U << 15U) + // Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS + +#ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS + +#define FLAGS_INT8 FLAGS_CHAR + + +#if (SHRT_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_SHORT +#elif (INT_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_INT +#elif (LONG_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_LONG +#elif (LLONG_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_LONG_LONG +#else +#error "No basic integer type has a size of 16 bits exactly" +#endif + +#if (SHRT_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_SHORT +#elif (INT_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_INT +#elif (LONG_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_LONG +#elif (LLONG_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_LONG_LONG +#else +#error "No basic integer type has a size of 32 bits exactly" +#endif + +#if (SHRT_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_SHORT +#elif (INT_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_INT +#elif (LONG_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_LONG +#elif (LLONG_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_LONG_LONG +#else +#error "No basic integer type has a size of 64 bits exactly" +#endif + +#endif // PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS + + +typedef unsigned int printf_flags_t; + +#define BASE_BINARY 2 +#define BASE_OCTAL 8 +#define BASE_DECIMAL 10 +#define BASE_HEX 16 + +typedef uint8_t numeric_base_t; + +#if PRINTF_SUPPORT_LONG_LONG +typedef unsigned long long printf_unsigned_value_t; +typedef long long printf_signed_value_t; +#else +typedef unsigned long printf_unsigned_value_t; +typedef long printf_signed_value_t; +#endif + +// The printf()-family functions return an `int`; it is therefore +// unnecessary/inappropriate to use size_t - often larger than int +// in practice - for non-negative related values, such as widths, +// precisions, offsets into buffers used for printing and the sizes +// of these buffers. instead, we use: +typedef unsigned int printf_size_t; +#define PRINTF_MAX_POSSIBLE_BUFFER_SIZE INT_MAX + // If we were to nitpick, this would actually be INT_MAX + 1, + // since INT_MAX is the maximum return value, which excludes the + // trailing '\0'. + +#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) +#include <float.h> +#if FLT_RADIX != 2 +#error "Non-binary-radix floating-point types are unsupported." +#endif + +/** + * This library supports taking float-point arguments up to and including + * long double's; but - it currently does _not_ support internal + * representation and manipulation of values as long doubles; the options + * are either single-precision `float` or double-precision `double`. + */ +#if PRINTF_USE_DOUBLE_INTERNALLY +typedef double floating_point_t; +#define FP_TYPE_MANT_DIG DBL_MANT_DIG +#else +typedef float floating_point_t; +#define FP_TYPE_MANT_DIG FLT_MANT_DIG +#endif + +#define NUM_DECIMAL_DIGITS_IN_INT64_T 18 + +#if FP_TYPE_MANT_DIG == 24 + +typedef uint32_t printf_fp_uint_t; +#define FP_TYPE_SIZE_IN_BITS 32 +#define FP_TYPE_EXPONENT_MASK 0xFFU +#define FP_TYPE_BASE_EXPONENT 127 +#define FP_TYPE_MAX FLT_MAX +#define FP_TYPE_MAX_10_EXP FLT_MAX_10_EXP +#define FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10 -38 +#define FP_TYPE_MAX_SUBNORMAL_POWER_OF_10 1e-38f +#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 10 + +#elif FP_TYPE_MANT_DIG == 53 + +typedef uint64_t printf_fp_uint_t; +#define FP_TYPE_SIZE_IN_BITS 64 +#define FP_TYPE_EXPONENT_MASK 0x7FFU +#define FP_TYPE_BASE_EXPONENT 1023 +#define FP_TYPE_MAX DBL_MAX +#define FP_TYPE_MAX_10_EXP DBL_MAX_10_EXP +#define FP_TYPE_MAX_10_EXP DBL_MAX_10_EXP +#define FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10 -308 +#define FP_TYPE_MAX_SUBNORMAL_POWER_OF_10 1e-308 +#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 NUM_DECIMAL_DIGITS_IN_INT64_T - 1 + + +#else +#error "Unsupported floating point type configuration" +#endif +#define FP_TYPE_STORED_MANTISSA_BITS (FP_TYPE_MANT_DIG - 1) + +typedef union { + printf_fp_uint_t U; + floating_point_t F; +} floating_point_with_bit_access; + +// This is unnecessary in C99, since compound initializers can be used, +// but: +// 1. Some compilers are finicky about this; +// 2. Some people may want to convert this to C89; +// 3. If you try to use it as C++, only C++20 supports compound literals +static inline floating_point_with_bit_access get_bit_access(floating_point_t x) +{ + floating_point_with_bit_access dwba; + dwba.F = x; + return dwba; +} + +static inline int get_sign_bit(floating_point_t x) +{ + // The sign is stored in the highest bit + return (int) (get_bit_access(x).U >> (FP_TYPE_SIZE_IN_BITS - 1)); +} + +static inline int get_exp2(floating_point_with_bit_access x) +{ + // The exponent in an IEEE-754 floating-point number occupies a contiguous + // sequence of bits (e.g. 52..62 for 64-bit doubles), but with a non-trivial representation: An + // unsigned offset from some negative value (with the extremal offset values reserved for + // special use). + return (int)((x.U >> FP_TYPE_STORED_MANTISSA_BITS ) & FP_TYPE_EXPONENT_MASK) - FP_TYPE_BASE_EXPONENT; +} +#define PRINTF_ABS(_x) ( (_x) > 0 ? (_x) : -(_x) ) + +#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) + +// Note in particular the behavior here on LONG_MIN or LLONG_MIN; it is valid +// and well-defined, but if you're not careful you can easily trigger undefined +// behavior with -LONG_MIN or -LLONG_MIN +#define ABS_FOR_PRINTING(_x) ((printf_unsigned_value_t) ( (_x) > 0 ? (_x) : -((printf_signed_value_t)_x) )) + +// wrapper (used as buffer) for output function type +// +// One of the following must hold: +// 1. max_chars is 0 +// 2. buffer is non-null +// 3. function is non-null +// +// ... otherwise bad things will happen. +typedef struct { + void (*function)(char c, void* extra_arg); + void* extra_function_arg; + char* buffer; + printf_size_t pos; + printf_size_t max_chars; + bool flag_cstyle_Xh; +} output_gadget_t; + +// Note: This function currently assumes it is not passed a '\0' c, +// or alternatively, that '\0' can be passed to the function in the output +// gadget. The former assumption holds within the printf library. It also +// assumes that the output gadget has been properly initialized. +static inline void putchar_via_gadget(output_gadget_t* gadget, char c) +{ + printf_size_t write_pos = gadget->pos++; + // We're _always_ increasing pos, so as to count how may characters + // _would_ have been written if not for the max_chars limitation + if (write_pos >= gadget->max_chars) { + return; + } + if (gadget->function != NULL) { + // No check for c == '\0' . + gadget->function(c, gadget->extra_function_arg); + } + else { + // it must be the case that gadget->buffer != NULL , due to the constraint + // on output_gadget_t ; and note we're relying on write_pos being non-negative. + gadget->buffer[write_pos] = c; + } +} + +// Possibly-write the string-terminating '\0' character +static inline void append_termination_with_gadget(output_gadget_t* gadget) +{ + if (gadget->function != NULL || gadget->max_chars == 0) { + return; + } + if (gadget->buffer == NULL) { + return; + } + printf_size_t null_char_pos = gadget->pos < gadget->max_chars ? gadget->pos : gadget->max_chars - 1; + gadget->buffer[null_char_pos] = '\0'; +} + +// We can't use putchar_ as is, since our output gadget +// only takes pointers to functions with an extra argument +static inline void putchar_wrapper(char c, void* unused) +{ + (void) unused; + putchar_(c); +} + +static inline output_gadget_t discarding_gadget(void) +{ + output_gadget_t gadget; + gadget.function = NULL; + gadget.extra_function_arg = NULL; + gadget.buffer = NULL; + gadget.pos = 0; + gadget.max_chars = 0; + gadget.flag_cstyle_Xh = false; + return gadget; +} + +static inline output_gadget_t buffer_gadget(char* buffer, size_t buffer_size) +{ + printf_size_t usable_buffer_size = (buffer_size > PRINTF_MAX_POSSIBLE_BUFFER_SIZE) ? + PRINTF_MAX_POSSIBLE_BUFFER_SIZE : (printf_size_t) buffer_size; + output_gadget_t result = discarding_gadget(); + if (buffer != NULL) { + result.buffer = buffer; + result.max_chars = usable_buffer_size; + } + return result; +} + +static inline output_gadget_t function_gadget(void (*function)(char, void*), void* extra_arg) +{ + output_gadget_t result = discarding_gadget(); + result.function = function; + result.extra_function_arg = extra_arg; + result.max_chars = PRINTF_MAX_POSSIBLE_BUFFER_SIZE; + return result; +} + +static inline output_gadget_t extern_putchar_gadget(void) +{ + return function_gadget(putchar_wrapper, NULL); +} + +// internal secure strlen +// @return The length of the string (excluding the terminating 0) limited by 'maxsize' +// @note strlen uses size_t, but wes only use this function with printf_size_t +// variables - hence the signature. +static inline printf_size_t strnlen_s_(const char* str, printf_size_t maxsize) +{ + const char* s; + for (s = str; *s && maxsize--; ++s); + return (printf_size_t)(s - str); +} + + +// internal test if char is a digit (0-9) +// @return true if char is a digit +static inline bool is_digit_(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + + +// internal ASCII string to printf_size_t conversion +static printf_size_t atou_(const char** str) +{ + printf_size_t i = 0U; + while (is_digit_(**str)) { + i = i * 10U + (printf_size_t)(*((*str)++) - '0'); + } + return i; +} + + +// output the specified string in reverse, taking care of any zero-padding +static void out_rev_(output_gadget_t* output, const char* buf, printf_size_t len, printf_size_t width, printf_flags_t flags) +{ + const printf_size_t start_pos = output->pos; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (printf_size_t i = len; i < width; i++) { + putchar_via_gadget(output, ' '); + } + } + + // reverse string + while (len) { + putchar_via_gadget(output, buf[--len]); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (output->pos - start_pos < width) { + putchar_via_gadget(output, ' '); + } + } +} + + +// Invoked by print_integer after the actual number has been printed, performing necessary +// work on the number's prefix (as the number is initially printed in reverse order) +static void print_integer_finalization(output_gadget_t* output, char* buf, printf_size_t len, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) +{ + printf_size_t unpadded_len = len; + + // pad with leading zeros + { + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + while ((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = '0'; + } + + if (base == BASE_OCTAL && (len > unpadded_len)) { + // Since we've written some zeros, we've satisfied the alternative format leading space requirement + flags &= ~FLAGS_HASH; + } + } + + // handle hash + if (flags & (FLAGS_HASH | FLAGS_POINTER)) { + if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) { + // Let's take back some padding digits to fit in what will eventually + // be the format-specific prefix + if (unpadded_len < len) { + len--; // This should suffice for BASE_OCTAL + } + if (len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) { + len--; // ... and an extra one for 0x or 0b + } + } + if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if ((base == BASE_BINARY) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_INTEGER_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_INTEGER_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + out_rev_(output, buf, len, width, flags); +} + +// An internal itoa-like function +static void print_integer(output_gadget_t* output, printf_unsigned_value_t value, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) +{ + char buf[PRINTF_INTEGER_BUFFER_SIZE]; + printf_size_t len = 0U; + + if (!value) { + if ( !(flags & FLAGS_PRECISION) ) { + buf[len++] = '0'; + flags &= ~FLAGS_HASH; + // We drop this flag this since either the alternative and regular modes of the specifier + // don't differ on 0 values, or (in the case of octal) we've already provided the special + // handling for this mode. + } + else if (base == BASE_HEX) { + flags &= ~FLAGS_HASH; + // We drop this flag this since either the alternative and regular modes of the specifier + // don't differ on 0 values + } + } + else { + do { + const char digit = (char)(value % base); + buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); + value /= base; + } while (value && (len < PRINTF_INTEGER_BUFFER_SIZE)); + } + + print_integer_finalization(output, buf, len, negative, base, precision, width, flags); +} + +#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) + +// Stores a fixed-precision representation of a floating-point number relative +// to a fixed precision (which cannot be determined by examining this structure) +struct floating_point_components { + int_fast64_t integral; + int_fast64_t fractional; + // ... truncation of the actual fractional part of the floating_point_t value, scaled + // by the precision value + bool is_negative; +}; + +static const floating_point_t powers_of_10[PRINTF_MAX_PRECOMPUTED_POWER_OF_10 + 1] = { + 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09, 1e10 +#if PRINTF_MAX_PRECOMPUTED_POWER_OF_10 > 10 + , 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17 +#endif +}; + +// Note: This value does not mean that all floating-point values printed with the +// library will be correct up to this precision; it is just an upper-bound for +// avoiding buffer overruns and such +#define PRINTF_MAX_SUPPORTED_PRECISION (NUM_DECIMAL_DIGITS_IN_INT64_T - 1) + + +// Break up a floating-point number - which is known to be a finite non-negative number - +// into its base-10 parts: integral - before the decimal point, and fractional - after it. +// Taken the precision into account, but does not change it even internally. +static struct floating_point_components get_components(floating_point_t number, printf_size_t precision) +{ + struct floating_point_components number_; + number_.is_negative = get_sign_bit(number); + floating_point_t abs_number = (number_.is_negative) ? -number : number; + number_.integral = (int_fast64_t) abs_number; + floating_point_t scaled_remainder = (abs_number - (floating_point_t) number_.integral) * powers_of_10[precision]; + number_.fractional = (int_fast64_t) scaled_remainder; // for precision == 0U, this will be 0 + + floating_point_t remainder = scaled_remainder - (floating_point_t) number_.fractional; + const floating_point_t one_half = (floating_point_t) 0.5; + + if (remainder > one_half) { + ++number_.fractional; + // handle rollover, e.g. case 0.99 with precision 1 is 1.0 + if ((floating_point_t) number_.fractional >= powers_of_10[precision]) { + number_.fractional = 0; + ++number_.integral; + } + } + else if ((remainder == one_half) && (number_.fractional & 1U)) { + // Banker's rounding, i.e. round half to even: + // 1.5 -> 2, but 2.5 -> 2 + ++number_.fractional; + } + + if (precision == 0U) { + remainder = abs_number - (floating_point_t) number_.integral; + if ((remainder == one_half) && (number_.integral & 1U)) { + // Banker's rounding, i.e. round half to even: + // 1.5 -> 2, but 2.5 -> 2 + ++number_.integral; + } + } + return number_; +} + +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS +struct scaling_factor { + floating_point_t raw_factor; + bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it +}; + +static floating_point_t apply_scaling(floating_point_t num, struct scaling_factor normalization) +{ + return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; +} + +static floating_point_t unapply_scaling(floating_point_t normalized, struct scaling_factor normalization) +{ +#ifdef __GNUC__ +// accounting for a static analysis bug in GCC 6.x and earlier +#pragma GCC diagnostic push +#if !defined(__has_warning) +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#elif __has_warning("-Wmaybe-uninitialized") +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#endif + return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +} + +static struct scaling_factor update_normalization(struct scaling_factor sf, floating_point_t extra_multiplicative_factor) +{ + struct scaling_factor result; + if (sf.multiply) { + result.multiply = true; + result.raw_factor = sf.raw_factor * extra_multiplicative_factor; + } + else { + int factor_exp2 = get_exp2(get_bit_access(sf.raw_factor)); + int extra_factor_exp2 = get_exp2(get_bit_access(extra_multiplicative_factor)); + + // Divide the larger-exponent raw raw_factor by the smaller + if (PRINTF_ABS(factor_exp2) > PRINTF_ABS(extra_factor_exp2)) { + result.multiply = false; + result.raw_factor = sf.raw_factor / extra_multiplicative_factor; + } + else { + result.multiply = true; + result.raw_factor = extra_multiplicative_factor / sf.raw_factor; + } + } + return result; +} + +static struct floating_point_components get_normalized_components(bool negative, printf_size_t precision, floating_point_t non_normalized, struct scaling_factor normalization, int floored_exp10) +{ + struct floating_point_components components; + components.is_negative = negative; + floating_point_t scaled = apply_scaling(non_normalized, normalization); + + bool close_to_representation_extremum = ( (-floored_exp10 + (int) precision) >= FP_TYPE_MAX_10_EXP - 1 ); + if (close_to_representation_extremum) { + // We can't have a normalization factor which also accounts for the precision, i.e. moves + // some decimal digits into the mantissa, since it's unrepresentable, or nearly unrepresentable. + // So, we'll give up early on getting extra precision... + return get_components(negative ? -scaled : scaled, precision); + } + components.integral = (int_fast64_t) scaled; + floating_point_t remainder = non_normalized - unapply_scaling((floating_point_t) components.integral, normalization); + floating_point_t prec_power_of_10 = powers_of_10[precision]; + struct scaling_factor account_for_precision = update_normalization(normalization, prec_power_of_10); + floating_point_t scaled_remainder = apply_scaling(remainder, account_for_precision); + floating_point_t rounding_threshold = 0.5; + + components.fractional = (int_fast64_t) scaled_remainder; // when precision == 0, the assigned value should be 0 + scaled_remainder -= (floating_point_t) components.fractional; //when precision == 0, this will not change scaled_remainder + + components.fractional += (scaled_remainder >= rounding_threshold); + if (scaled_remainder == rounding_threshold) { + // banker's rounding: Round towards the even number (making the mean error 0) + components.fractional &= ~((int_fast64_t) 0x1); + } + // handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100), + // and must then be corrected into (1, 0). + // Note: for precision = 0, this will "translate" the rounding effect from + // the fractional part to the integral part where it should actually be + // felt (as prec_power_of_10 is 1) + if ((floating_point_t) components.fractional >= prec_power_of_10) { + components.fractional = 0; + ++components.integral; + } + return components; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + +static void print_broken_up_decimal( + struct floating_point_components number_, output_gadget_t* output, printf_size_t precision, + printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) +{ + if (precision != 0U) { + // do fractional part, as an unsigned number + + printf_size_t count = precision; + + // %g/%G mandates we skip the trailing 0 digits... + if ((flags & FLAGS_ADAPT_EXP) && !(flags & FLAGS_HASH) && (number_.fractional > 0)) { + while(true) { + int_fast64_t digit = number_.fractional % 10U; + if (digit != 0) { + break; + } + --count; + number_.fractional /= 10U; + + } + // ... and even the decimal point if there are no + // non-zero fractional part digits (see below) + } + + if (number_.fractional > 0 || !(flags & FLAGS_ADAPT_EXP) || (flags & FLAGS_HASH) ) { + while (len < PRINTF_DECIMAL_BUFFER_SIZE) { + --count; + buf[len++] = (char)('0' + number_.fractional % 10U); + if (!(number_.fractional /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (count > 0U)) { + buf[len++] = '0'; + --count; + } + if (len < PRINTF_DECIMAL_BUFFER_SIZE) { + buf[len++] = '.'; + } + } + } + else { + if ((flags & FLAGS_HASH) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) { + buf[len++] = '.'; + } + } + + // Write the integer part of the number (it comes after the fractional + // since the character order is reversed) + while (len < PRINTF_DECIMAL_BUFFER_SIZE) { + buf[len++] = (char)('0' + (number_.integral % 10)); + if (!(number_.integral /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (number_.is_negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_DECIMAL_BUFFER_SIZE) { + if (number_.is_negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + out_rev_(output, buf, len, width, flags); +} + +// internal ftoa for fixed decimal floating point +static void print_decimal_number(output_gadget_t* output, floating_point_t number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len) +{ + struct floating_point_components value_ = get_components(number, precision); + print_broken_up_decimal(value_, output, precision, width, flags, buf, len); +} + +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + +// A floor function - but one which only works for numbers whose +// floor value is representable by an int. +static int bastardized_floor(floating_point_t x) +{ + if (x >= 0) { return (int) x; } + int n = (int) x; + return ( ((floating_point_t) n) == x ) ? n : n-1; +} + +// Computes the base-10 logarithm of the input number - which must be an actual +// positive number (not infinity or NaN, nor a sub-normal) +static floating_point_t log10_of_positive(floating_point_t positive_number) +{ + // The implementation follows David Gay (https://www.ampl.com/netlib/fp/dtoa.c). + // + // Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the components of + // our input number, and need only solve log_10(M) for M between 1 and 2 (as + // the base-2 mantissa is always 1-point-something). In that limited range, a + // Taylor series expansion of log10(x) should serve us well enough; and we'll + // take the mid-point, 1.5, as the point of expansion. + + floating_point_with_bit_access dwba = get_bit_access(positive_number); + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + int exp2 = get_exp2(dwba); + // drop the exponent, so dwba.F comes into the range [1,2) + dwba.U = (dwba.U & (((printf_fp_uint_t) (1) << FP_TYPE_STORED_MANTISSA_BITS) - 1U)) | + ((printf_fp_uint_t) FP_TYPE_BASE_EXPONENT << FP_TYPE_STORED_MANTISSA_BITS); + floating_point_t z = (dwba.F - (floating_point_t) 1.5); + return ( + // Taylor expansion around 1.5: + (floating_point_t) 0.1760912590556812420 // Expansion term 0: ln(1.5) / ln(10) + + z * (floating_point_t) 0.2895296546021678851 // Expansion term 1: (M - 1.5) * 2/3 / ln(10) +#if PRINTF_LOG10_TAYLOR_TERMS > 2 + - z*z * (floating_point_t) 0.0965098848673892950 // Expansion term 2: (M - 1.5)^2 * 2/9 / ln(10) +#if PRINTF_LOG10_TAYLOR_TERMS > 3 + + z*z*z * (floating_point_t) 0.0428932821632841311 // Expansion term 2: (M - 1.5)^3 * 8/81 / ln(10) +#endif +#endif + // exact log_2 of the exponent x, with logarithm base change + + (floating_point_t) exp2 * (floating_point_t) 0.30102999566398119521 // = exp2 * log_10(2) = exp2 * ln(2)/ln(10) + ); +} + + +static floating_point_t pow10_of_int(int floored_exp10) +{ + // A crude hack for avoiding undesired behavior with barely-normal or slightly-subnormal values. + if (floored_exp10 == FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10) { + return FP_TYPE_MAX_SUBNORMAL_POWER_OF_10; + } + // Compute 10^(floored_exp10) but (try to) make sure that doesn't overflow + floating_point_with_bit_access dwba; + int exp2 = bastardized_floor((floating_point_t) (floored_exp10 * 3.321928094887362 + 0.5)); + const floating_point_t z = (floating_point_t) (floored_exp10 * 2.302585092994046 - exp2 * 0.6931471805599453); + const floating_point_t z2 = z * z; + dwba.U = ((printf_fp_uint_t)(exp2) + FP_TYPE_BASE_EXPONENT) << FP_TYPE_STORED_MANTISSA_BITS; + // compute exp(z) using continued fractions, + // see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + dwba.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + return dwba.F; +} + +static void print_exponential_number(output_gadget_t* output, floating_point_t number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len) +{ + const bool negative = get_sign_bit(number); + // This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it + floating_point_t abs_number = negative ? -number : number; + + int floored_exp10; + bool abs_exp10_covered_by_powers_table; + struct scaling_factor normalization; + + + // Determine the decimal exponent + if (abs_number == (floating_point_t) 0.0) { + // TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for denormals more generally. + floored_exp10 = 0; // ... and no need to set a normalization factor or check the powers table + } + else { + floating_point_t exp10 = log10_of_positive(abs_number); + floored_exp10 = bastardized_floor(exp10); + floating_point_t p10 = pow10_of_int(floored_exp10); + // correct for rounding errors + if (abs_number < p10) { + floored_exp10--; + p10 /= 10; + } + abs_exp10_covered_by_powers_table = PRINTF_ABS(floored_exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; + normalization.raw_factor = abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS(floored_exp10)] : p10; + } + + // We now begin accounting for the widths of the two parts of our printed field: + // the decimal part after decimal exponent extraction, and the base-10 exponent part. + // For both of these, the value of 0 has a special meaning, but not the same one: + // a 0 exponent-part width means "don't print the exponent"; a 0 decimal-part width + // means "use as many characters as necessary". + + bool fall_back_to_decimal_only_mode = false; + if (flags & FLAGS_ADAPT_EXP) { + int required_significant_digits = (precision == 0) ? 1 : (int) precision; + // Should we want to fall-back to "%f" mode, and only print the decimal part? + fall_back_to_decimal_only_mode = (floored_exp10 >= -4 && floored_exp10 < required_significant_digits); + // Now, let's adjust the precision + // This also decided how we adjust the precision value - as in "%g" mode, + // "precision" is the number of _significant digits_, and this is when we "translate" + // the precision value to an actual number of decimal digits. + int precision_ = fall_back_to_decimal_only_mode ? + (int) precision - 1 - floored_exp10 : + (int) precision - 1; // the presence of the exponent ensures only one significant digit comes before the decimal point + precision = (precision_ > 0 ? (unsigned) precision_ : 0U); + flags |= FLAGS_PRECISION; // make sure print_broken_up_decimal respects our choice above + } + +#ifdef __GNUC__ +// accounting for a static analysis bug in GCC 6.x and earlier +#pragma GCC diagnostic push +#if !defined(__has_warning) +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#elif __has_warning("-Wmaybe-uninitialized") +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#endif + normalization.multiply = (floored_exp10 < 0 && abs_exp10_covered_by_powers_table); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + bool should_skip_normalization = (fall_back_to_decimal_only_mode || floored_exp10 == 0); + struct floating_point_components decimal_part_components = + should_skip_normalization ? + get_components(negative ? -abs_number : abs_number, precision) : + get_normalized_components(negative, precision, abs_number, normalization, floored_exp10); + + // Account for roll-over, e.g. rounding from 9.99 to 100.0 - which effects + // the exponent and may require additional tweaking of the parts + if (fall_back_to_decimal_only_mode) { + if ((flags & FLAGS_ADAPT_EXP) && floored_exp10 >= -1 && decimal_part_components.integral == powers_of_10[floored_exp10 + 1]) { + floored_exp10++; // Not strictly necessary, since floored_exp10 is no longer really used + if (precision > 0U) { precision--; } + // ... and it should already be the case that decimal_part_components.fractional == 0 + } + // TODO: What about rollover strictly within the fractional part? + } + else { + if (decimal_part_components.integral >= 10) { + floored_exp10++; + decimal_part_components.integral = 1; + decimal_part_components.fractional = 0; + } + } + + // the floored_exp10 format is "E%+03d" and largest possible floored_exp10 value for a 64-bit double + // is "307" (for 2^1023), so we set aside 4-5 characters overall + printf_size_t exp10_part_width = fall_back_to_decimal_only_mode ? 0U : (PRINTF_ABS(floored_exp10) < 100) ? 4U : 5U; + + printf_size_t decimal_part_width = + ((flags & FLAGS_LEFT) && exp10_part_width) ? + // We're padding on the right, so the width constraint is the exponent part's + // problem, not the decimal part's, so we'll use as many characters as we need: + 0U : + // We're padding on the left; so the width constraint is the decimal part's + // problem. Well, can both the decimal part and the exponent part fit within our overall width? + ((width > exp10_part_width) ? + // Yes, so we limit our decimal part's width. + // (Note this is trivially valid even if we've fallen back to "%f" mode) + width - exp10_part_width : + // No; we just give up on any restriction on the decimal part and use as many + // characters as we need + 0U); + + const printf_size_t printed_exponential_start_pos = output->pos; + print_broken_up_decimal(decimal_part_components, output, precision, decimal_part_width, flags, buf, len); + + if (! fall_back_to_decimal_only_mode) { + putchar_via_gadget(output, (flags & FLAGS_UPPERCASE) ? 'E' : 'e'); + print_integer(output, + ABS_FOR_PRINTING(floored_exp10), + floored_exp10 < 0, 10, 0, exp10_part_width - 1, + FLAGS_ZEROPAD | FLAGS_PLUS); + if (flags & FLAGS_LEFT) { + // We need to right-pad with spaces to meet the width requirement + while (output->pos - printed_exponential_start_pos < width) { + putchar_via_gadget(output, ' '); + } + } + } +} +#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + +static void print_floating_point(output_gadget_t* output, floating_point_t value, printf_size_t precision, printf_size_t width, printf_flags_t flags, bool prefer_exponential) +{ + char buf[PRINTF_DECIMAL_BUFFER_SIZE]; + printf_size_t len = 0U; + + // test for special values + if (value != value) { + out_rev_(output, "nan", 3, width, flags); + return; + } + if (value < -FP_TYPE_MAX) { + out_rev_(output, "fni-", 4, width, flags); + return; + } + if (value > FP_TYPE_MAX) { + out_rev_(output, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + return; + } + + if (!prefer_exponential && + ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD))) { + // The required behavior of standard printf is to print _every_ integral-part digit -- which could mean + // printing hundreds of characters, overflowing any fixed internal buffer and necessitating a more complicated + // implementation. +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + print_exponential_number(output, value, precision, width, flags, buf, len); +#endif + return; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + precision = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // limit precision so that our integer holding the fractional part does not overflow + while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) { + buf[len++] = '0'; // This respects the precision in terms of result length only + precision--; + } + +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + if (prefer_exponential) + print_exponential_number(output, value, precision, width, flags, buf, len); + else +#endif + print_decimal_number(output, value, precision, width, flags, buf, len); +} + +#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) + +// Advances the format pointer past the flags, and returns the parsed flags +// due to the characters passed +static printf_flags_t parse_flags(const char** format) +{ + printf_flags_t flags = 0U; + do { + switch (**format) { + case '0': flags |= FLAGS_ZEROPAD; (*format)++; break; + case '-': flags |= FLAGS_LEFT; (*format)++; break; + case '+': flags |= FLAGS_PLUS; (*format)++; break; + case ' ': flags |= FLAGS_SPACE; (*format)++; break; + case '#': flags |= FLAGS_HASH; (*format)++; break; + default : return flags; + } + } while (true); +} + +static inline void format_string_loop(output_gadget_t* output, const char* format, va_list args) +{ +#if PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER +#define ADVANCE_IN_FORMAT_STRING(cptr_) do { (cptr_)++; if (!*(cptr_)) return; } while(0) +#else +#define ADVANCE_IN_FORMAT_STRING(cptr_) (cptr_)++ +#endif + + + while (*format) + { + if (*format != '%') { + // A regular content character + putchar_via_gadget(output, *format); + format++; + continue; + } + // We're parsing a format specifier: %[flags][width][.precision][length] + ADVANCE_IN_FORMAT_STRING(format); + + printf_flags_t flags = parse_flags(&format); + + // evaluate width field + printf_size_t width = 0U; + if (is_digit_(*format)) { + width = (printf_size_t) atou_(&format); + } + else if (*format == '*') { + const int w = va_arg(args, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (printf_size_t)-w; + } + else { + width = (printf_size_t)w; + } + ADVANCE_IN_FORMAT_STRING(format); + } + + // evaluate precision field + printf_size_t precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + ADVANCE_IN_FORMAT_STRING(format); + if (is_digit_(*format)) { + precision = (printf_size_t) atou_(&format); + } + else if (*format == '*') { + const int precision_ = va_arg(args, int); + precision = precision_ > 0 ? (printf_size_t) precision_ : 0U; + ADVANCE_IN_FORMAT_STRING(format); + } + } + + // evaluate length field + switch (*format) { +#ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS + case 'I' : { + ADVANCE_IN_FORMAT_STRING(format); + // Greedily parse for size in bits: 8, 16, 32 or 64 + switch(*format) { + case '8': flags |= FLAGS_INT8; + ADVANCE_IN_FORMAT_STRING(format); + break; + case '1': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '6') { format++; flags |= FLAGS_INT16; } + break; + case '3': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '2') { ADVANCE_IN_FORMAT_STRING(format); flags |= FLAGS_INT32; } + break; + case '6': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '4') { ADVANCE_IN_FORMAT_STRING(format); flags |= FLAGS_INT64; } + break; + default: break; + } + break; + } +#endif + case 'l' : + flags |= FLAGS_LONG; + ADVANCE_IN_FORMAT_STRING(format); + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING(format); + } + break; + case 'L' : + flags |= FLAGS_LONG_DOUBLE; + ADVANCE_IN_FORMAT_STRING(format); + break; + case 'h' : + flags |= FLAGS_SHORT; + ADVANCE_IN_FORMAT_STRING(format); + if (*format == 'h') { + flags |= FLAGS_CHAR; + ADVANCE_IN_FORMAT_STRING(format); + } + break; + case 't' : + flags |= (sizeof(ptrdiff_t) <= sizeof(int) ) ? FLAGS_INT : (sizeof(ptrdiff_t) == sizeof(long)) ? FLAGS_LONG : FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING(format); + break; + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + ADVANCE_IN_FORMAT_STRING(format); + break; + case 'z' : + flags |= (sizeof(size_t) <= sizeof(int) ) ? FLAGS_INT : (sizeof(size_t) == sizeof(long)) ? FLAGS_LONG : FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING(format); + break; + default: + break; + } + + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + + if (*format == 'd' || *format == 'i') { + flags |= FLAGS_SIGNED; + } + + numeric_base_t base; + if (*format == 'x' || *format == 'X') { + base = BASE_HEX; + } + else if (*format == 'o') { + base = BASE_OCTAL; + } + else if (*format == 'b') { + base = BASE_BINARY; + } + else { + base = BASE_DECIMAL; + flags &= ~FLAGS_HASH; // decimal integers have no alternative presentation + } + + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + format++; + if (base == BASE_HEX) { + if (*format == 'h' && output->flag_cstyle_Xh) { + putchar_via_gadget(output, '0'); + putchar_via_gadget(output, 'x'); + format++; + } + } + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + if (flags & FLAGS_SIGNED) { + // A signed specifier: d, i or possibly I + bit size if enabled + + if (flags & FLAGS_LONG_LONG) { +#if PRINTF_SUPPORT_LONG_LONG + const long long value = va_arg(args, long long); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(args, long); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + } + else { + // We never try to interpret the argument as something potentially-smaller than int, + // due to integer promotion rules: Even if the user passed a short int, short unsigned + // etc. - these will come in after promotion, as int's (or unsigned for the case of + // short unsigned when it has the same size as int) + const int value = + (flags & FLAGS_CHAR) ? (signed char) va_arg(args, int) : + (flags & FLAGS_SHORT) ? (short int) va_arg(args, int) : + va_arg(args, int); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + } + } + else { + // An unsigned specifier: u, x, X, o, b + + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + + if (flags & FLAGS_LONG_LONG) { +#if PRINTF_SUPPORT_LONG_LONG + print_integer(output, (printf_unsigned_value_t) va_arg(args, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + print_integer(output, (printf_unsigned_value_t) va_arg(args, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = + (flags & FLAGS_CHAR) ? (unsigned char)va_arg(args, unsigned int) : + (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(args, unsigned int) : + va_arg(args, unsigned int); + print_integer(output, (printf_unsigned_value_t) value, false, base, precision, width, flags); + } + } + break; + } +#if PRINTF_SUPPORT_DECIMAL_SPECIFIERS + case 'f' : + case 'F' : { + floating_point_t value = (floating_point_t) (flags & FLAGS_LONG_DOUBLE ? va_arg(args, long double) : va_arg(args, double)); + if (*format == 'F') flags |= FLAGS_UPPERCASE; + print_floating_point(output, value, precision, width, flags, PRINTF_PREFER_DECIMAL); + format++; + break; + } +#endif +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + case 'e': + case 'E': + case 'g': + case 'G': { + floating_point_t value = (floating_point_t) (flags & FLAGS_LONG_DOUBLE ? va_arg(args, long double) : va_arg(args, double)); + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + print_floating_point(output, value, precision, width, flags, PRINTF_PREFER_EXPONENTIAL); + format++; + break; + } +#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + case 'c' : { + printf_size_t l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + putchar_via_gadget(output, ' '); + } + } + // char output + putchar_via_gadget(output, (char) va_arg(args, int) ); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + putchar_via_gadget(output, ' '); + } + } + format++; + break; + } + + case 's' : { + const char* p = va_arg(args, char*); + if (p == NULL) { + out_rev_(output, ")llun(", 6, width, flags); + } + else { + printf_size_t l = strnlen_s_(p, precision ? precision : PRINTF_MAX_POSSIBLE_BUFFER_SIZE); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + putchar_via_gadget(output, ' '); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision)) { + putchar_via_gadget(output, *(p++)); + --precision; + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + putchar_via_gadget(output, ' '); + } + } + } + format++; + break; + } + + case 'p' : { + width = sizeof(void*) * 2U + 2; // 2 hex chars per byte + the "0x" prefix + flags |= FLAGS_ZEROPAD | FLAGS_POINTER; + uintptr_t value = (uintptr_t)va_arg(args, void*); + (value == (uintptr_t) NULL) ? + out_rev_(output, ")lin(", 5, width, flags) : + print_integer(output, (printf_unsigned_value_t) value, false, BASE_HEX, precision, width, flags); + format++; + break; + } + + case '%' : + putchar_via_gadget(output, '%'); + format++; + break; + + // Many people prefer to disable support for %n, as it lets the caller + // engineer a write to an arbitrary location, of a value the caller + // effectively controls - which could be a security concern in some cases. +#if PRINTF_SUPPORT_WRITEBACK_SPECIFIER + case 'n' : { + if (flags & FLAGS_CHAR) *(va_arg(args, char*)) = (char) output->pos; + else if (flags & FLAGS_SHORT) *(va_arg(args, short*)) = (short) output->pos; + else if (flags & FLAGS_LONG) *(va_arg(args, long*)) = (long) output->pos; +#if PRINTF_SUPPORT_LONG_LONG + else if (flags & FLAGS_LONG_LONG) *(va_arg(args, long long*)) = (long long int) output->pos; +#endif // PRINTF_SUPPORT_LONG_LONG + else *(va_arg(args, int*)) = (int) output->pos; + format++; + break; + } +#endif // PRINTF_SUPPORT_WRITEBACK_SPECIFIER + + default : + putchar_via_gadget(output, *format); + format++; + break; + } + } +} + +// internal vsnprintf - used for implementing _all library functions +static int vsnprintf_impl(output_gadget_t* output, const char* format, va_list args) +{ + // Note: The library only calls vsnprintf_impl() with output->pos being 0. However, it is + // possible to call this function with a non-zero pos value for some "remedial printing". + format_string_loop(output, format, args); + + // termination + append_termination_with_gadget(output); + + // return written chars without terminating \0 + return (int)output->pos; +} + +/////////////////////////////////////////////////////////////////////////////// + +int vprintf_(const char* format, va_list arg) +{ + output_gadget_t gadget = extern_putchar_gadget(); + return vsnprintf_impl(&gadget, format, arg); +} + +int vsnprintf_(char* s, size_t n, const char* format, va_list arg) +{ + output_gadget_t gadget = buffer_gadget(s, n); + return vsnprintf_impl(&gadget, format, arg); +} + +int vosnprintf_(char* s, size_t n, const char* format, va_list arg) +{ + output_gadget_t gadget = buffer_gadget(s, n); + gadget.flag_cstyle_Xh = s[0] == '\0' ? false : true; + return vsnprintf_impl(&gadget, format, arg); +} + +int vsprintf_(char* s, const char* format, va_list arg) +{ + return vsnprintf_(s, PRINTF_MAX_POSSIBLE_BUFFER_SIZE, format, arg); +} + +int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) +{ + if (out == NULL) { return 0; } + output_gadget_t gadget = function_gadget(out, extra_arg); + return vsnprintf_impl(&gadget, format, arg); +} + +int vofctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) +{ + if (out == NULL) { return 0; } + output_gadget_t gadget = function_gadget(out, extra_arg); + gadget.flag_cstyle_Xh = *((char *)extra_arg) == '\0' ? false : true; + return vsnprintf_impl(&gadget, format, arg); +} + +int printf_(const char* format, ...) +{ + va_list args; + va_start(args, format); + const int ret = vprintf_(format, args); + va_end(args); + return ret; +} + +int sprintf_(char* s, const char* format, ...) +{ + va_list args; + va_start(args, format); + const int ret = vsprintf_(s, format, args); + va_end(args); + return ret; +} + +int snprintf_(char* s, size_t n, const char* format, ...) +{ + va_list args; + va_start(args, format); + const int ret = vsnprintf_(s, n, format, args); + va_end(args); + return ret; +} + +int fctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, ...) +{ + va_list args; + va_start(args, format); + const int ret = vfctprintf(out, extra_arg, format, args); + va_end(args); + return ret; +} + diff --git a/common/printf/printf.h b/common/printf/printf.h new file mode 100644 index 0000000..5ef6c6a --- /dev/null +++ b/common/printf/printf.h @@ -0,0 +1,240 @@ +/** + * @author (c) Eyal Rozenberg <eyalroz1@gmx.com> + * 2021-2023, Haifa, Palestine/Israel + * @author (c) Marco Paland (info@paland.com) + * 2014-2019, PALANDesign Hannover, Germany + * + * @note Others have made smaller contributions to this file: see the + * contributors page at https://github.com/eyalroz/printf/graphs/contributors + * or ask one of the authors. + * + * @brief Small stand-alone implementation of the printf family of functions + * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems + * with a very limited resources. + * + * @note the implementations are thread-safe; re-entrant; use no functions from + * the standard library; and do not dynamically allocate any memory. + * + * @license The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PRINTF_H_ +#define PRINTF_H_ + +#ifdef __cplusplus +# include <cstdarg> +# include <cstddef> +extern "C" { +#else +# include <stdarg.h> +# include <stddef.h> +#endif + +#ifdef __GNUC__ +# if ((__GNUC__ == 4 && __GNUC_MINOR__>= 4) || __GNUC__ > 4) +# define ATTR_PRINTF(one_based_format_index, first_arg) \ +__attribute__((format(gnu_printf, (one_based_format_index), (first_arg)))) +# else +# define ATTR_PRINTF(one_based_format_index, first_arg) \ +__attribute__((format(printf, (one_based_format_index), (first_arg)))) +# endif +# define ATTR_VPRINTF(one_based_format_index) \ +ATTR_PRINTF((one_based_format_index), 0) +#else +# define ATTR_PRINTF(one_based_format_index, first_arg) +# define ATTR_VPRINTF(one_based_format_index) +#endif + +#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT +#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT 0 +#endif + +#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD +#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 0 +#endif + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD +# define printf_ printf +# define sprintf_ sprintf +# define vsprintf_ vsprintf +# define snprintf_ snprintf +# define vsnprintf_ vsnprintf +# define vprintf_ vprintf +#endif + +// If you want to include this implementation file directly rather than +// link against it, this will let you control the functions' visibility, +// e.g. make them static so as not to clash with other objects also +// using them. +#ifndef PRINTF_VISIBILITY +#define PRINTF_VISIBILITY +#endif + +/** + * Prints/send a single character to some opaque output entity + * + * @note This function is not implemented by the library, only declared; you + * must provide an implementation if you wish to use the @ref printf / @ref + * vprintf function (and possibly for linking against the library, if your + * toolchain does not support discarding unused functions) + * + * @note The output could be as simple as a wrapper for the `write()` system + * call on a Unix-like * system, or even libc's @ref putchar , for replicating + * actual functionality of libc's @ref printf * function; but on an embedded + * system it may involve interaction with a special output device, like a UART, + * etc. + * + * @note in libc's @ref putchar, the parameter type is an int; this was intended + * to support the representation of either a proper character or EOF in a + * variable - but this is really not meaningful to pass into @ref putchar and is + * discouraged today. See further discussion in: + * @link https://stackoverflow.com/q/17452847/1593077 + * + * @param c the single character to print + */ +PRINTF_VISIBILITY +void putchar_(char c); + + +/** + * An implementation of the C standard's printf/vprintf + * + * @note you must implement a @ref putchar_ function for using this function - + * it invokes @ref putchar_ * rather than directly performing any I/O (which + * insulates it from any dependence on the operating system * and external + * libraries). + * + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments. + * @param arg Additional arguments to the function, one for each %-specifier in + * @p format + * @return The number of characters written into @p s, not counting the + * terminating null character + */ + ///@{ +PRINTF_VISIBILITY +int printf_(const char* format, ...) ATTR_PRINTF(1, 2); +PRINTF_VISIBILITY +int vprintf_(const char* format, va_list arg) ATTR_VPRINTF(1); +///@} + + +/** + * An implementation of the C standard's sprintf/vsprintf + * + * @note For security considerations (the potential for exceeding the buffer + * bounds), please consider using the size-constrained variant, @ref snprintf / + * @ref vsnprintf, instead. + * + * @param s An array in which to store the formatted string. It must be large + * enough to fit the formatted output! + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments + * @param arg Additional arguments to the function, one for each specifier in + * @p format + * @return The number of characters written into @p s, not counting the + * terminating null character + */ +///@{ +PRINTF_VISIBILITY +int sprintf_(char* s, const char* format, ...) ATTR_PRINTF(2, 3); +PRINTF_VISIBILITY +int vsprintf_(char* s, const char* format, va_list arg) ATTR_VPRINTF(2); +///@} + + +/** + * An implementation of the C standard's snprintf/vsnprintf + * + * @param s An array in which to store the formatted string. It must be large + * enough to fit either the entire formatted output, or at least @p n + * characters. Alternatively, it can be NULL, in which case nothing will + * be printed, and only the number of characters which _could_ have been + * printed is tallied and returned. + * @param n The maximum number of characters to write to the array, including + * a terminating null character + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments. + * @param arg Additional arguments to the function, one for each specifier in + * @p format + * @return The number of characters that COULD have been written into @p s, not + * counting the terminating null character. A value equal or larger than + * @p n indicates truncation. Only when the returned value is non-negative + * and less than @p n, the null-terminated string has been fully and + * successfully printed. + */ +///@{ +PRINTF_VISIBILITY +int snprintf_(char* s, size_t count, const char* format, ...) ATTR_PRINTF(3, 4); +PRINTF_VISIBILITY +int vsnprintf_(char* s, size_t count, const char* format, va_list arg) ATTR_VPRINTF(3); +PRINTF_VISIBILITY +int vosnprintf_(char* s, size_t count, const char* format, va_list arg) ATTR_VPRINTF(3); +///@} + +/** + * printf/vprintf with user-specified output function + * + * An alternative to @ref printf_, in which the output function is specified + * dynamically (rather than @ref putchar_ being used) + * + * @param out An output function which takes one character and a type-erased + * additional parameters + * @param extra_arg The type-erased argument to pass to the output function @p + * out with each call + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments. + * @param arg Additional arguments to the function, one for each specifier in + * @p format + * @return The number of characters for which the output f unction was invoked, + * not counting the terminating null character + * + */ +PRINTF_VISIBILITY +int fctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, ...) ATTR_PRINTF(3, 4); +PRINTF_VISIBILITY +int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) ATTR_VPRINTF(3); +PRINTF_VISIBILITY +int vofctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) ATTR_VPRINTF(3); + +#ifdef __cplusplus +} // extern "C" +#endif + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD +# undef printf_ +# undef sprintf_ +# undef vsprintf_ +# undef snprintf_ +# undef vsnprintf_ +# undef vprintf_ +#else +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT +# define printf printf_ +# define sprintf sprintf_ +# define vsprintf vsprintf_ +# define snprintf snprintf_ +# define vsnprintf vsnprintf_ +# define vprintf vprintf_ +#endif +#endif + +#endif // PRINTF_H_ diff --git a/common/treeitem.cpp b/common/treeitem.cpp index 3939c0b..5e5ee36 100644 --- a/common/treeitem.cpp +++ b/common/treeitem.cpp @@ -16,7 +16,7 @@ TreeItem::TreeItem(const UINT32 offset, const UINT8 type, const UINT8 subtype, const UString & name, const UString & text, const UString & info, - const UByteArray & header, const UByteArray & body, const UByteArray & tail, + const UINT32 headerSize, const UINT32 bodySize, const UINT32 tailSize, const bool fixed, const bool compressed, TreeItem *parent) : itemOffset(offset), @@ -27,9 +27,9 @@ itemMarking(0), itemName(name), itemText(text), itemInfo(info), -itemHeader(header), -itemBody(body), -itemTail(tail), +itemHeaderSize(headerSize), +itemBodySize(bodySize), +itemTailSize(tailSize), itemFixed(fixed), itemCompressed(compressed), parentItem(parent) @@ -37,16 +37,39 @@ parentItem(parent) } TreeItem::~TreeItem() { - std::list<TreeItem*>::iterator begin = childItems.begin(); + auto begin = childItems.begin(); while (begin != childItems.end()) { delete *begin; ++begin; } } +const char * TreeItem::content(UINT32 dataOffset) const +{ + if (!itemContent.isEmpty()) { + return itemContent.constData() + dataOffset; + } + auto o = itemOffset; + auto p = parentItem; + while (p && ((!p->itemCompressed && p->itemContent.isEmpty()) || (p->itemCompressed && p->itemUncompressedData.isEmpty()))) { + o += p->itemOffset; + p = p->parentItem; + } + if (!p) + return nullptr; + return !p->itemCompressed ? p->content(o + dataOffset) : p->itemUncompressedData.constData() + o + dataOffset - p->itemHeaderSize; +} + +bool TreeItem::compressedInherited() const { + auto p = this; + while (p && p->itemType != Types::Root && !p->itemCompressed) + p = p->parentItem; + return p ? p->itemCompressed : false; +} + UINT8 TreeItem::insertChildBefore(TreeItem *item, TreeItem *newItem) { - std::list<TreeItem*>::iterator found = std::find(childItems.begin(), childItems.end(), item); + auto found = std::find(childItems.begin(), childItems.end(), item); if (found == childItems.end()) return U_ITEM_NOT_FOUND; childItems.insert(found, newItem); @@ -55,7 +78,7 @@ UINT8 TreeItem::insertChildBefore(TreeItem *item, TreeItem *newItem) UINT8 TreeItem::insertChildAfter(TreeItem *item, TreeItem *newItem) { - std::list<TreeItem*>::iterator found = std::find(childItems.begin(), childItems.end(), item); + auto found = std::find(childItems.begin(), childItems.end(), item); if (found == childItems.end()) return U_ITEM_NOT_FOUND; childItems.insert(++found, newItem); @@ -84,7 +107,7 @@ UString TreeItem::data(int column) const int TreeItem::row() const { if (parentItem) { - std::list<TreeItem*>::const_iterator iter = parentItem->childItems.begin(); + auto iter = parentItem->childItems.begin(); for (int i = 0; i < (int)parentItem->childItems.size(); ++i, ++iter) { if (const_cast<TreeItem*>(this) == *iter) return i; @@ -95,7 +118,7 @@ int TreeItem::row() const TreeItem* TreeItem::child(int row) { - std::list<TreeItem*>::iterator child = childItems.begin(); + auto child = childItems.begin(); std::advance(child, row); return *child; } diff --git a/common/treeitem.h b/common/treeitem.h index 0baf3e1..49effc2 100644 --- a/common/treeitem.h +++ b/common/treeitem.h @@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef TREEITEM_H #define TREEITEM_H -#include <list> +#include <vector> #include <iterator> #include "basetypes.h" @@ -25,20 +25,20 @@ class TreeItem { public: TreeItem(const UINT32 offset, const UINT8 type, const UINT8 subtype, const UString &name, const UString &text, const UString &info, - const UByteArray & header, const UByteArray & body, const UByteArray & tail, + const UINT32 headerSize, const UINT32 bodySize, const UINT32 tailSize, const bool fixed, const bool compressed, TreeItem *parent = 0); ~TreeItem(); // Non-trivial implementation in CPP file // Operations with items void appendChild(TreeItem *item) { childItems.push_back(item); } - void prependChild(TreeItem *item) { childItems.push_front(item); }; + void prependChild(TreeItem *item) { childItems.insert(childItems.begin(), item); }; UINT8 insertChildBefore(TreeItem *item, TreeItem *newItem); // Non-trivial implementation in CPP file UINT8 insertChildAfter(TreeItem *item, TreeItem *newItem); // Non-trivial implementation in CPP file // Model support operations TreeItem *child(int row); // Non-trivial implementation in CPP file - int childCount() const {return (int)childItems.size(); } + int childCount() const { return (int)childItems.size(); } int columnCount() const { return 5; } UString data(int column) const; // Non-trivial implementation in CPP file int row() const; // Non-trivial implementation in CPP file @@ -60,14 +60,17 @@ public: UString text() const { return itemText; } void setText(const UString &text) { itemText = text; } - UByteArray header() const { return itemHeader; } - bool hasEmptyHeader() const { return itemHeader.isEmpty(); } + UByteArray header() const { return UByteArray(content(0), itemHeaderSize); } + UINT32 headerSize() const { return itemHeaderSize; } + bool hasEmptyHeader() const { return itemHeaderSize == 0; } - UByteArray body() const { return itemBody; }; - bool hasEmptyBody() const { return itemBody.isEmpty(); } + UByteArray body() const { return UByteArray(content(itemHeaderSize), itemBodySize); } + UINT32 bodySize() const { return itemBodySize; } + bool hasEmptyBody() const { return itemBodySize == 0; } - UByteArray tail() const { return itemTail; }; - bool hasEmptyTail() const { return itemTail.isEmpty(); } + UByteArray tail() const { return UByteArray(content(itemHeaderSize + itemBodySize), itemTailSize); } + UINT32 tailSize() const { return itemTailSize; } + bool hasEmptyTail() const { return itemTailSize == 0; } UString info() const { return itemInfo; } void addInfo(const UString &info, const bool append) { if (append) itemInfo += info; else itemInfo = info + itemInfo; } @@ -79,6 +82,10 @@ public: bool fixed() const { return itemFixed; } void setFixed(const bool fixed) { itemFixed = fixed; } + bool hasContent() const { return !itemContent.isEmpty(); } + void setContent(const UByteArray& c) { itemContent = c; } + + bool compressedInherited() const; bool compressed() const { return itemCompressed; } void setCompressed(const bool compressed) { itemCompressed = compressed; } @@ -94,8 +101,13 @@ public: void setMarking(const UINT8 marking) { itemMarking = marking; } private: - std::list<TreeItem*> childItems; + const char* content(UINT32 dataOffset) const; + + std::vector<TreeItem*> childItems; UINT32 itemOffset; + UINT32 itemHeaderSize; + UINT32 itemBodySize; + UINT32 itemTailSize; UINT8 itemAction; UINT8 itemType; UINT8 itemSubtype; @@ -103,9 +115,7 @@ private: UString itemName; UString itemText; UString itemInfo; - UByteArray itemHeader; - UByteArray itemBody; - UByteArray itemTail; + UByteArray itemContent; bool itemFixed; bool itemCompressed; UByteArray itemParsingData; diff --git a/common/treemodel.cpp b/common/treemodel.cpp index 8fcc0bd..6349793 100644 --- a/common/treemodel.cpp +++ b/common/treemodel.cpp @@ -11,6 +11,12 @@ */ +#if defined (QT_GUI_LIB) +#include <QtWidgets/QApplication> +#include <QIcon> +#include <QtWidgets/QStyle> +#endif + #include "treemodel.h" #include "stack" @@ -36,6 +42,16 @@ QVariant TreeModel::data(const UModelIndex &index, int role) const } } } + else if (role == Qt::DecorationRole) { + if ((item->hasContent() || (item->parent()->hasContent() && item->parent()->type() == Types::Root)) && index.column() == 0) { + if (item->compressed()) + return QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning); + else + return QApplication::style()->standardIcon(QStyle::SP_FileDialogContentsView); + } + else if (item->compressed() && index.column() == 0) + return QApplication::style()->standardIcon(QStyle::SP_FileDialogStart); + } #endif else if (role == Qt::UserRole) { return item->info().toLocal8Bit(); @@ -214,6 +230,14 @@ UByteArray TreeModel::header(const UModelIndex &index) const return item->header(); } +UINT32 TreeModel::headerSize(const UModelIndex& index) const +{ + if (!index.isValid()) + return 0; + TreeItem* item = static_cast<TreeItem*>(index.internalPointer()); + return item->headerSize(); +} + bool TreeModel::hasEmptyHeader(const UModelIndex &index) const { if (!index.isValid()) @@ -230,6 +254,14 @@ UByteArray TreeModel::body(const UModelIndex &index) const return item->body(); } +UINT32 TreeModel::bodySize(const UModelIndex& index) const +{ + if (!index.isValid()) + return 0; + TreeItem* item = static_cast<TreeItem*>(index.internalPointer()); + return item->bodySize(); +} + bool TreeModel::hasEmptyBody(const UModelIndex &index) const { if (!index.isValid()) @@ -246,6 +278,14 @@ UByteArray TreeModel::tail(const UModelIndex &index) const return item->tail(); } +UINT32 TreeModel::tailSize(const UModelIndex& index) const +{ + if (!index.isValid()) + return 0; + TreeItem* item = static_cast<TreeItem*>(index.internalPointer()); + return item->tailSize(); +} + bool TreeModel::hasEmptyTail(const UModelIndex &index) const { if (!index.isValid()) @@ -299,7 +339,7 @@ bool TreeModel::compressed(const UModelIndex &index) const if (!index.isValid()) return false; TreeItem *item = static_cast<TreeItem*>(index.internalPointer()); - return item->compressed(); + return item->compressedInherited(); } void TreeModel::setFixed(const UModelIndex &index, const bool fixed) @@ -315,7 +355,7 @@ void TreeModel::setFixed(const UModelIndex &index, const bool fixed) if (fixed) { // Special handling for uncompressed to compressed boundary - if (item->compressed() && item->parent()->compressed() == FALSE) { + if (item->compressedInherited() && item->parent()->compressedInherited() == FALSE) { item->setFixed(item->parent()->fixed()); return; } @@ -352,6 +392,14 @@ void TreeModel::TreeModel::setMarkingDarkMode(const bool enabled) emit dataChanged(UModelIndex(), UModelIndex()); } +void TreeModel::TreeModel::setCStyleHexEnabled(const bool enabled) +{ + cStyleHexEnabledFlag = enabled; + setCStyleHexView(enabled); + + emit dataChanged(UModelIndex(), UModelIndex()); +} + void TreeModel::setMarking(const UModelIndex &index, const UINT8 marking) { if (!index.isValid()) @@ -501,7 +549,7 @@ void TreeModel::setUncompressedData(const UModelIndex &index, const UByteArray & UModelIndex TreeModel::addItem(const UINT32 offset, const UINT8 type, const UINT8 subtype, const UString & name, const UString & text, const UString & info, - const UByteArray & header, const UByteArray & body, const UByteArray & tail, + const UINT32 headerSize, const UINT32 bodySize, const UINT32 tailSize, const ItemFixedState fixed, const UModelIndex & parent, const UINT8 mode) { @@ -524,8 +572,8 @@ UModelIndex TreeModel::addItem(const UINT32 offset, const UINT8 type, const UINT } } - TreeItem *newItem = new TreeItem(offset, type, subtype, name, text, info, header, body, tail, Movable, this->compressed(parent), parentItem); - + TreeItem* newItem = new TreeItem(offset, type, subtype, name, text, info, headerSize, bodySize, tailSize, Movable, false, parentItem); + if (mode == CREATE_MODE_APPEND) { emit layoutAboutToBeChanged(); parentItem->appendChild(newItem); @@ -551,6 +599,7 @@ UModelIndex TreeModel::addItem(const UINT32 offset, const UINT8 type, const UINT UModelIndex created = createIndex(newItem->row(), parentColumn, newItem); setFixed(created, (bool)fixed); // Non-trivial logic requires additional call + return created; } @@ -601,7 +650,7 @@ goDeeper: UModelIndex currentIndex = parentIndex.model()->index(i, 0, parentIndex); UINT32 currentBase = this->base(currentIndex); - UINT32 fullSize = (UINT32)(header(currentIndex).size() + body(currentIndex).size() + tail(currentIndex).size()); + UINT32 fullSize = (UINT32)(headerSize(currentIndex) + bodySize(currentIndex) + tailSize(currentIndex)); if ((compressed(currentIndex) == false || (compressed(currentIndex) == true && compressed(currentIndex.parent()) == false)) // Base is meaningful only for true uncompressed items && currentBase <= base && base < currentBase + fullSize) { // Base must be in range [currentBase, currentBase + fullSize) // Found a better candidate diff --git a/common/treemodel.h b/common/treemodel.h index 0d6b5ac..fe08969 100644 --- a/common/treemodel.h +++ b/common/treemodel.h @@ -97,14 +97,15 @@ private: TreeItem *rootItem; bool markingEnabledFlag; bool markingDarkModeFlag; + bool cStyleHexEnabledFlag; public: QVariant data(const UModelIndex &index, int role) const; Qt::ItemFlags flags(const UModelIndex &index) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - TreeModel(QObject *parent = 0) : QAbstractItemModel(parent), markingEnabledFlag(true), markingDarkModeFlag(false) { - rootItem = new TreeItem(0, Types::Root, 0, UString(), UString(), UString(), UByteArray(), UByteArray(), UByteArray(), true, false); + TreeModel(QObject *parent = 0) : QAbstractItemModel(parent), markingEnabledFlag(true), markingDarkModeFlag(false), cStyleHexEnabledFlag (true) { + rootItem = new TreeItem(0, Types::Root, 0, UString(), UString(), UString(), 0, 0, 0, true, false); } #else @@ -116,6 +117,7 @@ private: TreeItem *rootItem; bool markingEnabledFlag; bool markingDarkModeFlag; + bool cStyleHexEnabledFlag; void dataChanged(const UModelIndex &, const UModelIndex &) {} void layoutAboutToBeChanged() {} @@ -126,7 +128,7 @@ public: UString headerData(int section, int orientation, int role = 0) const; TreeModel() : markingEnabledFlag(false), markingDarkModeFlag(false) { - rootItem = new TreeItem(0, Types::Root, 0, UString(), UString(), UString(), UByteArray(), UByteArray(), UByteArray(), true, false); + rootItem = new TreeItem(0, Types::Root, 0, UString(), UString(), UString(), 0, 0, 0, true, false); } bool hasIndex(int row, int column, const UModelIndex &parent = UModelIndex()) const { @@ -142,12 +144,17 @@ public: delete rootItem; } + void setImage(const UByteArray& image) { rootItem->setContent(image); } + bool markingEnabled() { return markingEnabledFlag; } void setMarkingEnabled(const bool enabled); bool markingDarkMode() { return markingDarkModeFlag; } void setMarkingDarkMode(const bool enabled); + bool cStyleHexEnabled() { return cStyleHexEnabledFlag; } + void setCStyleHexEnabled(const bool enabled); + UModelIndex index(int row, int column, const UModelIndex &parent = UModelIndex()) const; UModelIndex parent(const UModelIndex &index) const; int rowCount(const UModelIndex &parent = UModelIndex()) const; @@ -190,12 +197,15 @@ public: void setMarking(const UModelIndex &index, const UINT8 marking); UByteArray header(const UModelIndex &index) const; + UINT32 headerSize(const UModelIndex& index) const; bool hasEmptyHeader(const UModelIndex &index) const; UByteArray body(const UModelIndex &index) const; + UINT32 bodySize(const UModelIndex& index) const; bool hasEmptyBody(const UModelIndex &index) const; UByteArray tail(const UModelIndex &index) const; + UINT32 tailSize(const UModelIndex& index) const; bool hasEmptyTail(const UModelIndex &index) const; UByteArray parsingData(const UModelIndex &index) const; @@ -204,7 +214,7 @@ public: UModelIndex addItem(const UINT32 offset, const UINT8 type, const UINT8 subtype, const UString & name, const UString & text, const UString & info, - const UByteArray & header, const UByteArray & body, const UByteArray & tail, + const UINT32 headerSize, const UINT32 bodySize, const UINT32 tailSize, const ItemFixedState fixed, const UModelIndex & parent = UModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); diff --git a/common/types.h b/common/types.h index 0ee8247..ac82105 100755 --- a/common/types.h +++ b/common/types.h @@ -116,6 +116,7 @@ namespace Subtypes { Reserved1Region, Reserved2Region, PttRegion, + InvalidRegion, }; enum PaddingSubtypes { diff --git a/common/ubytearray.h b/common/ubytearray.h index 1623403..00dd20e 100644 --- a/common/ubytearray.h +++ b/common/ubytearray.h @@ -46,7 +46,6 @@ public: uint32_t toUInt(bool* ok = NULL, const uint8_t base = 10) { return (uint32_t)strtoul(d.c_str(), NULL, base); } int32_t size() const { return (int32_t)d.size(); } - int32_t count(char ch) const { return (int32_t)std::count(d.begin(), d.end(), ch); } char at(uint32_t i) const { return d.at(i); } char operator[](uint32_t i) const { return d[i]; } char& operator[](uint32_t i) { return d[i]; } diff --git a/common/ustring.cpp b/common/ustring.cpp index 2493a41..4f0a45c 100644 --- a/common/ustring.cpp +++ b/common/ustring.cpp @@ -14,19 +14,42 @@ #include <cstddef> #include <cstdint> #include <stdarg.h> +#include "printf/printf.c" + +static bool cStyleHexEnabled = false; + +void putchar_(char c) {} + +void setCStyleHexView(const bool enable) +{ + cStyleHexEnabled = enable; +} #if defined(QT_CORE_LIB) + +typedef struct { + char flag_cstyle_Xh; + UString* string; +} qstring_opt_t; + +void qstring_out(char c, void* extra_arg) +{ + ((qstring_opt_t*)extra_arg)->string->append(c); +} + UString usprintf(const char* fmt, ...) { UString msg; + qstring_opt_t opt = { cStyleHexEnabled ? '\1' : '\0', &msg }; + va_list vl; va_start(vl, fmt); - - msg = msg.vasprintf(fmt, vl); - + + int n = vofctprintf(qstring_out, &opt, fmt, vl); + va_end(vl); return msg; -}; +} UString urepeated(char c, int len) { @@ -84,7 +107,8 @@ UString usprintf(const char* fmt, ...) } va_start(arglist, fmt); - exvsnprintf(r, (char *)b->data, n + 1, fmt, arglist); + *(char*)b->data = cStyleHexEnabled ? '\1' : '\0'; + r = vosnprintf_((char*)b->data, n + 1, fmt, arglist); va_end(arglist); b->data[n] = '\0'; diff --git a/common/ustring.h b/common/ustring.h index c143df4..529d5fb 100644 --- a/common/ustring.h +++ b/common/ustring.h @@ -31,6 +31,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define ATTRIBUTE_FORMAT_(t,f,a) #endif +void setCStyleHexView(const bool enable); UString usprintf(const char* fmt, ...) ATTRIBUTE_FORMAT_(printf, 1, 2); UString urepeated(char c, int len); UString uFromUcs2(const char* str, size_t max_len = 0); diff --git a/common/utility.cpp b/common/utility.cpp index 612f3da..339f119 100755 --- a/common/utility.cpp +++ b/common/utility.cpp @@ -429,16 +429,6 @@ UINT32 calculateChecksum32(const UINT32* buffer, UINT32 bufferSize) return (UINT32)(0x100000000ULL - counter); } -// Get padding type for a given padding -UINT8 getPaddingType(const UByteArray & padding) -{ - if (padding.count('\x00') == padding.size()) - return Subtypes::ZeroPadding; - if (padding.count('\xFF') == padding.size()) - return Subtypes::OnePadding; - return Subtypes::DataPadding; -} - static inline int char2hex(char c) { if (c >= '0' && c <= '9') diff --git a/common/utility.h b/common/utility.h index 505d217..5203893 100755 --- a/common/utility.h +++ b/common/utility.h @@ -59,8 +59,33 @@ UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize); // 32bit checksum calculation routine UINT32 calculateChecksum32(const UINT32* buffer, UINT32 bufferSize); -// Return padding type from it's contents -UINT8 getPaddingType(const UByteArray & padding); +// Check if an array is filled in by a single repeated char +inline signed long checkSingle(const UByteArray& a, signed long defaultRc = -1) +{ + size_t s = a.size(); + if (!s) + return defaultRc; + if (s == 1 || memcmp(a.constData(), a.constData() + 1, s - 1) == 0) + return (unsigned char)a.at(0); + return -1; +} + +// Get padding type for a given padding +inline UINT8 getPaddingType(const UByteArray& a) +{ + size_t s = a.size(); + if (s) { + if (s == 1 || memcmp(a.constData(), a.constData() + 1, s - 1) == 0) { + switch (a.at(0)) { + case 0: + return Subtypes::ZeroPadding; + case 0xFF: + return Subtypes::OnePadding; + } + } + } + return Subtypes::DataPadding; +} // Make pattern from a hexstring with an assumption of . being any char bool makePattern(const CHAR8 *textPattern, std::vector<UINT8> &pattern, std::vector<UINT8> &patternMask);