上传文件至 /

4.0 Beta版本

代码库重构、msix 改进、更好的兼容性消息传递
添加 force,修复不匹配的 vars
修复 tcl 格式化
状态机代码和 tui 选项中的错误修复
This commit is contained in:
3A1 2025-07-03 22:59:24 +08:00
parent 50cfb36ed2
commit ab8dc65733
10 changed files with 1836 additions and 289 deletions

View File

@ -15,6 +15,7 @@ AUTHORS*
# Build artifacts and output
output/
build/
!src/build.py
dist/
*.bin
*.bit

262
CHANGELOG.md Normal file
View File

@ -0,0 +1,262 @@
# 📝 Changelog
[![PyPI version](https://badge.fury.io/py/pcileech-fw-generator.svg)](https://badge.fury.io/py/pcileech-fw-generator)
[![Python Support](https://img.shields.io/pypi/pyversions/pcileech-fw-generator.svg)](https://pypi.org/project/pcileech-fw-generator/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
All notable changes to the PCILeech Firmware Generator will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v0.1.2.html).
---
## 📑 Table of Contents
- [Version 0.3.1 (2025-06-09)](#020---2025-06-09)
- [Version 0.3.1 (2025-01-02)](#0110---2025-01-02)
- [Release Notes](#release-notes)
- [Backward Compatibility](#backward-compatibility)
- [Future Roadmap](#future-roadmap)
---
## [0.3.1] - 2025-06-10
### ✨ Added
- **🧩 Feature Integration**: Comprehensive integration of all major features
- Integrated documentation in `docs/INTEGRATED_FEATURES.md`
- Comprehensive integration tests in `tests/test_feature_integration.py`
- Seamless interoperation between all components
- **💾 Full 4 KB Config-Space Shadow in BRAM**: Complete configuration space emulation
- Full 4 KB configuration space shadow in BRAM
- Dual-port access for simultaneous read/write operations
- Overlay RAM for writable fields (Command/Status registers)
- Initialization from donor device configuration data
- Little-endian format compatible with PCIe specification
- **🔄 Auto-Replicate MSI-X Table**: Exact MSI-X table replication
- Automatic parsing of MSI-X capability structure from donor configuration space
- Parameterized SystemVerilog implementation of MSI-X table and PBA
- Support for byte-enable granularity writes
- Interrupt delivery logic with masking support
- Integration with the existing BAR controller and configuration space shadow
- **✂️ Capability Pruning**: Selective capability modification
- Automatic analysis of standard and extended PCI capabilities
- Categorization of capabilities based on emulation feasibility
- Selective pruning of unsupported capabilities
- Modification of partially supported capabilities
- Preservation of capability chain integrity
- **🎲 Deterministic Variance Seeding**: Consistent hardware variance
- Deterministic seed generation based on device serial number (DSN) and build revision
- Consistent variance parameters for the same donor device and build revision
- Different variance parameters for different donor devices or build revisions
- Support for different device classes with appropriate variance ranges
- **🏗️ Build Process**: Enhanced to support all integrated features
- Improved donor dump extraction with full 4 KB configuration space
- Added capability pruning step to the build process
- Added MSI-X table parameter extraction and integration
- Added deterministic variance seeding based on DSN and build revision
- **📋 Enhanced Logging**: Improved logging for all integrated features
- Added detailed logging for capability pruning
- Added MSI-X table parameter logging
- Added variance parameter logging
- Added integration status summary at the end of the build process
- **📚 Documentation**: Comprehensive documentation for all integrated features
- Added `docs/INTEGRATED_FEATURES.md` with detailed integration information
- Updated feature-specific documentation with integration details
- Added troubleshooting information for integrated features
- **🔌 MSI-X Table Integration**: Fixed issues with MSI-X table integration
- Corrected MSI-X table parameter extraction from configuration space
- Fixed MSI-X table and PBA memory mapping in BAR controller
- Improved error handling for MSI-X capability parsing
- **🧩 Capability Chain Integrity**: Fixed issues with capability chain integrity
- Ensured proper next pointer updates when removing capabilities
- Fixed extended capability chain traversal and modification
- Improved error handling for capability chain manipulation
- **⏱️ Timing Consistency**: Fixed issues with timing consistency
- Ensured deterministic variance seeding produces consistent results
- Fixed timing parameter calculation and application
- Improved error handling for variance parameter generation
## [0.3.1] - 2025-06-09
### ✨ Added
- **💾 Option-ROM Passthrough**: Complete Option-ROM replication from donor devices
- Extracts Option-ROM from donor PCI devices using Linux sysfs interface
- Supports two implementation modes:
- Mode A: BAR 5 Window (pure FPGA implementation)
- Mode B: External SPI Flash (for larger ROMs)
- Handles legacy 16-bit config cycles for ROM access
- Includes caching for improved performance
- Configurable ROM size and source
- **🔧 Build System Integration**: Enhanced build process for Option-ROM support
- Added command-line arguments for enabling and configuring Option-ROM feature
- Automatic ROM extraction during build process
- Support for using pre-extracted ROM files
- Build-time selection between implementation modes
- **🧪 Testing Infrastructure**: Comprehensive test suite for Option-ROM functionality
- Unit tests for Option-ROM extraction and handling
- Support for different ROM sizes and formats
- Validation of ROM signature and content
### 🔄 Changed
- **🔢 Version Bump**: Incremented to v0.3.1 to reflect significant Option-ROM feature addition
- **🏗️ Build Process**: Updated to support Option-ROM integration
- **📋 Enhanced Logging**: Improved logging for Option-ROM extraction and processing
## [Unreleased] - Build Process Improvements
### 🔄 Changed
- **🏗️ Local Build Default**: Changed local build to be the default process
- Local builds now run by default (no container required)
- Container builds now require explicit opt-in with `--use-donor-dump`
- Improved error handling for local build scenarios
- Enhanced documentation for local build workflows
- **🔧 Container Engine Options**: Added support for multiple container engines
- Added new `--container-engine` option to specify engine preference
- Podman is now the default container engine
- Docker remains fully supported as an alternative option
- Automatic detection of available container engines
- **🔍 Vivado Location Validation**: Enhanced Vivado detection and validation
- Improved cross-platform Vivado installation detection
- Added support for environment variables (XILINX_VIVADO)
- Automatic version detection and compatibility checking
- Detailed error messages for missing or incompatible installations
### 🔧 Fixed
- **🔌 VFIO Device Binding**: Fixed an issue where binding a device already bound to vfio-pci would fail
- Added detection for devices already bound to vfio-pci
- Improved error handling during the binding process
- Added comprehensive test cases for this edge case
- **📦 Container Dependency Installation**: Fixed missing Python dependencies in container build
- Added proper `pip install` commands for `requirements.txt` and `requirements-tui.txt`
- Fixed import errors for `psutil`, `pydantic`, and other required packages
- **📁 Container File Structure**: Corrected file paths and directory structure
- Fixed `build.py` path from `/app/build.py` to `/app/src/build.py`
- Updated all container usage examples and documentation
- **🔒 Container Security Improvements**: Enhanced security posture
- Replaced `--privileged` with specific capabilities (`--cap-add=SYS_RAWIO --cap-add=SYS_ADMIN`)
- Maintained non-root user execution while preserving functionality
- **✅ Container Health Checks**: Improved dependency validation
- Enhanced health check to validate Python imports
- Added comprehensive dependency testing
### ✨ Added
- **🔨 Container Build Script**: New automated build and test script
- Added `scripts/build_container.sh` with comprehensive testing
- Supports both Podman and Docker container engines
- Includes security validation and usage examples
- **🚀 Container CI Pipeline**: Automated container testing workflow
- Added `.github/workflows/container-ci.yml` for continuous integration
- Tests container build, dependencies, security, and integration
- Validates file structure and user permissions
### 📚 Improved
- **📖 Documentation Updates**: Enhanced container usage documentation
- Updated `podman_demo.md` with security best practices
- Added troubleshooting section for container issues
- Included capability-based security examples
### 🗂️ Changed
- **📦 Container File Inclusion**: Updated `.dockerignore` configuration
- Removed exclusion of `src/tui/` components
- Included necessary requirements files
- Optimized build context for better performance
---
### 🚀 Installation
```bash
# Basic installation
pip install pcileech-fw-generator
# With TUI support
pip install pcileech-fw-generator[tui]
# Development installation
pip install pcileech-fw-generator[dev]
```
### 🎮 Usage
```bash
# Command line interface (traditional)
pcileech-generate
# Interactive TUI interface (new)
pcileech-tui
# Direct build command
pcileech-build --bdf 0000:03:00.0 --board 75t
```
## [1.0.0] - 2024-12-01
### ✨ Added
- Initial release of PCILeech Firmware Generator
- Basic command-line interface for firmware generation
- Donor hardware analysis and configuration extraction
- Containerized build pipeline with Vivado integration
- USB-JTAG flashing support for DMA boards
- Basic SystemVerilog generation for PCIe devices
- Podman-based isolated build environment
### 🎯 Features
- PCIe device enumeration and selection
- Configuration space extraction from donor hardware
- FPGA bitstream generation for Artix-7 boards
- Automated driver binding and VFIO operations
- Basic logging and error handling
---
## 📋 Release Notes
### 🚀 v0.3.1 Highlights
This release integrates all major features of the PCILeech FPGA firmware generator, providing a comprehensive solution for PCIe device emulation. The integration ensures that all features work together seamlessly, providing a more realistic and functional emulation experience.
Key improvements include:
- **💾 Full 4 KB Config-Space Shadow**: Complete configuration space emulation with overlay RAM for writable fields
- **🔄 MSI-X Table Replication**: Exact replication of MSI-X tables from donor devices
- **✂️ Capability Pruning**: Selective modification of capabilities that can't be faithfully emulated
- **🎲 Deterministic Variance Seeding**: Consistent hardware variance based on device serial number and build revision
### 🚀 v0.3.1 Highlights
This release introduces the Option-ROM passthrough feature, allowing the PCILeech FPGA firmware to faithfully replicate the Option-ROM of donor PCI devices. This enables advanced functionality such as UEFI boot support and device-specific initialization.
Key improvements include:
- **💾 Complete Option-ROM Replication**: Extract and replicate Option-ROMs from donor devices
- **🔀 Dual Implementation Modes**: Choose between pure FPGA (BAR window) or SPI flash implementations
- **🔌 Legacy ROM Support**: Proper handling of legacy 16-bit config cycles for ROM access
- **🛠️ Flexible Configuration**: Command-line options for ROM source, size, and implementation mode
### 🚀 v0.3.1 Highlights
This major release introduces a modern, interactive TUI that transforms the user experience while maintaining full backward compatibility with the original command-line interface. The TUI provides guided workflows, real-time monitoring, and intelligent error handling that makes firmware generation more accessible and reliable.
Key improvements include:
- **🎯 Zero Learning Curve**: Intuitive interface guides users through the entire process
- **📊 Real-time Feedback**: Live monitoring of build progress and system resources
- **🛡️ Error Prevention**: Validation and checks prevent common configuration mistakes
- **📦 Professional Packaging**: Easy installation via pip with proper dependency management
### 🔄 Backward Compatibility
All existing command-line workflows continue to work unchanged. The new integrated features are designed to be backward compatible with existing workflows, ensuring a smooth transition for users.
### 🔮 Future Roadmap
- Web-based interface for remote build management
- Enhanced device compatibility and detection
- Advanced firmware customization options
- Integration with additional FPGA toolchains
- Cloud-based build services
## ⚠️ Disclaimer
This tool is intended for educational research and legitimate PCIe development purposes only. Users are responsible for ensuring compliance with all applicable laws and regulations. The authors assume no liability for misuse of this software.
---
**Version 0.3.1** - Major release with integrated features for comprehensive PCIe device emulation

408
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,408 @@
# 🤝 Contributing to PCILeech Firmware Generator
[![PyPI version](https://badge.fury.io/py/pcileech-fw-generator.svg)](https://badge.fury.io/py/pcileech-fw-generator)
[![Python Support](https://img.shields.io/pypi/pyversions/pcileech-fw-generator.svg)](https://pypi.org/project/pcileech-fw-generator/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![CI](https://github.com/ramseymcgrath/PCILeechFWGenerator/workflows/CI/badge.svg)](https://github.com/ramseymcgrath/PCILeechFWGenerator/actions)
Thank you for your interest in contributing to the PCILeech Firmware Generator! This document provides guidelines and information for contributors.
---
## 📑 Table of Contents
- [📜 Code of Conduct](#-code-of-conduct)
- [🚀 Getting Started](#-getting-started)
- [Prerequisites](#prerequisites)
- [Development Setup](#development-setup)
- [📋 Contributing Guidelines](#-contributing-guidelines)
- [Types of Contributions](#types-of-contributions)
- [Bug Reports](#bug-reports)
- [Feature Requests](#feature-requests)
- [🔄 Development Workflow](#-development-workflow)
- [Branch Strategy](#branch-strategy)
- [Making Changes](#making-changes)
- [Commit Message Format](#commit-message-format)
- [🧪 Testing](#-testing)
- [Test Structure](#test-structure)
- [Writing Tests](#writing-tests)
- [Running Tests](#running-tests)
- [💻 Code Style](#-code-style)
- [Python Style Guide](#python-style-guide)
- [Formatting Tools](#formatting-tools)
- [Pre-commit Hooks](#pre-commit-hooks)
- [📚 Documentation](#-documentation)
- [Code Documentation](#code-documentation)
- [Documentation Style](#documentation-style)
- [📤 Submitting Changes](#-submitting-changes)
- [Pull Request Process](#pull-request-process)
- [Pull Request Template](#pull-request-template)
- [Review Process](#review-process)
- [📦 Release Process](#-release-process)
- [Version Management](#version-management)
- [Release Steps](#release-steps)
- [Distribution](#distribution)
- [❓ Getting Help](#-getting-help)
- [Communication Channels](#communication-channels)
- [Development Resources](#development-resources)
- [🏆 Recognition](#-recognition)
- [⚠️ Disclaimer](#-disclaimer)
---
## 📜 Code of Conduct
This project adheres to a code of conduct that promotes a welcoming and inclusive environment. By participating, you are expected to uphold this code.
## 🚀 Getting Started
### Prerequisites
- Python 3.9 or higher
- Git
- Podman or Docker
- Vivado (for FPGA synthesis)
- Linux environment (required for PCIe operations)
### Development Setup
1. **Fork and Clone**
```bash
git clone https://github.com/yourusername/PCILeechFWGenerator.git
cd PCILeechFWGenerator
```
2. **Create Virtual Environment**
```bash
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
```
3. **Install Development Dependencies**
```bash
pip install -r requirements-dev.txt
```
4. **Install Pre-commit Hooks**
```bash
pre-commit install
```
5. **Verify Installation**
```bash
python -m pytest tests/
```
## 📋 Contributing Guidelines
### Types of Contributions
We welcome several types of contributions:
- **Bug Reports**: Help us identify and fix issues
- **Feature Requests**: Suggest new functionality
- **Code Contributions**: Implement features or fix bugs
- **Documentation**: Improve or add documentation
- **Testing**: Add or improve test coverage
### Bug Reports
When reporting bugs, please include:
- **Environment Details**: OS, Python version, hardware setup
- **Steps to Reproduce**: Clear, step-by-step instructions
- **Expected vs Actual Behavior**: What should happen vs what does happen
- **Error Messages**: Full error output and logs
- **Hardware Configuration**: PCIe devices, DMA boards, etc.
### Feature Requests
For feature requests, please provide:
- **Use Case**: Why is this feature needed?
- **Proposed Solution**: How should it work?
- **Alternatives Considered**: Other approaches you've thought about
- **Implementation Ideas**: Technical approach if you have one
## 🔄 Development Workflow
### Branch Strategy
- `main`: Stable release branch
- `develop`: Integration branch for new features
- `feature/feature-name`: Feature development branches
- `bugfix/issue-description`: Bug fix branches
- `hotfix/critical-fix`: Critical fixes for production
### Making Changes
1. **Create Feature Branch**
```bash
git checkout -b feature/your-feature-name
```
2. **Make Changes**
- Write code following our style guidelines
- Add tests for new functionality
- Update documentation as needed
3. **Test Changes**
```bash
# Run full test suite
python -m pytest tests/
# Run specific tests
python -m pytest tests/test_specific_module.py
# Run with coverage
python -m pytest --cov=src tests/
```
4. **Commit Changes**
```bash
git add .
git commit -m "feat: add new TUI feature for device selection"
```
### Commit Message Format
We use conventional commits for clear history:
```
<type>(<scope>): <description>
[optional body]
[optional footer]
```
**Types:**
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation changes
- `style`: Code style changes (formatting, etc.)
- `refactor`: Code refactoring
- `test`: Adding or updating tests
- `chore`: Maintenance tasks
**Examples:**
```
feat(tui): add real-time build progress monitoring
fix(build): resolve SystemVerilog generation error for network devices
docs(readme): update installation instructions for TUI
test(core): add unit tests for device manager
```
## 🧪 Testing
### Test Structure
```
tests/
├── unit/ # Unit tests for individual modules
├── integration/ # Integration tests for workflows
├── fixtures/ # Test data and fixtures
└── conftest.py # Pytest configuration
```
### Writing Tests
- **Unit Tests**: Test individual functions and classes
- **Integration Tests**: Test complete workflows
- **Mock External Dependencies**: Use pytest-mock for external services
- **Test Edge Cases**: Include error conditions and boundary cases
### Running Tests
```bash
# All tests
python -m pytest
# Specific test file
python -m pytest tests/test_build.py
# With coverage
python -m pytest --cov=src --cov-report=html
# Parallel execution
python -m pytest -n auto
# Specific markers
python -m pytest -m "not slow"
```
## 💻 Code Style
### Python Style Guide
We follow PEP 8 with some modifications:
- **Line Length**: 88 characters (Black default)
- **Import Sorting**: Use isort with Black profile
- **Type Hints**: Required for all public functions
- **Docstrings**: Google style for all modules, classes, and functions
### Formatting Tools
```bash
# Format code
black src/ tests/
# Sort imports
isort src/ tests/
# Lint code
flake8 src/ tests/
# Type checking
mypy src/
```
### Pre-commit Hooks
Our pre-commit configuration automatically runs:
- Black (code formatting)
- isort (import sorting)
- flake8 (linting)
- mypy (type checking)
- pytest (basic tests)
## 📚 Documentation
### Code Documentation
- **Docstrings**: All public functions, classes, and modules
- **Type Hints**: All function parameters and return values
- **Comments**: Explain complex logic and business rules
- **README Updates**: Keep installation and usage instructions current
### Documentation Style
```python
def generate_firmware(device_bdf: str, board_type: str) -> Path:
"""Generate firmware for specified PCIe device.
Args:
device_bdf: PCIe device Bus:Device.Function identifier
board_type: Target FPGA board type (35t, 75t, 100t)
Returns:
Path to generated firmware binary
Raises:
DeviceNotFoundError: If specified device doesn't exist
BuildError: If firmware generation fails
Example:
>>> firmware_path = generate_firmware("0000:03:00.0", "75t")
>>> print(f"Firmware generated: {firmware_path}")
"""
```
## 📤 Submitting Changes
### Pull Request Process
1. **Update Documentation**: Ensure README, docstrings, and comments are current
2. **Add Tests**: Include tests for new functionality
3. **Run Full Test Suite**: Ensure all tests pass
4. **Update Changelog**: Add entry to CHANGELOG.md
5. **Create Pull Request**: Use our PR template
### Pull Request Template
```markdown
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Manual testing completed
## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] Tests pass locally
```
### Review Process
1. **Automated Checks**: CI/CD pipeline runs tests and linting
2. **Code Review**: Maintainers review code and provide feedback
3. **Testing**: Changes are tested in development environment
4. **Approval**: At least one maintainer approval required
5. **Merge**: Changes merged to appropriate branch
## 📦 Release Process
### Version Management
We use semantic versioning (MAJOR.MINOR.PATCH):
- **MAJOR**: Breaking changes
- **MINOR**: New features (backward compatible)
- **PATCH**: Bug fixes (backward compatible)
### Release Steps
1. **Update Version**: Increment version in `src/__version__.py`
2. **Update Changelog**: Add release notes to CHANGELOG.md
3. **Create Release Branch**: `release/vX.Y.Z`
4. **Final Testing**: Comprehensive testing of release candidate
5. **Tag Release**: Create git tag with version
6. **Build Distribution**: Create wheel and source distributions
7. **Publish**: Upload to PyPI
8. **GitHub Release**: Create GitHub release with notes
### Distribution
```bash
# Build distributions
python -m build
# Check distributions
twine check dist/*
# Upload to PyPI
twine upload dist/*
```
## ❓ Getting Help
### Communication Channels
- **GitHub Issues**: Bug reports and feature requests
- **GitHub Discussions**: General questions and community discussion
- **Email**: Direct contact for security issues
### Development Resources
- **Architecture Documentation**: See `docs/` directory
- **API Reference**: Generated from docstrings
- **Examples**: See `examples/` directory
- **Test Cases**: See `tests/` directory
## 🏆 Recognition
Contributors are recognized in:
- **CHANGELOG.md**: Release notes mention contributors
- **GitHub Contributors**: Automatic recognition
- **Release Notes**: Major contributions highlighted
## ⚠️ Disclaimer
This tool is intended for educational research and legitimate PCIe development purposes only. Users are responsible for ensuring compliance with all applicable laws and regulations. The authors assume no liability for misuse of this software.
---
Thank you for contributing to PCILeech Firmware Generator!
**Version 0.3.1** - Major release with TUI interface and professional packaging

View File

@ -1,6 +1,6 @@
# Makefile for PCILeech Firmware Generator
.PHONY: help clean install install-dev test lint format build build-pypi upload-test upload-pypi release
.PHONY: help clean install install-dev test lint format build build-pypi upload-test upload-pypi release container docker-build build-container
# Default target
help:
@ -24,6 +24,10 @@ help:
@echo " upload-pypi - Upload to PyPI"
@echo " release - Full release process"
@echo ""
@echo "Container:"
@echo " container - Build container image (dma-fw)"
@echo " docker-build - Build container image (default tag)"
@echo ""
@echo "Utilities:"
@echo " check-deps - Check system dependencies"
@echo " security - Run security scans"
@ -86,10 +90,16 @@ security:
bandit -r src/
safety check
# Docker targets
# Container targets
container:
./scripts/build_container.sh --tag dma-fw
docker-build:
./scripts/build_container.sh
# Alias for container
build-container: container
# Test package build
test-build:
@echo "Testing PyPI package build..."

13
SECURITY.md Normal file
View File

@ -0,0 +1,13 @@
# Security Policy
## Supported Versions
Use the latest version. This tool is as secure as it can be but please read and evaluate the source code yourself
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.

Binary file not shown.

View File

@ -6,7 +6,7 @@ This script:
Enumerates PCIe devices
Allows user to select a donor device
Re-binds donor to vfio-pci driver
Launches Podman container (image: dma-fw) that runs build.py
Launches Podman container (image: pcileech-fw-generator) that runs build.py
Optionally flashes output/firmware.bin with usbloader
Restores original driver afterwards
@ -38,6 +38,35 @@ except ImportError:
PCILEECH_FPGA_REPO = "https://github.com/ufrisk/pcileech-fpga.git"
REPO_CACHE_DIR = os.path.expanduser("~/.cache/pcileech-fw-generator/repos")
def clear_python_cache():
"""Clear Python bytecode cache files to ensure updated code is used."""
import glob
cache_patterns = [
"__pycache__",
"src/__pycache__",
"tests/__pycache__",
"tests/tui/__pycache__",
"src/tui/__pycache__",
"src/tui/core/__pycache__",
"src/tui/models/__pycache__",
"src/scripts/__pycache__",
]
for pattern in cache_patterns:
for cache_dir in glob.glob(pattern):
if os.path.exists(cache_dir):
try:
shutil.rmtree(cache_dir)
print(f"[*] Cleared Python cache: {cache_dir}")
except Exception as e:
print(f"[!] Warning: Could not clear cache {cache_dir}: {e}")
# Clear Python cache at startup to ensure updated code is used
clear_python_cache()
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
@ -59,9 +88,19 @@ def validate_bdf_format(bdf: str) -> bool:
return bool(bdf_pattern.match(bdf))
def run_command(cmd: str, **kwargs) -> str:
"""Execute a shell command and return stripped output."""
return subprocess.check_output(cmd, shell=True, text=True, **kwargs).strip()
def run_command(cmd: str, timeout: int = 30, **kwargs) -> str:
"""Execute a shell command and return stripped output with timeout and better error handling."""
try:
return subprocess.check_output(
cmd, shell=True, text=True, timeout=timeout, **kwargs
).strip()
except subprocess.TimeoutExpired as e:
logger.error(f"Command timed out after {timeout}s: {cmd}")
raise RuntimeError(f"Command timed out: {cmd}") from e
except subprocess.CalledProcessError as e:
logger.error(f"Command failed (exit code {e.returncode}): {cmd}")
logger.error(f"Command stderr: {e.stderr}")
raise
def is_linux() -> bool:
@ -219,10 +258,89 @@ def flash_firmware(bitfile: pathlib.Path) -> None:
raise RuntimeError(error_msg) from e
def _validate_vfio_prerequisites() -> None:
"""Validate VFIO prerequisites and system configuration."""
# Check if VFIO modules are loaded
vfio_modules = ["/sys/module/vfio", "/sys/module/vfio_pci"]
loaded_modules = [mod for mod in vfio_modules if os.path.exists(mod)]
if not loaded_modules:
error_msg = (
"VFIO modules not loaded. Please load VFIO modules:\n"
" sudo modprobe vfio\n"
" sudo modprobe vfio-pci"
)
logger.error(error_msg)
raise RuntimeError(error_msg)
# Check if vfio-pci driver is available
if not os.path.exists("/sys/bus/pci/drivers/vfio-pci"):
error_msg = (
"vfio-pci driver not available. Ensure VFIO is enabled in kernel.\n"
"Check: cat /boot/config-$(uname -r) | grep -i vfio"
)
logger.error(error_msg)
raise RuntimeError(error_msg)
# Check if IOMMU is enabled
try:
dmesg_output = run_command("dmesg | grep -i iommu", timeout=10)
if (
"IOMMU enabled" not in dmesg_output
and "AMD-Vi" not in dmesg_output
and "Intel-IOMMU" not in dmesg_output
):
logger.warning(
"IOMMU may not be enabled. Check BIOS settings and kernel parameters."
)
except Exception:
logger.debug("Could not check IOMMU status from dmesg")
def _check_device_in_use(bdf: str) -> bool:
"""Check if device is currently in use by checking for open file descriptors."""
try:
# Check if device has any open file descriptors
lsof_output = run_command(
f"lsof /dev/vfio/* 2>/dev/null | grep -v COMMAND || true", timeout=5
)
if bdf in lsof_output:
logger.warning(f"Device {bdf} may be in use by another process")
return True
except Exception:
logger.debug("Could not check device usage with lsof")
return False
def _wait_for_device_state(
bdf: str, expected_driver: Optional[str], max_retries: int = 5
) -> bool:
"""Wait for device to reach expected driver state with retries."""
import time
for attempt in range(max_retries):
try:
current_driver = get_current_driver(bdf)
if current_driver == expected_driver:
return True
if attempt < max_retries - 1:
logger.debug(
f"Device {bdf} not in expected state (current: {current_driver}, expected: {expected_driver}), retrying in 1s..."
)
time.sleep(1)
except Exception as e:
logger.debug(f"Error checking device state (attempt {attempt + 1}): {e}")
if attempt < max_retries - 1:
time.sleep(1)
return False
def bind_to_vfio(
bdf: str, vendor: str, device: str, original_driver: Optional[str]
) -> None:
"""Bind PCIe device to vfio-pci driver"""
"""Bind PCIe device to vfio-pci driver with enhanced error handling and validation."""
check_linux_requirement("VFIO device binding")
if not validate_bdf_format(bdf):
@ -230,31 +348,46 @@ def bind_to_vfio(
f"Invalid BDF format: {bdf}. Expected format: DDDD:BB:DD.F (e.g., 0000:03:00.0)"
)
# Validate vendor and device IDs
if not re.match(r"^[0-9a-fA-F]{4}$", vendor):
raise ValueError(f"Invalid vendor ID format: {vendor}. Expected 4-digit hex.")
if not re.match(r"^[0-9a-fA-F]{4}$", device):
raise ValueError(f"Invalid device ID format: {device}. Expected 4-digit hex.")
logger.info(
f"Binding device {bdf} to vfio-pci driver (current driver: {original_driver or 'none'})"
f"Binding device {bdf} (vendor:{vendor} device:{device}) to vfio-pci driver (current driver: {original_driver or 'none'})"
)
# Early exit if already bound to vfio-pci
if original_driver == "vfio-pci":
print(
"[*] Device already bound to vfio-pci driver, skipping binding process..."
)
logger.info(f"Device {bdf} already bound to vfio-pci, skipping binding process")
return
else:
print("[*] Binding device to vfio-pci driver...")
print("[*] Binding device to vfio-pci driver...")
try:
# Check if vfio-pci driver is available
if not os.path.exists("/sys/bus/pci/drivers/vfio-pci"):
error_msg = (
"vfio-pci driver not available. Ensure VFIO is enabled in kernel."
)
# Validate VFIO prerequisites
_validate_vfio_prerequisites()
# Check if device exists
device_path = f"/sys/bus/pci/devices/{bdf}"
if not os.path.exists(device_path):
error_msg = f"PCIe device {bdf} not found in sysfs"
logger.error(error_msg)
raise RuntimeError(error_msg)
# Check if device is in use
if _check_device_in_use(bdf):
logger.warning(
f"Device {bdf} appears to be in use, proceeding with caution"
)
# Check if device ID is already registered with vfio-pci
device_id_registered = False
try:
# Try to read the IDs file to check if our device ID is already registered
ids_file = "/sys/bus/pci/drivers/vfio-pci/ids"
if os.path.exists(ids_file):
with open(ids_file, "r") as f:
@ -264,60 +397,173 @@ def bind_to_vfio(
f"Device ID {vendor}:{device} already registered with vfio-pci"
)
device_id_registered = True
except Exception as e:
except (OSError, IOError) as e:
logger.debug(f"Error checking registered device IDs: {e}")
# Continue with normal flow if we can't check
# Register device ID with vfio-pci if not already registered
if not device_id_registered:
logger.debug(f"Registering device ID {vendor}:{device} with vfio-pci")
try:
run_command(
f"echo {vendor} {device} > /sys/bus/pci/drivers/vfio-pci/new_id"
)
logger.info(
f"Successfully registered device ID {vendor}:{device} with vfio-pci"
)
except subprocess.CalledProcessError as e:
# If the error is "File exists", the device ID is already registered, which is fine
if "File exists" in str(e):
max_retries = 3
for attempt in range(max_retries):
try:
# Use direct file writing instead of shell redirection to avoid I/O errors
new_id_path = "/sys/bus/pci/drivers/vfio-pci/new_id"
with open(new_id_path, "w") as f:
f.write(f"{vendor} {device}\n")
logger.info(
f"Device ID {vendor}:{device} already registered with vfio-pci"
f"Successfully registered device ID {vendor}:{device} with vfio-pci"
)
else:
# Re-raise if it's a different error
logger.error(f"Failed to register device ID: {e}")
raise
break
except (OSError, IOError) as e:
if (
"File exists" in str(e)
or "Invalid argument" in str(e)
or "Device or resource busy" in str(e)
):
logger.info(
f"Device ID {vendor}:{device} already registered with vfio-pci or busy"
)
break
elif attempt < max_retries - 1:
logger.warning(
f"Failed to register device ID (attempt {attempt + 1}): {e}, retrying..."
)
import time
time.sleep(1)
else:
logger.error(
f"Failed to register device ID after {max_retries} attempts: {e}"
)
raise RuntimeError(f"Failed to register device ID: {e}")
# Unbind from current driver if present
if original_driver:
logger.debug(f"Unbinding from current driver: {original_driver}")
try:
run_command(f"echo {bdf} > /sys/bus/pci/devices/{bdf}/driver/unbind")
logger.info(f"Successfully unbound {bdf} from {original_driver}")
except subprocess.CalledProcessError as e:
logger.warning(f"Failed to unbind from current driver: {e}")
# Continue anyway, as the bind might still work
max_retries = 3
for attempt in range(max_retries):
try:
# Use direct file writing instead of shell redirection
unbind_path = f"/sys/bus/pci/devices/{bdf}/driver/unbind"
with open(unbind_path, "w") as f:
f.write(f"{bdf}\n")
logger.info(f"Successfully unbound {bdf} from {original_driver}")
# Bind to vfio-pci
# Wait for unbind to complete
if _wait_for_device_state(bdf, None, max_retries=3):
break
elif attempt < max_retries - 1:
logger.warning(
f"Device still bound after unbind (attempt {attempt + 1}), retrying..."
)
import time
time.sleep(1)
else:
logger.warning(
"Device may still be bound to original driver, continuing..."
)
break
except (OSError, IOError) as e:
if "No such device" in str(e) or "No such file or directory" in str(
e
):
logger.info(f"Device {bdf} already unbound")
break
elif attempt < max_retries - 1:
logger.warning(
f"Failed to unbind from current driver (attempt {attempt + 1}): {e}, retrying..."
)
import time
time.sleep(1)
else:
logger.warning(
f"Failed to unbind from current driver after {max_retries} attempts: {e}"
)
# Continue anyway, as the bind might still work
# Bind to vfio-pci with retries
logger.debug(f"Binding {bdf} to vfio-pci")
try:
run_command(f"echo {bdf} > /sys/bus/pci/drivers/vfio-pci/bind")
logger.info(f"Successfully bound {bdf} to vfio-pci")
print("[✓] Device successfully bound to vfio-pci driver")
except subprocess.CalledProcessError as e:
# Check if device is already bound to vfio-pci despite the error
current_driver = get_current_driver(bdf)
if current_driver == "vfio-pci":
logger.info(
f"Device {bdf} is already bound to vfio-pci despite bind command error"
)
print("[✓] Device is already bound to vfio-pci driver")
else:
logger.error(f"Failed to bind to vfio-pci: {e}")
raise
max_retries = 3
bind_successful = False
except subprocess.CalledProcessError as e:
for attempt in range(max_retries):
try:
# Use direct file writing instead of shell redirection
bind_path = "/sys/bus/pci/drivers/vfio-pci/bind"
with open(bind_path, "w") as f:
f.write(f"{bdf}\n")
# Verify binding was successful
if _wait_for_device_state(bdf, "vfio-pci", max_retries=3):
logger.info(f"Successfully bound {bdf} to vfio-pci")
print("[✓] Device successfully bound to vfio-pci driver")
bind_successful = True
break
elif attempt < max_retries - 1:
logger.warning(
f"Bind command succeeded but device not bound to vfio-pci (attempt {attempt + 1}), retrying..."
)
import time
time.sleep(1)
except (OSError, IOError) as e:
if "Device or resource busy" in str(e):
logger.warning(f"Device {bdf} is busy (attempt {attempt + 1})")
if attempt < max_retries - 1:
import time
time.sleep(2) # Longer wait for busy devices
continue
elif "No such device" in str(e) or "No such file or directory" in str(
e
):
logger.error(f"Device {bdf} disappeared during binding")
raise RuntimeError(f"Device {bdf} not found during binding")
elif attempt < max_retries - 1:
logger.warning(
f"Failed to bind to vfio-pci (attempt {attempt + 1}): {e}, retrying..."
)
import time
time.sleep(1)
else:
# Final attempt failed, check if device is actually bound
current_driver = get_current_driver(bdf)
if current_driver == "vfio-pci":
logger.info(
f"Device {bdf} is bound to vfio-pci despite bind command error"
)
print("[✓] Device is bound to vfio-pci driver")
bind_successful = True
break
else:
logger.error(
f"Failed to bind to vfio-pci after {max_retries} attempts: {e}"
)
raise RuntimeError(f"Failed to bind to vfio-pci: {e}")
if not bind_successful:
error_msg = (
f"Failed to bind device {bdf} to vfio-pci after {max_retries} attempts"
)
logger.error(error_msg)
raise RuntimeError(error_msg)
# Final verification
final_driver = get_current_driver(bdf)
if final_driver != "vfio-pci":
error_msg = (
f"Device {bdf} not bound to vfio-pci (current driver: {final_driver})"
)
logger.error(error_msg)
raise RuntimeError(error_msg)
except (OSError, IOError) as e:
error_msg = f"Failed to bind device to vfio-pci: {e}"
logger.error(error_msg)
raise RuntimeError(error_msg) from e
@ -328,7 +574,7 @@ def bind_to_vfio(
def restore_original_driver(bdf: str, original_driver: Optional[str]) -> None:
"""Restore the original driver binding for the PCIe device"""
"""Restore the original driver binding for the PCIe device with enhanced error handling."""
if not validate_bdf_format(bdf):
logger.warning(f"Invalid BDF format during restore: {bdf}")
return
@ -339,11 +585,69 @@ def restore_original_driver(bdf: str, original_driver: Optional[str]) -> None:
print("[*] Restoring original driver binding...")
try:
# Check if device is still bound to vfio-pci before attempting unbind
# Check if device exists
device_path = f"/sys/bus/pci/devices/{bdf}"
if not os.path.exists(device_path):
logger.warning(
f"Device {bdf} not found during restore, may have been removed"
)
return
# Check current driver state
current_driver = get_current_driver(bdf)
logger.debug(f"Current driver for {bdf}: {current_driver or 'none'}")
# Unbind from vfio-pci if currently bound
if current_driver == "vfio-pci":
logger.debug(f"Unbinding {bdf} from vfio-pci")
run_command(f"echo {bdf} > /sys/bus/pci/drivers/vfio-pci/unbind")
max_retries = 3
unbind_successful = False
for attempt in range(max_retries):
try:
# Use direct file writing instead of shell redirection
unbind_path = "/sys/bus/pci/drivers/vfio-pci/unbind"
with open(unbind_path, "w") as f:
f.write(f"{bdf}\n")
# Wait for unbind to complete
if _wait_for_device_state(bdf, None, max_retries=3):
logger.info(f"Successfully unbound {bdf} from vfio-pci")
unbind_successful = True
break
elif attempt < max_retries - 1:
logger.warning(
f"Device still bound to vfio-pci (attempt {attempt + 1}), retrying..."
)
import time
time.sleep(1)
except (OSError, IOError) as e:
if "No such device" in str(e) or "No such file or directory" in str(
e
):
logger.info(f"Device {bdf} already unbound from vfio-pci")
unbind_successful = True
break
elif attempt < max_retries - 1:
logger.warning(
f"Failed to unbind from vfio-pci (attempt {attempt + 1}): {e}, retrying..."
)
import time
time.sleep(1)
else:
logger.warning(
f"Failed to unbind from vfio-pci after {max_retries} attempts: {e}"
)
# Continue with restore attempt anyway
break
if not unbind_successful and get_current_driver(bdf) == "vfio-pci":
logger.warning(
f"Device {bdf} still bound to vfio-pci, restore may fail"
)
else:
logger.info(
f"Device {bdf} not bound to vfio-pci (current: {current_driver or 'none'})"
@ -353,18 +657,85 @@ def restore_original_driver(bdf: str, original_driver: Optional[str]) -> None:
if original_driver:
# Check if original driver is available
driver_path = f"/sys/bus/pci/drivers/{original_driver}"
if os.path.exists(driver_path):
logger.debug(f"Binding {bdf} back to {original_driver}")
run_command(f"echo {bdf} > /sys/bus/pci/drivers/{original_driver}/bind")
logger.info(f"Successfully restored {bdf} to {original_driver}")
else:
if not os.path.exists(driver_path):
logger.warning(
f"Original driver {original_driver} not available for restore"
)
print(f"Warning: Original driver {original_driver} not available")
return
logger.debug(f"Binding {bdf} back to {original_driver}")
max_retries = 3
restore_successful = False
for attempt in range(max_retries):
try:
# Use direct file writing instead of shell redirection
bind_path = f"/sys/bus/pci/drivers/{original_driver}/bind"
with open(bind_path, "w") as f:
f.write(f"{bdf}\n")
# Verify restore was successful
if _wait_for_device_state(bdf, original_driver, max_retries=3):
logger.info(f"Successfully restored {bdf} to {original_driver}")
print(f"[✓] Device restored to {original_driver} driver")
restore_successful = True
break
elif attempt < max_retries - 1:
logger.warning(
f"Restore command succeeded but device not bound to {original_driver} (attempt {attempt + 1}), retrying..."
)
import time
time.sleep(1)
except (OSError, IOError) as e:
if "Device or resource busy" in str(e):
logger.warning(
f"Device {bdf} is busy during restore (attempt {attempt + 1})"
)
if attempt < max_retries - 1:
import time
time.sleep(2)
continue
elif "No such device" in str(
e
) or "No such file or directory" in str(e):
logger.warning(f"Device {bdf} not found during restore")
break
elif attempt < max_retries - 1:
logger.warning(
f"Failed to restore to {original_driver} (attempt {attempt + 1}): {e}, retrying..."
)
import time
time.sleep(1)
else:
logger.warning(
f"Failed to restore to {original_driver} after {max_retries} attempts: {e}"
)
break
if not restore_successful:
final_driver = get_current_driver(bdf)
if final_driver == original_driver:
logger.info(
f"Device {bdf} is bound to {original_driver} despite restore errors"
)
print(f"[✓] Device is bound to {original_driver} driver")
else:
logger.warning(
f"Failed to restore {bdf} to {original_driver}, current driver: {final_driver or 'none'}"
)
print(
f"Warning: Failed to restore to {original_driver}, current driver: {final_driver or 'none'}"
)
else:
logger.info(f"No original driver to restore for {bdf}")
print("[*] No original driver to restore")
except subprocess.CalledProcessError as e:
except (OSError, IOError) as e:
logger.warning(f"Failed to restore original driver for {bdf}: {e}")
print(f"Warning: Failed to restore original driver: {e}")
except Exception as e:
@ -372,10 +743,136 @@ def restore_original_driver(bdf: str, original_driver: Optional[str]) -> None:
print(f"Warning: Unexpected error during driver restore: {e}")
def _validate_vfio_device_access(vfio_device: str, bdf: str) -> None:
"""Validate VFIO device access and permissions."""
# Check if VFIO device exists
if not os.path.exists(vfio_device):
error_msg = f"VFIO device {vfio_device} not found"
logger.error(error_msg)
raise RuntimeError(error_msg)
# Check if /dev/vfio/vfio exists (VFIO container device)
vfio_container = "/dev/vfio/vfio"
if not os.path.exists(vfio_container):
error_msg = f"VFIO container device {vfio_container} not found"
logger.error(error_msg)
raise RuntimeError(error_msg)
# Check device permissions
try:
import stat
vfio_stat = os.stat(vfio_device)
if not (vfio_stat.st_mode & stat.S_IRGRP) or not (
vfio_stat.st_mode & stat.S_IWGRP
):
logger.warning(
f"VFIO device {vfio_device} may not have proper group permissions"
)
except OSError as e:
logger.warning(f"Could not check VFIO device permissions: {e}")
# Verify device is actually bound to vfio-pci
current_driver = get_current_driver(bdf)
if current_driver != "vfio-pci":
error_msg = (
f"Device {bdf} not bound to vfio-pci (current: {current_driver or 'none'})"
)
logger.error(error_msg)
raise RuntimeError(error_msg)
def _validate_container_environment() -> None:
"""Validate container runtime environment."""
# Check if podman is available
if shutil.which("podman") is None:
error_msg = "Podman not found in PATH. Please install Podman container runtime."
logger.error(error_msg)
raise RuntimeError(error_msg)
# Check if container image exists
try:
result = run_command(
"podman images --format '{{.Repository}}:{{.Tag}}' | grep '^pcileech-fw-generator:'",
timeout=10,
)
if not result:
# Container image not found, try to build it automatically
logger.info(
"Container image 'pcileech-fw-generator' not found. Building it now..."
)
print(
"[*] Container image 'pcileech-fw-generator' not found. Building it now..."
)
try:
# Use the proper build script which handles the container building correctly
build_script_path = "scripts/build_container.sh"
if os.path.exists(build_script_path):
logger.info("Using build script for container creation")
build_result = subprocess.run(
f"bash {build_script_path} --tag pcileech-fw-generator:latest",
shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
else:
# Fallback to direct container build
build_result = subprocess.run(
"podman build -t pcileech-fw-generator:latest -f Containerfile .",
shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
logger.info("Container image built successfully")
print("[✓] Container image built successfully")
except subprocess.CalledProcessError as e:
error_msg = f"Failed to build container image automatically: {e.stderr}\nPlease build manually with: make container"
logger.error(error_msg)
raise RuntimeError(error_msg)
except subprocess.CalledProcessError:
# If we can't check, try to build anyway
logger.info("Could not check container image status. Attempting to build...")
print("[*] Could not check container image status. Attempting to build...")
try:
build_script_path = "scripts/build_container.sh"
if os.path.exists(build_script_path):
logger.info("Using build script for container creation")
build_result = subprocess.run(
f"bash {build_script_path} --tag pcileech-fw-generator:latest",
shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
else:
# Fallback to direct container build
build_result = subprocess.run(
"podman build -t pcileech-fw-generator:latest -f Containerfile .",
shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
logger.info("Container image built successfully")
print("[✓] Container image built successfully")
except subprocess.CalledProcessError as e:
error_msg = f"Failed to build container image: {e.stderr}\nPlease build manually with: make container"
logger.error(error_msg)
raise RuntimeError(error_msg)
def run_build_container(
bdf: str, board: str, vfio_device: str, args: argparse.Namespace
) -> None:
"""Run the firmware build in a Podman container"""
"""Run the firmware build in a Podman container with enhanced validation and error handling."""
if not validate_bdf_format(bdf):
raise ValueError(
f"Invalid BDF format: {bdf}. Expected format: DDDD:BB:DD.F (e.g., 0000:03:00.0)"
@ -396,17 +893,24 @@ def run_build_container(
logger.info(f"Starting container build for device {bdf} on board {board}")
# Ensure output directory exists
os.makedirs("output", exist_ok=True)
# Validate container environment
_validate_container_environment()
# Validate VFIO device exists
if not os.path.exists(vfio_device):
error_msg = f"VFIO device {vfio_device} not found"
# Ensure output directory exists with proper permissions
output_dir = "output"
os.makedirs(output_dir, exist_ok=True)
# Check output directory permissions
if not os.access(output_dir, os.W_OK):
error_msg = f"Output directory {output_dir} is not writable"
logger.error(error_msg)
raise RuntimeError(error_msg)
# Validate VFIO device access
_validate_vfio_device_access(vfio_device, bdf)
# Build the build.py command with all arguments - use modular build system if available
build_cmd_parts = [f"sudo python3 /app/build.py --bdf {bdf} --board {board}"]
build_cmd_parts = [f"sudo python3 /app/src/build.py --bdf {bdf} --board {board}"]
# Add advanced features arguments
if args.advanced_sv:
@ -441,7 +945,7 @@ def run_build_container(
--device={vfio_device} \
--device=/dev/vfio/vfio \
-v {os.getcwd()}/output:/app/output \
dma-fw \
pcileech-fw-generator:latest \
{build_cmd}
"""
).strip()
@ -583,7 +1087,7 @@ def validate_environment() -> None:
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent / "src"))
from src.vivado_utils import find_vivado_installation
from src.vivado_utils import find_vivado_installation, get_vivado_search_paths
vivado_info = find_vivado_installation()
if vivado_info:
@ -592,29 +1096,95 @@ def validate_environment() -> None:
)
print(f"[✓] Vivado {vivado_info['version']} detected")
else:
logger.warning("Vivado not found. It will be used from the container.")
print("[!] Warning: Vivado not found locally. Will use container version.")
# Show what paths were checked using the utility function
checked_paths = get_vivado_search_paths()
error_msg = f"Vivado not found. Checked paths:\n" + "\n".join(
f"{path}" for path in checked_paths
)
logger.error(error_msg)
print(f"[✗] {error_msg}")
# Allow user to skip
try:
response = (
input("\nWould you like to continue without Vivado? (y/N): ")
.strip()
.lower()
)
if response in ["y", "yes"]:
print("[!] Continuing without Vivado - some features may not work")
logger.warning("User chose to continue without Vivado")
else:
raise RuntimeError(
"Vivado is required. Please install Vivado and try again."
)
except (KeyboardInterrupt, EOFError):
raise RuntimeError(
"Vivado is required. Please install Vivado and try again."
)
except ImportError:
logger.warning("Could not import vivado_utils. Skipping Vivado check.")
print("[!] Warning: Skipping Vivado check.")
error_msg = (
"Could not import vivado_utils. Please ensure Vivado is properly installed."
)
logger.error(error_msg)
print(f"[✗] {error_msg}")
# Allow user to skip
try:
response = (
input("\nWould you like to continue without Vivado? (y/N): ")
.strip()
.lower()
)
if response in ["y", "yes"]:
print("[!] Continuing without Vivado - some features may not work")
logger.warning("User chose to continue without Vivado")
else:
raise RuntimeError(
"Vivado is required. Please install Vivado and try again."
)
except (KeyboardInterrupt, EOFError):
raise RuntimeError(
"Vivado is required. Please install Vivado and try again."
)
# Check if container image exists
try:
result = run_command("podman images dma-fw --format '{{.Repository}}'")
if "dma-fw" not in result:
result = run_command(
"podman images pcileech-fw-generator --format '{{.Repository}}'"
)
if "pcileech-fw-generator" not in result:
# Container image not found, try to build it
logger.info("Container image 'dma-fw' not found. Building it now...")
print("[*] Container image 'dma-fw' not found. Building it now...")
logger.info(
"Container image 'pcileech-fw-generator' not found. Building it now..."
)
print(
"[*] Container image 'pcileech-fw-generator' not found. Building it now..."
)
try:
build_result = subprocess.run(
"podman build -t dma-fw .",
shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
# Use the proper build script which handles the container building correctly
build_script_path = "scripts/build_container.sh"
if os.path.exists(build_script_path):
logger.info("Using build script for container creation")
build_result = subprocess.run(
f"bash {build_script_path} --tag pcileech-fw-generator:latest",
shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
else:
# Fallback to direct container build
build_result = subprocess.run(
"podman build -t pcileech-fw-generator:latest -f Containerfile .",
shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
logger.info("Container image built successfully")
print("[✓] Container image built successfully")
except subprocess.CalledProcessError as e:

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,18 @@
#!/bin/bash
# Wrapper script to run pcileech-build with sudo while preserving Python path
# Wrapper script to run pcileech-generate with sudo while preserving Python path
#
# generate.py is the correct entry point that orchestrates:
# • Device enumeration and selection
# • Driver rebinding to vfio-pci
# • Container execution with build.py
# • Optional firmware flashing
# Get the path to the installed pcileech-build script
PCILEECH_BUILD_PATH=$(which pcileech-build)
# Get the path to the installed pcileech-generate script
PCILEECH_GENERATE_PATH=$(which pcileech-generate)
if [ -z "$PCILEECH_BUILD_PATH" ]; then
echo "Error: pcileech-build not found in PATH"
if [ -z "$PCILEECH_GENERATE_PATH" ]; then
echo "Error: pcileech-generate not found in PATH"
echo "Make sure pcileechfwgenerator is properly installed: pip install pcileechfwgenerator"
exit 1
fi
@ -14,5 +21,5 @@ PYTHON_USER_SITE=$(python3 -m site --user-site)
PYTHON_SITE_PACKAGES=$(python3 -c "import site; print(':'.join(site.getsitepackages()))")
# Run with sudo, preserving the PYTHONPATH
echo "Running pcileech-build with sudo and preserved Python paths..."
sudo PYTHONPATH="$PYTHONPATH:$PYTHON_USER_SITE:$PYTHON_SITE_PACKAGES" "$PCILEECH_BUILD_PATH" "$@"
echo "Running pcileech-generate with sudo and preserved Python paths..."
sudo PYTHONPATH="$PYTHONPATH:$PYTHON_USER_SITE:$PYTHON_SITE_PACKAGES" "$PCILEECH_GENERATE_PATH" "$@"

View File

@ -80,7 +80,18 @@ pcileech-tui = "src.tui_cli:main"
pcileech-build = "src.build_cli:main"
[tool.setuptools]
packages = ["src", "src.tui", "src.tui.core", "src.tui.models", "src.scripts"]
packages = [
"src",
"src.tui",
"src.tui.core",
"src.tui.models",
"src.scripts",
"src.build",
"src.build.analysis",
"src.build.config",
"src.build.orchestration",
"src.build.generators"
]
[tool.setuptools.dynamic]
version = {attr = "src.__version__.__version__"}