diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000..bbe21b712c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,234 @@ +# nvme-cli Codebase Guide for AI Agents + +## Project Overview + +nvme-cli is a cross-platform NVMe management utility (Linux/Windows) with an integrated libnvme library (v3.x+). The project uses Meson build system and supports an extensible plugin architecture for vendor-specific commands. + +**Critical:** Starting with nvme-cli 3.x, libnvme is fully integrated into the source tree at `libnvme/` - there's no external dependency. + +## Architecture + +### Component Structure + +- **nvme-cli** (`nvme.c`, `nvme-builtin.h`): Main CLI tool with built-in NVMe commands +- **libnvme** (`libnvme/src/nvme/`): Cross-platform NVMe library with OS-specific ioctl implementations + - `ioctl.c` (Linux), `ioctl-windows.c` (Windows) +- **Plugins** (`plugins/*/`): Vendor-specific extensions (OCP, WDC, Intel, Solidigm, etc.) +- **Utilities** (`util/`): Shared helpers (argument parsing, formatting, cleanup) + +### Command Registration Pattern + +Commands use a macro-based registration system via `define_cmd.h`: + +1. **Built-in commands** (`nvme-builtin.h`): + ```c + COMMAND_LIST( + ENTRY("list", "List all NVMe devices", list) + ENTRY("id-ctrl", "Send Identify Controller", id_ctrl) + ) + ``` + +2. **Plugin commands** (e.g., `plugins/ocp/ocp-nvme.h`): + ```c + PLUGIN(NAME("ocp", "OCP cloud SSD extensions", OCP_PLUGIN_VERSION), + COMMAND_LIST( + ENTRY("smart-add-log", "Retrieve extended SMART", smart_add_log) + ) + ) + ``` + +Commands are invoked as: `nvme ` (built-in) or `nvme ` (plugin). + +### Cross-Platform Abstraction + +- **Platform detection**: Use Meson's `build_machine.system()` checks +- **OS-specific code**: Separate implementations in `libnvme/src/nvme/ioctl-*.c` +- **Windows quirks**: + - Network libraries (ws2_32, wsock32, iphlpapi) auto-linked on Windows + - Linux-only features (json-c, liburing, keyutils) auto-disabled on Windows + +## Build System (Meson) + +### Standard Build Commands + +**Linux:** +```bash +meson setup .build +meson compile -C .build +meson install -C .build +``` + +**Windows (MSYS2):** +```bash +meson setup .build +meson compile -C .build +meson install -C .build +``` + +**Windows (PowerShell):** +```powershell +meson setup .build +meson compile -C .build +``` + +### Key Build Options + +- `-Dnvme=enabled|disabled` - Build nvme CLI (default: enabled) +- `-Dlibnvme=enabled|disabled` - Build libnvme library (default: enabled) +- `-Dtests=true|false` - Build tests (default: true) +- `-Ddefault_library=static|shared` - Library type +- `-Djson-c=enabled|auto|disabled` - JSON support (required for plugins on Linux) + +**Important:** Disabling json-c on Linux disables all plugins. + +### Build Configurations + +See `scripts/build.sh` for CI configurations: +- `scripts/build.sh` - Default build +- `scripts/build.sh fallback` - Download all dependencies +- `scripts/build.sh static` - Static binary +- `scripts/build.sh libnvme` - libnvme only + +## Development Workflows + +### Adding a New Built-in Command + +1. Add `ENTRY()` to `nvme-builtin.h` +2. Implement callback in `nvme.c`: + ```c + static int my_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin) + ``` +3. Use `argconfig` for option parsing (see existing commands) +4. Return 0 on success, errno on failure + +### Adding a Plugin Command + +1. Create plugin header in `plugins//-nvme.h`: + ```c + #undef CMD_INC_FILE + #define CMD_INC_FILE plugins//-nvme + + PLUGIN(NAME("", "Description", VERSION), + COMMAND_LIST( + ENTRY("cmd-name", "Description", callback_fn) + ) + ) + #include "define_cmd.h" + ``` +2. Implement commands in `plugins//-nvme.c` +3. Update `plugins/meson.build` to include new plugin + +### Testing + +Python-based tests in `tests/`: +- Test framework: `nvme_test.py`, `nvme_test_logger.py` +- Run with: `meson test -C .build` or `pytest tests/` +- Tests require real NVMe hardware (controlled by `-Dnvme-tests=true`) + +### Windows Development + +You're currently working on Windows implementation (`libnvme/src/nvme/ioctl-windows.c`). Key points: + +- Use Windows API: `DeviceIoControl()`, `CreateFile()`, etc. +- Convert Windows errors to errno with helper functions +- Many Linux features unsupported: reset, rescan, io_uring +- Test using MinGW builds + +## Code Conventions + +### Coding Style + +**Follow Linux kernel coding style** for all code contributions: + +- **Indentation**: Use tabs (not spaces) for indentation, with 8-character tab stops +- **Line length**: Should not exceed 80 characters with 8-space tabs if possible. This is a soft guideline - readability takes priority, with a hard limit of 100 columns for code. +- **Bracing**: K&R style - opening brace on same line, closing brace on new line. For conditional statements with single instructions in all branches, do not use braces. + ```c + // Multiple statements - use braces + if (condition) { + do_something(); + do_another_thing(); + } else { + do_something_else(); + } + + // Single statement in all branches - no braces + if (condition) + do_something(); + else + do_something_else(); + ``` +- **Naming**: + - Functions/variables: lowercase with underscores (`get_smart_log`, `namespace_id`) + - Macros/constants: uppercase with underscores (`NVME_LOG_PAGE_SIZE`) + - Struct members: lowercase with underscores +- **Spacing**: Space after keywords (`if (`, `while (`), no space for function calls (`func(`) +- **Pointer declarations**: Attach asterisk to type name, not variable name: `type *name` not `type* name` + ```c + int *ptr; // Correct + char *str; // Correct + void *user_data; // Correct + int* ptr; // Wrong + ``` +- **Comments**: Follow these conventions: + - **Multi-line block comments**: Use Linux kernel style with `/* */` wrapper and ` * ` prefix: + ```c + /* + * This is a multi-line comment explaining something + * with important details across multiple lines. + */ + ``` + - **Single-line standalone comments**: Use `/* */` format: `/* Single line comment */` + - **Inline comments** (end-of-line): Use `/* */` format: `/* Inline comment */` + - **TODO comments**: Use C++ style: `// TODO: description` + - **SPDX license headers**: Use C style: `/* SPDX-License-Identifier: ... */` +- **Type declarations**: Prefer kernel types (`__u32`, `__le16`) for hardware structures + +See the Linux kernel [coding style documentation](https://www.kernel.org/doc/html/latest/process/coding-style.html) for complete details. + +### Commit Messages + +Follow Linux kernel style: +``` +: + + + +Signed-off-by: Name +``` + +Examples: `nvme: fix buffer overflow`, `ocp: add latency monitor log` + +### Error Handling + +- Return 0 on success, negative errno on failure +- Use `nvme_status_to_errno()` for NVMe status codes +- Log errors with `nvme_show_error()` or `perror()` + +### Memory Management + +- Use cleanup attributes for auto-cleanup: `_cleanup_(nvme_free_tree)`, `_cleanup_free_` +- Defined in `util/cleanup.h` +- Example: `_cleanup_free_ char *buf = malloc(size);` + +### Licensing + +- nvme-cli: GPL-2.0-or-later (some files GPL-2.0-only) +- libnvme: LGPL-2.1-or-later +- Always include SPDX header: `// SPDX-License-Identifier: GPL-2.0-or-later` + +## Key Files Reference + +- `nvme.c` (11k lines) - Main CLI implementation +- `nvme-builtin.h` - Built-in command registry +- `define_cmd.h` - Command macro magic +- `plugin.c`, `plugin.h` - Plugin infrastructure +- `ENVIRONMENT.md` - Dependency setup guide +- `meson.build` - Root build configuration + +## Common Pitfalls + +1. **Plugins disabled**: Check if json-c is enabled on Linux builds +2. **Test failures**: Tests require real NVMe hardware and `-Dnvme-tests=true` +3. **libnvme not found**: In 3.x+, libnvme is integrated - don't install separately +4. **Wrong command invocation**: Plugins use `nvme `, not `nvme ` \ No newline at end of file diff --git a/.github/workflows/micron-build.yml b/.github/workflows/micron-build.yml new file mode 100644 index 0000000000..d9b959e68c --- /dev/null +++ b/.github/workflows/micron-build.yml @@ -0,0 +1,163 @@ +--- +name: micron-build + +on: + push: + branches: [mingw] # Changed from master + pull_request: + branches: [mingw] # Changed from master + + workflow_dispatch: + +jobs: + nvme-cli: + runs-on: ubuntu-latest + strategy: + matrix: + compiler: [gcc, clang] + buildtype: [debug, release] + container: + image: ghcr.io/linux-nvme/debian.python:latest + steps: + - uses: actions/checkout@v5 + - name: Mark repo as safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: build + run: | + scripts/build.sh -b ${{ matrix.buildtype }} -c ${{ matrix.compiler }} -x + - uses: actions/upload-artifact@v5 + name: upload logs + if: failure() + with: + name: logs files + path: | + .build-ci/meson-logs/*.txt + + libnvme: + runs-on: ubuntu-latest + strategy: + matrix: + compiler: [gcc, clang] + buildtype: [debug, release] + container: + image: ghcr.io/linux-nvme/debian.python:latest + steps: + - uses: actions/checkout@v5 + - name: Mark repo as safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: build + run: | + scripts/build.sh -b ${{ matrix.buildtype }} -c ${{ matrix.compiler }} -x libnvme + - uses: actions/upload-artifact@v5 + name: upload logs + if: failure() + with: + name: libnvme logs files + path: | + .build-ci/meson-logs/*.txt + + cross: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - arch: armhf + - arch: s390x + - arch: ppc64le + steps: + - uses: actions/checkout@v5 + + # enable foreign arch (replace unverified dbhi/qus with verified Docker QEMU setup) + - name: Enable QEMU for cross-arch + uses: docker/setup-qemu-action@v3 + with: + platforms: arm,ppc64le,s390x + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # compile and run unit tests (replace unverified mosteo-actions/docker-run with native docker run) + - name: compile and run unit tests + run: | + set -eux + docker pull --platform linux/amd64 ghcr.io/linux-nvme/ubuntu-cross-${{ matrix.arch }}:latest + docker run --rm \ + --platform linux/amd64 \ + -v "${{ github.workspace }}:/build" \ + -w /build \ + ghcr.io/linux-nvme/ubuntu-cross-${{ matrix.arch }}:latest \ + bash -lc ' + git config --global --add safe.directory /build + scripts/build.sh -b release -c gcc -t ${{ matrix.arch }} cross + ' + + - uses: actions/upload-artifact@v5 + name: upload logs + if: failure() + with: + name: log files + path: | + .build-ci/meson-logs/*.txt + + fallback-shared-libraries: + name: fallback shared libraries + runs-on: ubuntu-latest + container: + image: ghcr.io/linux-nvme/debian:latest + if: github.ref == 'refs/heads/mingw' # Changed from master + steps: + - uses: actions/checkout@v5 + - name: Mark repo as safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: build + run: | + scripts/build.sh -b release -c gcc fallback + - uses: actions/upload-artifact@v5 + if: failure() + with: + name: log files + path: | + .build-ci/meson-logs/*.txt + + build-muon: + name: muon minimal static + runs-on: ubuntu-latest + container: + image: ghcr.io/linux-nvme/debian:latest + steps: + - uses: actions/checkout@v5 + - name: Mark repo as safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: build + run: | + scripts/build.sh -m muon + + build-make-static: + name: make static + runs-on: ubuntu-latest + container: + image: ghcr.io/linux-nvme/debian:latest + steps: + - uses: actions/checkout@v5 + - name: Mark repo as safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: build + run: | + make static + + build-distro: + name: build libnvme and nvme-cli separately + runs-on: ubuntu-latest + container: + image: ghcr.io/linux-nvme/debian:latest + steps: + - uses: actions/checkout@v5 + - name: Mark repo as safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: build + run: | + scripts/build.sh distro diff --git a/.github/workflows/micron-checkpatch.yml b/.github/workflows/micron-checkpatch.yml new file mode 100644 index 0000000000..7cb83ad05c --- /dev/null +++ b/.github/workflows/micron-checkpatch.yml @@ -0,0 +1,51 @@ +name: Micron checkpatch review + +on: + pull_request: + branches: [mingw] # Branch is not specified in upstream file. + +jobs: + checkpatch: + name: checkpatch review + runs-on: ubuntu-latest + + steps: + # Same as upstream: compute how many commits to fetch + - name: Calculate PR commits + 1 + run: echo "PR_FETCH_DEPTH=$(( ${{ github.event.pull_request.commits }} + 1 ))" >> $GITHUB_ENV + + # GitHub-owned (allowed) checkout action + - name: Checkout PR branch + uses: actions/checkout@v5 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: ${{ env.PR_FETCH_DEPTH }} + + # Install dependencies needed to run checkpatch.pl + - name: Install prerequisites + run: | + sudo apt-get update + sudo apt-get install -y wget perl + + - name: Download checkpatch.pl and dependencies + run: | + wget https://raw.githubusercontent.com/torvalds/linux/master/scripts/checkpatch.pl \ + -O checkpatch.pl + wget https://raw.githubusercontent.com/torvalds/linux/master/scripts/spelling.txt \ + -O spelling.txt + chmod +x checkpatch.pl + + - name: Run checkpatch review + run: | + git fetch origin ${{ github.base_ref }} + git diff origin/${{ github.base_ref }} > patch.diff + + echo "::group::Checkpatch Results" + if ./checkpatch.pl --no-tree --show-types patch.diff; then + echo "::endgroup::" + echo "✅ No checkpatch issues found" + else + echo "::endgroup::" + echo "::error::Checkpatch found issues. Please fix them before merging." + exit 1 + fi diff --git a/.github/workflows/micron-merge-master-to-mingw.yml b/.github/workflows/micron-merge-master-to-mingw.yml new file mode 100644 index 0000000000..4d534f5ea4 --- /dev/null +++ b/.github/workflows/micron-merge-master-to-mingw.yml @@ -0,0 +1,54 @@ +name: Micron - Merge master to mingw + +on: + push: + branches: + # Trigger merge when master is synced to upstream. + - master + schedule: + # Backup: Run at 4:30 AM UTC (1 hour after sync completes) + - cron: '30 4 * * *' + workflow_dispatch: + +jobs: + merge: + runs-on: ubuntu-latest + + steps: + - name: Generate token + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.TACT_SYNC_APP_ID }} + private-key: ${{ secrets.TACT_SYNC_APP_PRIVATE_KEY }} + + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: mingw + fetch-depth: 0 + token: ${{ steps.generate-token.outputs.token }} + + - name: Configure Git identity + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Merge master into mingw + run: | + set -euo pipefail + git fetch origin master + git merge origin/master -m "Auto-merge master into mingw" + + - name: Push changes + run: | + set -euo pipefail + git push origin mingw + + - name: Notify on merge conflict + if: failure() + run: | + set -euo pipefail + echo "::error::Failed to merge master into mingw. Manual intervention required." + exit 1 \ No newline at end of file diff --git a/ENVIRONMENT.md b/ENVIRONMENT.md new file mode 100644 index 0000000000..30f928738c --- /dev/null +++ b/ENVIRONMENT.md @@ -0,0 +1,298 @@ +# Environment Setup for nvme-cli + +This guide covers how to set up your development environment for building nvme-cli and libnvme on Linux and Windows platforms. + +## Dependencies Overview + +### Core Build Tools + +**Both Platforms:** +- Meson >= 0.62.0 (build system) +- Ninja (build backend) or Samurai (minimal alternative) +- C compiler (GCC or Clang on Linux, MinGW with GCC on Windows) + +### Platform-Specific Dependencies + +#### Linux Dependencies + +**Required:** +- gcc or clang +- meson +- ninja-build or samurai + +**Optional (recommended):** +- json-c >= 0.13 (for JSON output and plugins) +- OpenSSL >= 3.0.0 (for security features) +- libkeyutils (for key management) +- liburing >= 2.2 (for io_uring support) +- dbus-1 (for MCTP dbus scan support) + +**Documentation (optional):** +- asciidoc or asciidoctor +- xmlto + +**Testing (optional):** +- Python 3 with pytest (for tests) +- SWIG (for Python bindings) + +#### Windows Dependencies + +**Required:** +- MinGW-w64 +- Meson +- Ninja +- Python 3.8+ + +**Optional:** +- OpenSSL >= 3.0.0 (for security features) +- Perl (for documentation generation scripts) + +**Note:** On Windows, Linux-specific dependencies (json-c, liburing, keyutils, libdbus) are automatically disabled. + +## Linux Environment Setup + +### Ubuntu/Debian + +```bash +# Install core build tools +sudo apt update +sudo apt install -y build-essential meson ninja-build pkg-config + +# Install optional dependencies +sudo apt install -y \ + libjson-c-dev \ + libssl-dev \ + libkeyutils-dev \ + libdbus-1-dev + +# For io_uring support (optional) +sudo apt install -y liburing-dev + +# For documentation generation (optional) +sudo apt install -y asciidoc xmlto + +# For Python bindings (optional) +sudo apt install -y python3-dev swig +``` + +### Fedora/RHEL/CentOS + +```bash +# Install core build tools +sudo dnf install -y gcc meson ninja-build pkgconfig + +# Install optional dependencies +sudo dnf install -y \ + json-c-devel \ + openssl-devel \ + keyutils-libs-devel \ + dbus-devel + +# For io_uring support (optional) +sudo dnf install -y liburing-devel + +# For documentation generation (optional) +sudo dnf install -y asciidoc xmlto + +# For Python bindings (optional) +sudo dnf install -y python3-devel swig +``` + +### Arch Linux + +```bash +# Install core build tools +sudo pacman -S base-devel meson ninja pkg-config + +# Install optional dependencies +sudo pacman -S json-c openssl keyutils dbus + +# For io_uring support (optional) +sudo pacman -S liburing + +# For documentation generation (optional) +sudo pacman -S asciidoc xmlto + +# For Python bindings (optional) +sudo pacman -S python swig +``` + +## Windows Environment Setup + +### Using WinGet (Windows 10/11) + +WinGet provides the easiest way to set up the build environment on Windows: + +#### Installation + +```powershell +# Install MinGW-w64 (GCC compiler toolchain) +winget install BrechtSanders.WinLibs.POSIX.UCRT + +# Install Python (required for meson) +winget install Python.Python.3.12 + +# Install Perl (required for documentation generation scripts) +winget install StrawberryPerl.StrawberryPerl + +# Install Meson build system (after Python is installed) +pip install meson + +# Install OpenSSL (optional, for security features) +winget install ShiningLight.OpenSSL +``` + +#### Setting Up PATH + +After installation, you need to add the tools to your PATH: + +**Temporary PATH setup (for current PowerShell session):** + +```powershell +# Set PATH to include MinGW, Python, and Meson +$env:PATH = "C:\Users\$env:USERNAME\AppData\Local\Microsoft\WinGet\Packages\BrechtSanders.WinLibs.POSIX.UCRT_Microsoft.Winget.Source_8wekyb3d8bbwe\mingw64\bin;C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python312\Scripts;C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python312;$env:PATH" +``` + +**Note:** The exact path for MinGW may vary depending on your WinGet installation. Use this command to locate it: +```powershell +Get-ChildItem -Path "$env:LOCALAPPDATA\Microsoft\WinGet\Packages" -Filter "BrechtSanders.WinLibs*" -Directory +``` + +**Permanent PATH setup:** + +To avoid setting PATH every time: + +1. Open System Properties: + - Press `Win + X` → System → Advanced system settings + - Or search for "Environment Variables" in Start Menu + +2. Click "Environment Variables" + +3. Under "User variables", select "Path" and click "Edit" + +4. Click "New" and add these paths (adjust the username and exact paths as needed): + - `C:\Users\YourUsername\AppData\Local\Microsoft\WinGet\Packages\BrechtSanders.WinLibs.POSIX.UCRT_Microsoft.Winget.Source_8wekyb3d8bbwe\mingw64\bin` + - `C:\Users\YourUsername\AppData\Local\Programs\Python\Python312` + - `C:\Users\YourUsername\AppData\Local\Programs\Python\Python312\Scripts` + +5. Click OK on all dialogs + +6. Restart PowerShell or any open terminals + +#### Verification + +Verify the installation by checking versions: + +```powershell +gcc --version +python --version +meson --version +ninja --version +``` + +Expected output should show: +- GCC 15.x or later +- Python 3.12.x or later +- Meson 1.x or later +- Ninja 1.x or later + +## Troubleshooting Environment Setup + +### Windows: Finding Installation Paths + +If you need to locate where WinGet installed tools: + +**Find Python:** +```powershell +Get-ChildItem -Path "$env:LOCALAPPDATA\Programs\Python" -Recurse -Filter "python.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName +``` + +**Find Meson:** +```powershell +Get-ChildItem -Path "$env:LOCALAPPDATA" -Recurse -Filter "meson.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName +``` + +**Find GCC (MinGW):** +```powershell +Get-ChildItem -Path "$env:LOCALAPPDATA" -Recurse -Filter "gcc.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName +``` + +### Windows: Command Not Found Errors + +**"meson: command not found"** + +Solution: Add Python Scripts directory to PATH: +```powershell +$env:PATH = "C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python312\Scripts;$env:PATH" +``` + +**"gcc: command not found"** + +Solution: Add MinGW bin directory to PATH: +```powershell +$env:PATH = "C:\Users\$env:USERNAME\AppData\Local\Microsoft\WinGet\Packages\BrechtSanders.WinLibs.POSIX.UCRT_Microsoft.Winget.Source_8wekyb3d8bbwe\mingw64\bin;$env:PATH" +``` + +**"python: command not found"** + +Solution: Add Python to PATH: +```powershell +$env:PATH = "C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python312;$env:PATH" +``` + +### Linux: Missing Dependencies + +**json-c not found:** +```bash +# Ubuntu/Debian +sudo apt install libjson-c-dev + +# Fedora/RHEL +sudo dnf install json-c-devel + +# Arch +sudo pacman -S json-c +``` + +**OpenSSL not found:** +```bash +# Ubuntu/Debian +sudo apt install libssl-dev + +# Fedora/RHEL +sudo dnf install openssl-devel + +# Arch +sudo pacman -S openssl +``` + +**Meson too old:** +```bash +# Install newer version via pip +pip3 install --user meson + +# Add to PATH if needed +export PATH="$HOME/.local/bin:$PATH" +``` + +### Verifying Your Environment + +After setup, verify all required tools are available: + +**Linux:** +```bash +gcc --version +meson --version +ninja --version +pkg-config --version +``` + +**Windows:** +```powershell +gcc --version +python --version +meson --version +ninja --version +``` + +If any command fails, review the PATH setup instructions above. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000000..2a125a1886 --- /dev/null +++ b/TODO.md @@ -0,0 +1,557 @@ +# Windows Build TODO - Excluded Features and Plugins + +This document tracks features and plugins that are currently excluded from the Windows build of nvme-cli. + +## Status Legend +- ❌ **Will likely never need Windows support** - Requires Linux-specific kernel/networking features +- ⚠️ **Low priority** - Limited use case on Windows +- ✅ **Should eventually be supported** - Would be useful on Windows with porting effort + +--- + +## Core Features Excluded + +### ❌ NVMe over Fabrics (fabrics.c) +**Status:** Will likely never need Windows support +**Reason:** Requires Linux kernel NVMe-oF implementation, networking stack, and sysfs + +**Commands affected:** +- `nvme discover` - Discover NVMeoF subsystems +- `nvme connect` - Connect to NVMeoF subsystem +- `nvme connect-all` - Connect to all discovered subsystems +- `nvme disconnect` - Disconnect from NVMeoF subsystem +- `nvme disconnect-all` - Disconnect from all subsystems +- `nvme config` - Configuration of NVMeoF subsystems +- `nvme dim` - Discovery Information Management + +**Current implementation:** Stub functions that print "not supported on Windows" + +--- + +### ⚠️ RPMB (nvme-rpmb.c) +**Status:** Low priority +**Reason:** Replay Protection Memory Block uses Linux kernel crypto API + +**Commands affected:** +- `nvme rpmb` - RPMB operations + +**Porting effort:** Would require Windows crypto API port +**Current implementation:** Stub function that prints "not supported on Windows" + +--- + +## Vendor Plugins Excluded + +### ✅ Huawei (plugins/huawei/huawei-nvme.c) +**Status:** Should eventually be supported +**Reason:** Uses `scandir()`, `mkdir()` with mode parameter + +**Porting requirements:** +- Replace `scandir()` with Windows equivalent (FindFirstFile/FindNextFile) +- Handle `mkdir()` mode parameter (already have wrapper in win-compat.h) + +**Use case:** Useful for Huawei NVMe device users on Windows + +--- + +### ✅ IBM (plugins/ibm/ibm-nvme.c) +**Status:** Should eventually be supported +**Reason:** Uses `localtime_r()`, `mkdir()` with mode parameter + +**Porting requirements:** +- Replace `localtime_r()` with `localtime_s()` (Windows equivalent) +- Handle `mkdir()` mode parameter + +**Use case:** Useful for IBM NVMe device users on Windows + +--- + +### ✅ NetApp (plugins/netapp/netapp-nvme.c) +**Status:** Should eventually be supported +**Reason:** Uses `mkdir()` with mode parameter + +**Porting requirements:** +- Handle `mkdir()` mode parameter (already have wrapper) +- Verify no other Linux dependencies + +**Use case:** Useful for NetApp NVMe device users on Windows + +--- + +### ✅ SanDisk (plugins/sandisk/) +**Status:** Should eventually be supported +**Reason:** Uses `scandir()`, `mkdir()` with mode parameter + +**Files:** +- `sandisk-nvme.c` +- `sandisk-utils.c` + +**Porting requirements:** +- Replace `scandir()` with Windows directory enumeration +- Handle `mkdir()` mode parameter + +**Use case:** Popular consumer NVMe devices, high value for Windows users + +--- + +### ⚠️ ScaleFlux (plugins/scaleflux/sfx-nvme.c) +**Status:** Low priority +**Reason:** Uses `linux/fs.h` for filesystem ioctls + +**Porting requirements:** +- Replace Linux filesystem ioctls with Windows equivalents +- May require significant rework + +**Use case:** Enterprise storage, less common on Windows workstations + +--- + +### ✅ WDC/Western Digital (plugins/wdc/) +**Status:** Should eventually be supported +**Reason:** Uses `scandir()`, `mkdir()` with mode parameter, `linux/fs.h` + +**Files:** +- `wdc-nvme.c` +- `wdc-utils.c` + +**Porting requirements:** +- Replace `scandir()` with Windows directory enumeration +- Handle Linux filesystem ioctls (BLKGETSIZE, etc.) +- Replace `mkdir()` mode handling + +**Use case:** Popular consumer and enterprise NVMe devices, **high priority** for Windows + +--- + +### ⚠️ YMTC (plugins/ymtc/ymtc-nvme.c) +**Status:** Low priority +**Reason:** Uses `mkdir()` with mode parameter + +**Porting requirements:** +- Handle `mkdir()` mode parameter (already have wrapper) + +**Use case:** Chinese market primarily, lower Windows user base + +--- + +### ✅ ZNS - Zoned Namespaces (plugins/zns/zns.c) +**Status:** Should eventually be supported +**Reason:** Uses `linux/fs.h` for block device operations + +**Porting requirements:** +- Replace Linux block device ioctls with Windows equivalents +- May require Windows NVMe driver support for ZNS + +**Use case:** Zoned storage is emerging technology, useful for development/testing on Windows + +--- + +### ❌ LM - Lossless Logger (plugins/lm/) +**Status:** Will likely never need Windows support +**Reason:** Subdirectory plugin with heavy Linux dependencies + +**Porting requirements:** +- Unknown - would need investigation +- Likely uses sysfs, Linux-specific logging + +**Use case:** Appears to be Linux kernel/driver development focused + +--- + +## Currently Enabled Plugins (19 total) + +These plugins work on Windows: +- ✅ amzn - Amazon vendor specific +- ✅ dapustor - DapuStor vendor specific +- ✅ dell - Dell vendor specific +- ✅ dera - Dera vendor specific +- ✅ fdp - Flexible Data Placement +- ✅ innogrit - Innogrit vendor specific +- ✅ inspur - Inspur vendor specific +- ✅ intel - Intel vendor specific +- ✅ mangoboost - MangoBoost vendor specific +- ✅ memblaze - Memblaze vendor specific +- ✅ **micron - Micron vendor specific** (enabled with mkdir wrapper) +- ✅ nbft - ACPI NBFT table extensions +- ✅ nvidia - NVIDIA vendor specific +- ✅ ocp - OCP cloud SSD extensions +- ✅ feat - NVMe feature extensions +- ✅ seagate - Seagate vendor specific +- ✅ shannon - Shannon vendor specific +- ✅ ssstc - SSSTC vendor specific +- ✅ toshiba - Toshiba vendor specific +- ✅ transcend - Transcend vendor specific +- ✅ virtium - Virtium vendor specific + +--- + +## Porting Priority Recommendations + +### High Priority (Popular consumer devices) +1. **WDC/Western Digital** - Very common consumer NVMe SSDs +2. **SanDisk** - Popular consumer NVMe SSDs (owned by WD) +3. **ZNS** - Emerging standard, useful for development + +### Medium Priority +4. **IBM** - Enterprise users +5. **Huawei** - Large vendor +6. **NetApp** - Enterprise storage + +### Low Priority +7. **YMTC** - Regional market focus +8. **ScaleFlux** - Niche enterprise +9. **LM** - Development/debug tool + +### Not Recommended +- **NVMe-oF (fabrics)** - Requires Linux kernel support +- **RPMB** - Requires crypto API port, limited use case + +--- + +## Implementation Notes + +### Completed Compatibility Additions +- ✅ `win-compat.h/c` - Windows compatibility layer +- ✅ `mkdir(path, mode)` macro - Redirects to `_mkdir(path)` +- ✅ `getline()`, `strsep()`, `reallocarray()` implementations +- ✅ `gmtime_r()` wrapper using `gmtime_s()` +- ✅ `dirent.h` emulation (opendir, readdir, closedir) +- ✅ mmap/munmap stubs +- ✅ Signal handling compatibility (sigaction, sigemptyset) + +### Still Needed for Full Plugin Support +- ⚠️ `scandir()` implementation for Windows +- ⚠️ `localtime_r()` wrapper using `localtime_s()` +- ⚠️ Linux filesystem ioctl replacements (BLKGETSIZE, etc.) +- ⚠️ Block device size detection on Windows + +--- + +## How to Enable a Plugin + +1. Check the plugin source for Linux-specific dependencies: + ```bash + grep -E "scandir|localtime_r|linux/fs.h|sys/sysinfo.h" plugins//*.c + ``` + +2. Add necessary compatibility wrappers to `win-compat.h` + +3. Move plugin from Linux-only section to cross-platform section in `plugins/meson.build` + +4. Build and test: + ```bash + meson compile -C .build + ``` + +--- + +## Testing on Windows + +Currently, testing is limited without physical NVMe devices. The Windows build: +- ✅ Compiles successfully +- ✅ Shows help and lists commands +- ⚠️ Cannot test actual device operations without Windows NVMe devices +- ⚠️ Device access requires Windows NVMe driver and proper permissions + +--- + +Last updated: January 23, 2026 + +--- + +## Potentially Implementable Stub Functions + +This section lists stub functions in `libnvme/src/nvme/windows-stubs.c` that could be implemented with reasonable effort **if device I/O is implemented**. These are separate from the Critical Missing Functionality below, which blocks all device operations. + +### ✅ Easy - Simple Data Structures (30 minutes effort) + +**`nvme_create_global_ctx()`** - Global context allocation +- Current: Returns NULL +- Needed: Allocate `nvme_global_ctx` structure, initialize log level, return pointer +- Use case: Required by many commands for configuration/logging +- Implementation: ~50 lines, mostly struct initialization +- Dependencies: None + +**`nvme_set_dry_run()`** - Dry run mode flag +- Current: No-op stub +- Needed: Set flag in global context: `ctx->dry_run = enable;` +- Use case: Testing commands without actual hardware changes +- Implementation: 1 line +- Dependencies: Requires `nvme_create_global_ctx()` first + +**`nvme_set_etdas()` / `nvme_clear_etdas()`** - Telemetry flags +- Current: Return -ENOTSUP +- Needed: Set/clear flags in global context +- Use case: Telemetry data collection control +- Implementation: 2-3 lines each +- Dependencies: Requires `nvme_create_global_ctx()` first + +### ⚠️ Medium - Requires Windows APIs (4-8 hours effort) + +**`nvmf_hostnqn_generate()`** - Host NQN generation +- Current: Returns NULL +- Needed: Generate UUID-based host NQN using Windows `UuidCreate()` API +- Use case: Host identification for some NVMe commands (may be needed even without fabrics) +- Implementation: ~30 lines + ```c + // Use Windows RPC API + UUID uuid; + UuidCreate(&uuid); + UuidToStringA(&uuid, &uuid_str); + asprintf(&hostnqn, "nqn.2014-08.org.nvmexpress:uuid:%s", uuid_str); + RpcStringFreeA(&uuid_str); + return hostnqn; + ``` +- Dependencies: Link against `rpcrt4.lib` +- Note: Linux version tries DMI/device-tree first, Windows can just use random UUID + +**`nvmf_hostnqn_generate_from_hostid()`** - Format host NQN from UUID +- Current: Returns NULL +- Needed: Format provided UUID into NQN string +- Use case: Creates standardized host identifier +- Implementation: ~10 lines (just string formatting) +- Dependencies: None (pure string manipulation) + +### ❌ NOT Implementable - Require Linux Kernel Features + +**Fabrics Operations** - All require Linux NVMe-oF kernel driver: +- `nvmf_add_ctrl()`, `nvmf_get_discovery_log()`, `nvmf_connect_*()`, etc. +- These will NEVER work on Windows without kernel NVMe-oF support + +**Fabrics String Converters** - Pointless without fabrics: +- `nvmf_trtype_str()`, `nvmf_adrfam_str()`, `nvmf_sectype_str()`, etc. +- These only format fabrics discovery/connection information +- No fabrics = no use for these functions + +**Tree/Topology Functions** - Require Linux sysfs: +- `nvme_scan_topology()`, `nvme_create_root()`, `nvme_free_tree()` +- Windows has no sysfs equivalent +- Path properties (NUMA, queue depth) also depend on sysfs + +**Keyring/TLS Functions** - Require Linux keyctl: +- `nvme_read_key()`, `nvme_lookup_keyring()`, `nvme_insert_tls_key_*()`, etc. +- Windows Credential Manager is completely different architecture + +**NBFT Functions** - Fabrics boot table (fabrics-specific): +- `nvmf_nbft_read_files()`, `nvmf_nbft_free()` +- Only used for NVMe-oF boot, not applicable to Windows + +### Summary + +**Worth implementing (if device I/O works):** +- ✅ `nvme_create_global_ctx()` - Essential for most commands +- ✅ `nvme_set_dry_run()` - Testing support +- ✅ `nvme_set_etdas()` / `nvme_clear_etdas()` - Telemetry control +- ⚠️ `nvmf_hostnqn_generate*()` - May be needed for host identification (2 functions) + +**Total effort:** ~8-10 hours for all of the above + +**Not worth implementing:** +- ❌ 9 fabrics string converters - Useless without fabrics support +- ❌ 40+ fabrics operations - Require Linux kernel +- ❌ 30+ tree/topology functions - Require sysfs +- ❌ 20+ keyring/TLS functions - Require Linux keyctl +- ❌ NBFT functions - Fabrics boot only + +**Critical Note:** These stubs are low priority. The entire list above is meaningless until the Critical Missing Functionality (device I/O layer) is implemented. Focus should be on Windows IOCTL implementation first. + +--- + +## Critical Missing Functionality for Windows + +The current Windows build successfully compiles and shows help output, but **cannot actually communicate with NVMe devices**. All device I/O operations are stubbed. The following core functionality must be implemented for a minimally functional tool: + +### 🔴 CRITICAL: Device I/O Layer (libnvme) + +**Status:** Currently all stubbed - zero device functionality +**Location:** `libnvme/src/nvme/ioctl.c` and Windows platform layer +**Priority:** **HIGHEST - MUST IMPLEMENT** + +#### What's Missing: + +1. **Windows NVMe IOCTL Implementation** + - Current: `ioctl()` function in `platform/windows.h` returns `ENOSYS` + - Needed: Implement Windows storage device IOCTLs using: + - `IOCTL_STORAGE_QUERY_PROPERTY` + - `IOCTL_STORAGE_PROTOCOL_COMMAND` (Windows 10+) + - `DeviceIoControl()` Win32 API + - Reference: Windows NVMe driver documentation + - Impact: **ALL nvme commands fail without this** + +2. **Device Path Translation** + - Current: Code expects Linux paths like `/dev/nvme0`, `/dev/nvme0n1` + - Needed: Windows device paths: + - Physical drives: `\\.\PhysicalDrive0`, `\\.\PhysicalDrive1` + - SCSI devices: `\\.\SCSI#Disk&Ven_...` + - NVMe devices: May need Windows NVMe miniport interfaces + - Must implement path translation in device open routines + - Impact: **Cannot open any devices** + +3. **Device Enumeration (`nvme list`)** + - Current: `nvme_scan_topology()` stubbed to return error + - Needed: Windows device enumeration using: + - `SetupAPI` (`SetupDiGetClassDevs`, `SetupDiEnumDeviceInterfaces`) + - `CFGMGR32` (Configuration Manager API) + - Query for NVMe device class GUID + - Enumerate physical drives and identify NVMe devices + - Location: `libnvme/src/nvme/tree.c` (excluded from Windows build) + - Impact: **`nvme list` command completely non-functional** + +4. **Global Context (`nvme_create_global_ctx()`)** + - Current: Stubbed to return NULL in `windows-stubs.c` + - Needed: Minimal implementation for Windows + - Log level management + - Device handle tracking + - Configuration state + - Impact: **Most commands fail to initialize** + +5. **Device Handle Management** + - Current: `nvme_open()`, `nvme_close()` stubbed + - Needed: + - Use `CreateFile()` with proper flags for NVMe device access + - Handle exclusive access (`O_EXCL` → `FILE_SHARE_*` flags) + - Manage file descriptors vs Windows `HANDLE` + - Impact: **Cannot open/close devices** + +6. **Admin and I/O Command Passthrough** + - Current: All passthrough stubbed + - Needed: Windows IOCTL wrappers for: + - `nvme_submit_admin_passthru()` + - `nvme_submit_io_passthru()` + - Convert Linux NVMe commands to Windows NVMe protocol commands + - Reference: `STORAGE_PROTOCOL_COMMAND` structure + - Impact: **All NVMe commands (identify, get-log, etc.) fail** + +#### Implementation Approach: + +**Phase 1 - Basic Device Access (Minimal Functionality):** +1. Implement `nvme_open()` using `CreateFile()` for physical drives +2. Implement basic `ioctl()` wrapper for `IOCTL_STORAGE_PROTOCOL_COMMAND` +3. Implement admin passthrough for Identify Controller command +4. Test: `nvme id-ctrl \\.\PhysicalDrive0` should work + +**Phase 2 - Device Enumeration:** +5. Implement basic `nvme_scan_topology()` using SetupAPI +6. Enumerate NVMe devices and populate device list +7. Test: `nvme list` should show NVMe devices + +**Phase 3 - Full Command Support:** +8. Implement remaining admin commands (get-log, get-feature, etc.) +9. Implement I/O passthrough for data transfer commands +10. Test vendor-specific commands with supported devices + +#### Files Requiring Implementation: + +- `libnvme/src/platform/windows.h` - Replace `ioctl()` stub +- `libnvme/src/nvme/windows-stubs.c` - Implement device functions +- New file: `libnvme/src/nvme/windows-ioctl.c` (recommended) +- New file: `libnvme/src/nvme/windows-enum.c` (recommended) + +#### Testing Requirements: + +- **Administrator privileges required** - Windows restricts direct device access +- Need physical NVMe device on Windows test machine +- Cannot test in VM without NVMe passthrough +- Consider using Windows Driver Kit (WDK) samples as reference + +--- + +### 🟡 IMPORTANT: Block Device Operations + +**Status:** Partially stubbed +**Priority:** **HIGH** + +Several commands need block device size and properties: +- `nvme format` - needs device size +- `nvme write`, `nvme read` - need block size +- Various log pages reference block counts + +**Missing APIs:** +- Block device size detection (IOCTL_DISK_GET_LENGTH_INFO) +- Sector size queries (IOCTL_STORAGE_QUERY_PROPERTY) +- `fstat()` for device files - Windows equivalent needed +- `S_ISCHR()`, `S_ISBLK()` macros - need Windows device type checking + +**Current Workarounds:** +- Some operations may work without size info (admin commands) +- I/O commands will need proper implementation + +--- + +### 🟡 IMPORTANT: Permissions and Security + +**Status:** Not addressed +**Priority:** **HIGH** + +**Requirements:** +1. **Administrator/Elevated Privileges** + - Direct device access requires admin rights on Windows + - Should detect and warn if not elevated + - Add manifest to request elevation? + +2. **Device Access Rights** + - Windows restricts `FILE_SHARE_*` flags + - Exclusive access may conflict with system drivers + - May need to coordinate with Windows NVMe driver + +3. **Error Handling** + - Windows error codes differ from errno + - `GetLastError()` → translate to errno equivalents + - Proper error messages for permission denied scenarios + +**Implementation:** +- Add `IsUserAnAdmin()` check on startup +- Display warning if not elevated +- Consider UAC elevation prompt + +--- + +### 🟢 NICE TO HAVE: Additional Improvements + +**Priority:** **LOW** + +1. **Native Windows Paths** + - Accept both Linux-style and Windows-style paths + - `/dev/nvme0` → `\\.\PhysicalDrive0` auto-translation + - Device aliases/friendly names + +2. **Windows-Specific Documentation** + - Update man pages for Windows + - Document device path format + - Document privilege requirements + - Installation instructions for Windows + +3. **Windows Installer** + - MSI or MSIX package + - Include libnvme DLL + - Add to PATH automatically + - Desktop shortcuts + +4. **Error Message Improvements** + - Windows-specific error messages + - Link to troubleshooting docs + - Better privilege requirement messages + +--- + +## Summary of Required Work + +| Component | Priority | Effort | Blocker | +|-----------|----------|--------|---------| +| Device IOCTL implementation | 🔴 Critical | High | **YES** | +| Device path translation | 🔴 Critical | Medium | **YES** | +| Device enumeration | 🔴 Critical | High | **YES** | +| Admin command passthrough | 🔴 Critical | High | **YES** | +| Block device operations | 🟡 Important | Medium | Partial | +| Permission handling | 🟡 Important | Low | No | +| Documentation | 🟢 Nice to have | Medium | No | + +**Bottom Line:** The Windows build is currently a "shell" that compiles but has **zero runtime device functionality**. All the critical I/O operations are stubbed. Implementing Windows NVMe device access is a **significant porting effort** requiring: +- Windows Driver Kit knowledge +- Understanding of Windows storage stack +- Testing hardware with NVMe devices +- ~2-4 weeks for experienced Windows driver developer + +--- + +Last updated: January 23, 2026 diff --git a/WINDOWS-PORT-README.md b/WINDOWS-PORT-README.md new file mode 100644 index 0000000000..bfb5f3dcf1 --- /dev/null +++ b/WINDOWS-PORT-README.md @@ -0,0 +1,43 @@ +# Windows Port README + +This document outlines some of the design choices and strategies used during the development of Windows support. + +## Windows support strategy using MinGW + +The mingw-w64 libraries and tools were chosen for the design to provide a more straightforward port to Windows, taking advantage of its headers and libraries and the GNU toolchain, including the gcc compiler. Benefits from this approach include faster turnaround, toolchain compatibility, and fewer code changes, which will help the code be more maintainable. + +Installation and configuration of mingw and other tools such as meson, python, perl, and OpenSSL is described in [ENVIRONMENT.md](ENVIRONMENT.md). Meson build configuration is the same on Windows as on Linux. + +## Windows build configuration support + +The Meson build configuration files have been updated to detect the platform, and configure the build accordingly. Configuration options remain unchanged. On Windows builds, some features are flagged as not supported, and some files are not yet included in the build. + +## Platform-specific includes + +To reduce preprocessor checks for `_WIN32` throughout the codebase, common platform-specific includes, type definitions, and utility functions are centralized in `platform/linux.h` and `platform/windows.h` under libnvme. Code can include `platform/include.h`, which includes the appropriate platform header based on the target platform. + +Some methods, macros, and types used throughout the code lack direct Windows equivalents. Windows-specific implementations and definitions have been added to `platform/windows.h` and `platform/windows.c`. + +For types defined by `linux/types.h` on Linux, a new `platform/types.h` was created to define equivalent types for Windows. On Linux, `platform/types.h` simply includes `linux/types.h`. All project includes of `linux/types.h` have been replaced with `platform/types.h`. + +## Stubs for currently unsupported methods + +To allow compilation while some features remain unsupported on Windows, `windows-stubs.c` files contain stubbed implementations of unsupported methods. As support is added, these stubs are removed. These stub files are temporary and will be phased out as development progresses. + +The Meson build configurations determine whether to build using the windows-stub.c files or the standard implementation files based on the target platform. + +## Platform-specific method implementation + +Many existing implementations work for both Linux and Windows. When platform-specific implementations are needed, we follow this pattern: + +- **Header files (.h)**: Method declarations remain unchanged and platform-agnostic. +- **Windows implementations (filename-windows.c)**: Platform-specific implementations for Windows are placed in files following the naming convention `filename-windows.c` (for example, `ioctl-windows.c`). +- **Platform-agnostic implementations**: Code that works on both Linux and Windows remains in the existing `.c` file. +- **Linux-specific implementations**: Linux-only code remains in the existing `.c` file within `#ifndef _WIN32` guards. Eventually, we plan to extract these into separate `filename-linux.c` files. For now, keeping them inline simplifies upstream merging and ensures we don't miss method updates from the main project. +- **Build configuration**: The Meson build configuration determines which implementation files to include in the build based on the target platform. + +Some Windows methods behave differently than their Linux counterparts and require project-specific implementations. Examples include `fstat` and `free`. We created wrapper methods such as `nvme_fstat` and `nvme_free` with platform-specific implementations to provide platform-agnostic interfaces throughout the codebase. + +### The linux.h / linux.c exception + +The files `nvme/linux.h` and `nvme/linux.c` present a problem for the pattern described above. Although the file names suggests that the methods they implement are Linux-specific, they are actually utility methods that are needed for both Linux and Windows implementations, and many of the existing implementations are compitible with Windows using MinGW. For now, a new file named `nvme/windows.c` contains the Windows-specific implementations of methods declared in `nvme/linux.h`, breaking with the pattern. Re-evaluation of the linux.h / linux.c names is needed. \ No newline at end of file diff --git a/ccan/ccan/minmax/minmax.h b/ccan/ccan/minmax/minmax.h index f6abd796f5..33c96eaffd 100644 --- a/ccan/ccan/minmax/minmax.h +++ b/ccan/ccan/minmax/minmax.h @@ -21,6 +21,10 @@ do { } while (0) #endif +/* Undefine any existing min/max macros. */ +#undef min +#undef max + #define min(a, b) \ ({ \ typeof(a) _a = (a); \ diff --git a/common.h b/common.h index 9e3a367bc3..e798ffc7d6 100644 --- a/common.h +++ b/common.h @@ -9,8 +9,13 @@ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +/* On Windows, min/max may already be defined */ +#ifndef min #define min(x, y) ((x) > (y) ? (y) : (x)) +#endif +#ifndef max #define max(x, y) ((x) > (y) ? (x) : (y)) +#endif #ifdef __packed #else /* __packed */ diff --git a/libnvme/examples/meson.build b/libnvme/examples/meson.build index 40da11edbb..0cfd930b57 100644 --- a/libnvme/examples/meson.build +++ b/libnvme/examples/meson.build @@ -5,6 +5,10 @@ # # Authors: Martin Belanger # + +# Skip examples on Windows for now. +if host_system != 'windows' + executable( 'telemetry-listen', ['telemetry-listen.c'], @@ -92,3 +96,5 @@ if want_mi ) endif endif + +endif # host_system != 'windows' diff --git a/libnvme/src/libnvme.ld b/libnvme/src/libnvme.ld index d3ddc77d1e..6543b4b165 100644 --- a/libnvme/src/libnvme.ld +++ b/libnvme/src/libnvme.ld @@ -189,6 +189,7 @@ LIBNVME_3 { libnvme_transport_handle_set_submit_entry; libnvme_transport_handle_set_submit_exit; libnvme_unlink_ctrl; + libnvme_update_block_size; libnvme_update_key; libnvme_uuid_from_string; libnvme_uuid_to_string; diff --git a/libnvme/src/meson.build b/libnvme/src/meson.build index 3bc24a3dd1..1c531e9c26 100644 --- a/libnvme/src/meson.build +++ b/libnvme/src/meson.build @@ -5,37 +5,49 @@ # # Authors: Martin Belanger # -sources = [] +sources = [ + 'nvme/accessors.c', + 'nvme/base64.c', + 'nvme/crc32.c', + 'nvme/ioctl.c', + 'nvme/lib.c', + 'nvme/log.c', + 'nvme/nvme-cmds.c', + 'nvme/tree.c', + 'nvme/util.c', +] if host_system == 'windows' - sources += [] + sources += [ + 'nvme/ioctl-windows.c', # Windows stubs for Linux-specific ioctl functions + 'nvme/lib-windows.c', # Windows implementations of lib.h functions + 'nvme/windows-stubs.c', # Stubs for excluded functions + ] else sources += [ - 'nvme/accessors.c', - 'nvme/base64.c', - 'nvme/crc32.c', 'nvme/filters.c', - 'nvme/ioctl.c', - 'nvme/lib.c', 'nvme/linux.c', - 'nvme/log.c', - 'nvme/nvme-cmds.c', 'nvme/sysfs.c', - 'nvme/tree.c', - 'nvme/util.c', ] endif headers = [ 'nvme/accessors.h', 'nvme/endian.h', + 'nvme/fcntl.h', 'nvme/filters.h', 'nvme/ioctl.h', 'nvme/lib-types.h', 'nvme/lib.h', 'nvme/linux.h', + 'nvme/malloc.h', + 'nvme/mkdir.h', 'nvme/nvme-cmds.h', 'nvme/nvme-types.h', + 'nvme/signal.h', + 'nvme/stdio.h', + 'nvme/stdlib.h', 'nvme/tree.h', 'nvme/types.h', + 'nvme/unistd.h', 'nvme/util.h', ] @@ -95,7 +107,9 @@ deps = [ ] if host_system == 'windows' deps += [ - kernel32_dep + ws2_32_dep, + kernel32_dep, + bcrypt_dep, ] endif @@ -184,4 +198,3 @@ if want_mi install_mode: mode, ) endif - diff --git a/libnvme/src/nvme/cleanup.h b/libnvme/src/nvme/cleanup.h index 5c7c550e24..ac512c55c3 100644 --- a/libnvme/src/nvme/cleanup.h +++ b/libnvme/src/nvme/cleanup.h @@ -22,4 +22,10 @@ static inline void freep(void *p) } #define __cleanup_free __cleanup(freep) +static inline void nvme_freep(void *p) +{ + aligned_free(*(void **)p); +} +#define __cleanup_nvme_free __cleanup(nvme_freep) + #endif diff --git a/libnvme/src/nvme/fcntl.h b/libnvme/src/nvme/fcntl.h new file mode 100644 index 0000000000..78c986c0a3 --- /dev/null +++ b/libnvme/src/nvme/fcntl.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libnvme. + * Copyright (c) 2026 Micron Technology, Inc. + * + * Cross-platform compatibility for fcntl.h. + * + * Authors: Broc Going + */ +#pragma once + +#include + +/* + * O_BINARY is required for Windows to avoid line ending translations. + * Define it as 0 on platforms where it is not defined so that it can be used + * but will have no effect. + */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif diff --git a/libnvme/src/nvme/ioctl-windows.c b/libnvme/src/nvme/ioctl-windows.c new file mode 100644 index 0000000000..cdc90e3211 --- /dev/null +++ b/libnvme/src/nvme/ioctl-windows.c @@ -0,0 +1,1606 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2025 Micron Technology, Inc. + * + * Authors: Broc Going + * + * Windows-specific implementations of ioctl-based functions. + */ + +#include "ioctl.h" +#include "private.h" +#include "types.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "compiler-attributes.h" + +/* Definitions not yet included in mingw's winerror.h */ +#define STG_E_FIRMWARE_SLOT_INVALID _HRESULT_TYPEDEF_(0x80030208L) +#define STG_E_FIRMWARE_IMAGE_INVALID _HRESULT_TYPEDEF_(0x80030209L) + +static int get_last_error_as_errno(void) +{ + DWORD error = GetLastError(); + + /* Convert Windows error to errno */ + switch (error) { + case ERROR_INVALID_PARAMETER: + return -EINVAL; + case ERROR_CALL_NOT_IMPLEMENTED: + case ERROR_INVALID_FUNCTION: + case ERROR_NOT_SUPPORTED: + return -ENOTSUP; + case ERROR_INSUFFICIENT_BUFFER: + case ERROR_NO_SYSTEM_RESOURCES: + return -ENOMEM; + case ERROR_IO_DEVICE: + return -EIO; + case STG_E_FIRMWARE_IMAGE_INVALID: + return -EILSEQ; + case STG_E_FIRMWARE_SLOT_INVALID: + return -EINVAL; + default: + return -EIO; + } +} + +static int get_errno_from_storage_protocol_status(DWORD status) +{ + switch (status) { + case STORAGE_PROTOCOL_STATUS_SUCCESS: + return 0; + case STORAGE_PROTOCOL_STATUS_PENDING: + return -EAGAIN; + case STORAGE_PROTOCOL_STATUS_ERROR: + return -EIO; + case STORAGE_PROTOCOL_STATUS_INVALID_REQUEST: + return -EINVAL; + case STORAGE_PROTOCOL_STATUS_NO_DEVICE: + return -ENODEV; + case STORAGE_PROTOCOL_STATUS_BUSY: + return -EBUSY; + case STORAGE_PROTOCOL_STATUS_DATA_OVERRUN: + return -E2BIG; + case STORAGE_PROTOCOL_STATUS_INSUFFICIENT_RESOURCES: + return -ENOMEM; + case STORAGE_PROTOCOL_STATUS_THROTTLED_REQUEST: + return -EIO; + case STORAGE_PROTOCOL_STATUS_NOT_SUPPORTED: + return -ENOTSUP; + default: + return -EIO; + } +} + +static bool get_is_win_pe(void) +{ + HKEY key; + LONG rc = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Control\\MiniNT", + 0, + KEY_READ, + &key + ); + + if (rc == ERROR_SUCCESS) { + RegCloseKey(key); + return true; + } + return false; +} + +__public int libnvme_reset_subsystem(struct libnvme_transport_handle *hdl) +{ + (void)hdl; + return -ENOTSUP; +} + +__public int libnvme_reset_ctrl(struct libnvme_transport_handle *hdl) +{ + (void)hdl; + return -ENOTSUP; +} + +__public int libnvme_rescan_ns(struct libnvme_transport_handle *hdl) +{ + (void)hdl; + return 0; // NOP on Windows +} + +__public int libnvme_get_nsid(struct libnvme_transport_handle *hdl, __u32 *nsid) +{ + /* Get the SCSI LUN, which corresponds to NSID - 1. */ + SCSI_ADDRESS addr = {0}; + addr.Length = sizeof(addr); + + DWORD bytesReturned = 0; + if (!DeviceIoControl(hdl->fd, IOCTL_SCSI_GET_ADDRESS, NULL, 0, + &addr, sizeof(addr), &bytesReturned, NULL)) + return -EIO; + + *nsid = addr.Lun + 1; + return 0; +} + +__public int libnvme_update_block_size(struct libnvme_transport_handle *hdl, + int block_size) +{ + (void)hdl; + (void)block_size; + return 0; // NOP on Windows +} + +/* + * IOCTL_STORAGE_PROTOCOL_COMMAND pass-through implementation used for + * VU commands and a small subset of other admin and IO commands. + */ +static int submit_storage_protocol_command( + struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + PSTORAGE_PROTOCOL_COMMAND protocol_command = NULL; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + PUCHAR buffer = NULL; + void *user_data = NULL; + int err = 0; + bool is_read = false; + bool is_write = false; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + if (cmd->data_len > 0 && !cmd->addr) { + err = -EINVAL; + goto out; + } + + /* + * Get the Data Transfer Direction (DTD) from the opcode: + * 00b = No data transfer + * 01b = Host to Controller Transfer + * 10b = Controller to Host Transfer + * 11b = Bi-Directional Transfer + */ + is_write = cmd->opcode & 0x1; + is_read = cmd->opcode & 0x2; + + /* Bi-directional transfers not supported */ + if (is_read && is_write) { + err = -ENOTSUP; + goto out; + } + + /* Allocate buffer for STORAGE_PROTOCOL_COMMAND + NVME command + data */ + buffer_len = FIELD_OFFSET(STORAGE_PROTOCOL_COMMAND, Command) + + STORAGE_PROTOCOL_COMMAND_LENGTH_NVME + + (cmd->data_len > 0 ? cmd->data_len : 0); + + buffer = (PUCHAR)malloc(buffer_len); + if (!buffer) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(buffer, buffer_len); + + protocol_command = (PSTORAGE_PROTOCOL_COMMAND)buffer; + + protocol_command->Version = STORAGE_PROTOCOL_STRUCTURE_VERSION; + protocol_command->Length = sizeof(STORAGE_PROTOCOL_COMMAND); + protocol_command->ProtocolType = ProtocolTypeNvme; + protocol_command->Flags = STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST; + protocol_command->CommandLength = STORAGE_PROTOCOL_COMMAND_LENGTH_NVME; + protocol_command->ErrorInfoLength = 0; + protocol_command->TimeOutValue = (cmd->timeout_ms > 0) ? + ((cmd->timeout_ms + 999) / 1000) : 10; /* Round up to seconds */ + + protocol_command->CommandSpecific = + STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND; + memcpy(protocol_command->Command, cmd, + STORAGE_PROTOCOL_COMMAND_LENGTH_NVME); + + if (cmd->addr && cmd->data_len > 0 && is_read) { + protocol_command->DataFromDeviceTransferLength = cmd->data_len; + protocol_command->DataFromDeviceBufferOffset = + FIELD_OFFSET(STORAGE_PROTOCOL_COMMAND, Command) + + STORAGE_PROTOCOL_COMMAND_LENGTH_NVME; + } else if (cmd->addr && cmd->data_len > 0 && is_write) { + protocol_command->DataToDeviceTransferLength = cmd->data_len; + protocol_command->DataToDeviceBufferOffset = + FIELD_OFFSET(STORAGE_PROTOCOL_COMMAND, Command) + + STORAGE_PROTOCOL_COMMAND_LENGTH_NVME; + memcpy((PUCHAR)buffer + protocol_command->DataToDeviceBufferOffset, + (void *)(uintptr_t)cmd->addr, cmd->data_len); + } + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_STORAGE_PROTOCOL_COMMAND, + buffer, + buffer_len, + buffer, + buffer_len, + &returned_len, + NULL); + if (result && protocol_command->ReturnStatus == STORAGE_PROTOCOL_STATUS_SUCCESS) + break; + + if (!result) + err = get_last_error_as_errno(); + else + err = get_errno_from_storage_protocol_status( + protocol_command->ReturnStatus); + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + /* Copy the returned data to the user's buffer */ + if (cmd->addr && cmd->data_len > 0 && is_read) { + memcpy((void *)(uintptr_t)cmd->addr, + (PUCHAR)buffer + protocol_command->DataFromDeviceBufferOffset, + cmd->data_len); + } + + /* Copy the completion queue entry (CQE) DW0-1 into cmd->result. */ + memcpy(&cmd->result, &protocol_command->FixedProtocolReturnData, + sizeof(cmd->result)); + +out_free_buffer: + free(buffer); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +/* + * Windows-specific IO command implementations. + */ + +/* SCSI operation code definitions */ +#define SCSIOP_SYNCHRONIZE_CACHE 0x35 +#define SCSIOP_READ16 0x88 +#define SCSIOP_WRITE16 0x8A + +static int submit_io_flush(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + PSCSI_PASS_THROUGH pass_through = NULL; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + PUCHAR buffer = NULL; + void *user_data = NULL; + int err = 0; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + buffer_len = sizeof(SCSI_PASS_THROUGH); + buffer = (PUCHAR)malloc(buffer_len); + if (!buffer) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(buffer, buffer_len); + + pass_through = (PSCSI_PASS_THROUGH)buffer; + pass_through->Length = sizeof(SCSI_PASS_THROUGH); + pass_through->CdbLength = 10; + pass_through->DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; + pass_through->TimeOutValue = (cmd->timeout_ms > 0) ? + ((cmd->timeout_ms + 999) / 1000) : 30; + + pass_through->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE; + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_SCSI_PASS_THROUGH, + buffer, + buffer_len, + buffer, + buffer_len, + &returned_len, + NULL); + if (result && !pass_through->ScsiStatus) + break; + + if (!result) + err = get_last_error_as_errno(); + else + err = -EIO; + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + cmd->result = 0; + +out_free_buffer: + free(buffer); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +static __u8 nvme_prinfo_to_scsi_wrrdprotect(__u8 prinfo) +{ + /* + * From WRPROTECT/RDPROTECT -> PRINFO (PRACT + PRCHK) mapping tables + * in NVM Express: SCSI Translation Reference documentation. + */ + switch (prinfo) { + case 0b0111: + return 1; + case 0b0011: + return 2; + case 0b0000: + return 3; + case 0b0100: + return 4; + case 0b1000: /* For Write */ + case 0b1111: /* For Read */ + default: + return 0; /* Default to no check */ + } +} + +/** + * fill_scsi_rw16_cdb() - Fill the CDB for a SCSI READ(16) or WRITE(16) command. + * @cmd: NVMe command containing the CDW fields to translate + * @cdb: CDB buffer to fill + * + * Fills the CDB for a SCSI READ(16) or WRITE(16) command based on the + * supported NVMe command fields. + * + * See NVM Express: SCSI Translation Reference documentation. + */ +static void fill_scsi_rw16_cdb(struct libnvme_passthru_cmd *cmd, UCHAR cdb[16]) +{ + __u8 opcode = (cmd->opcode == nvme_cmd_read) ? + SCSIOP_READ16 : SCSIOP_WRITE16; + __u64 lba = ((__u64)cmd->cdw11 << 32) | cmd->cdw10; + __u32 transfer_len = NVME_FIELD_DECODE(cmd->cdw12, + NVME_IOCS_COMMON_CDW12_NLB_SHIFT, + NVME_IOCS_COMMON_CDW12_NLB_MASK) + 1; /* NLB + 1 */ + __u8 prinfo = NVME_FIELD_DECODE(cmd->cdw12, + NVME_IOCS_COMMON_CDW12_PRINFO_SHIFT, + NVME_IOCS_COMMON_CDW12_PRINFO_MASK); + __u8 fua = NVME_FIELD_DECODE(cmd->cdw12, + NVME_IOCS_COMMON_CDW12_FUA_SHIFT, + NVME_IOCS_COMMON_CDW12_FUA_MASK); + + __u8 wrrdprotect = nvme_prinfo_to_scsi_wrrdprotect(prinfo); + + cdb[0] = opcode; + cdb[1] = (wrrdprotect << 5) | ((fua & 1) << 3); + cdb[2] = (lba >> 56) & 0xFF; + cdb[3] = (lba >> 48) & 0xFF; + cdb[4] = (lba >> 40) & 0xFF; + cdb[5] = (lba >> 32) & 0xFF; + cdb[6] = (lba >> 24) & 0xFF; + cdb[7] = (lba >> 16) & 0xFF; + cdb[8] = (lba >> 8) & 0xFF; + cdb[9] = lba & 0xFF; + cdb[10] = (transfer_len >> 24) & 0xFF; + cdb[11] = (transfer_len >> 16) & 0xFF; + cdb[12] = (transfer_len >> 8) & 0xFF; + cdb[13] = transfer_len & 0xFF; + cdb[14] = 0; + cdb[15] = 0; +} + +static int submit_io_write(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + PSCSI_PASS_THROUGH pass_through = NULL; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + PUCHAR buffer = NULL; + void *user_data = NULL; + int err = 0; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + if (cmd->data_len > 0 && !cmd->addr) { + err = -EINVAL; + goto out; + } + + /* Allocate buffer for SCSI_PASS_THROUGH + write payload */ + buffer_len = sizeof(SCSI_PASS_THROUGH) + cmd->data_len; + buffer = (PUCHAR)malloc(buffer_len); + if (!buffer) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(buffer, buffer_len); + + pass_through = (PSCSI_PASS_THROUGH)buffer; + pass_through->Length = sizeof(SCSI_PASS_THROUGH); + pass_through->CdbLength = 16; + pass_through->DataIn = SCSI_IOCTL_DATA_OUT; + pass_through->DataTransferLength = cmd->data_len; + pass_through->TimeOutValue = (cmd->timeout_ms > 0) ? + ((cmd->timeout_ms + 999) / 1000) : 30; + pass_through->DataBufferOffset = sizeof(SCSI_PASS_THROUGH); + + fill_scsi_rw16_cdb(cmd, pass_through->Cdb); + + if (cmd->addr && cmd->data_len > 0) { + memcpy(buffer + pass_through->DataBufferOffset, + (void *)(uintptr_t)cmd->addr, cmd->data_len); + } + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_SCSI_PASS_THROUGH, + buffer, + buffer_len, + buffer, + buffer_len, + &returned_len, + NULL); + if (result && !pass_through->ScsiStatus) + break; + + if (!result) + err = get_last_error_as_errno(); + else + err = -EIO; + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + cmd->result = 0; + +out_free_buffer: + free(buffer); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +static int submit_io_read(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + PSCSI_PASS_THROUGH pass_through = NULL; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + PUCHAR buffer = NULL; + void *user_data = NULL; + int err = 0; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + if (cmd->data_len > 0 && !cmd->addr) { + err = -EINVAL; + goto out; + } + + /* Allocate buffer for SCSI_PASS_THROUGH + read payload */ + buffer_len = sizeof(SCSI_PASS_THROUGH) + cmd->data_len; + buffer = (PUCHAR)malloc(buffer_len); + if (!buffer) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(buffer, buffer_len); + + pass_through = (PSCSI_PASS_THROUGH)buffer; + pass_through->Length = sizeof(SCSI_PASS_THROUGH); + pass_through->CdbLength = 16; + pass_through->DataIn = SCSI_IOCTL_DATA_IN; + pass_through->DataTransferLength = cmd->data_len; + pass_through->TimeOutValue = (cmd->timeout_ms > 0) ? + ((cmd->timeout_ms + 999) / 1000) : 30; + pass_through->DataBufferOffset = sizeof(SCSI_PASS_THROUGH); + + fill_scsi_rw16_cdb(cmd, pass_through->Cdb); + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_SCSI_PASS_THROUGH, + buffer, + buffer_len, + buffer, + buffer_len, + &returned_len, + NULL); + if (result && !pass_through->ScsiStatus) + break; + + if (!result) + err = get_last_error_as_errno(); + else + err = -EIO; + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + if (cmd->addr && cmd->data_len > 0) { + memcpy((void *)(uintptr_t)cmd->addr, + buffer + pass_through->DataBufferOffset, + min(pass_through->DataTransferLength, cmd->data_len)); + } + + cmd->result = 0; + +out_free_buffer: + free(buffer); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +/* + * Windows-specific admin command implementations. + */ + +static int submit_admin_get_log_page(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + PSTORAGE_PROPERTY_QUERY query = NULL; + PSTORAGE_PROTOCOL_SPECIFIC_DATA protocol_data = NULL; + STORAGE_PROTOCOL_DATA_SUBVALUE_GET_LOG_PAGE protocol_data_subval = { 0 }; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + PUCHAR buffer = NULL; + void *user_data = NULL; + int err = 0; + __u32 csi; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + /* Command Set Indicator values other than NVME_CSI_NVM not supported */ + csi = NVME_FIELD_DECODE(cmd->cdw14, + NVME_LOG_CDW14_CSI_SHIFT, + NVME_LOG_CDW14_CSI_MASK); + if (csi != NVME_CSI_NVM) { + err = -ENOTSUP; + goto out; + } + + buffer_len = FIELD_OFFSET(STORAGE_PROPERTY_QUERY, AdditionalParameters) + + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + cmd->data_len; + + buffer = (PUCHAR)malloc(buffer_len); + if (!buffer) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(buffer, buffer_len); + + query = (PSTORAGE_PROPERTY_QUERY)buffer; + protocol_data = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters; + + /* + * Use StorageDeviceProtocolSpecificProperty for log pages. + * Per Windows documentation, this queries device/namespace + * protocol-specific properties. + */ + query->PropertyId = StorageDeviceProtocolSpecificProperty; + query->QueryType = PropertyStandardQuery; + + protocol_data->ProtocolType = ProtocolTypeNvme; + protocol_data->DataType = NVMeDataTypeLogPage; + + protocol_data->ProtocolDataRequestValue = NVME_FIELD_DECODE(cmd->cdw10, + NVME_LOG_CDW10_LID_SHIFT, + NVME_LOG_CDW10_LID_MASK); + + protocol_data->ProtocolDataRequestSubValue = cmd->cdw12; /* LPO[31:0] */ + protocol_data->ProtocolDataRequestSubValue2 = cmd->cdw13; /* LPO[63:32] */ + + protocol_data->ProtocolDataRequestSubValue3 = NVME_FIELD_DECODE(cmd->cdw11, + NVME_LOG_CDW11_LSI_SHIFT, + NVME_LOG_CDW11_LSI_MASK); + + protocol_data_subval.RetainAsynEvent = NVME_FIELD_DECODE(cmd->cdw10, + NVME_LOG_CDW10_RAE_SHIFT, + NVME_LOG_CDW10_RAE_MASK); + protocol_data_subval.LogSpecificField = NVME_FIELD_DECODE(cmd->cdw10, + NVME_LOG_CDW10_LSP_SHIFT, + NVME_LOG_CDW10_LSP_MASK); + protocol_data->ProtocolDataRequestSubValue4 = protocol_data_subval.AsUlong; + + protocol_data->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA); + protocol_data->ProtocolDataLength = cmd->data_len; + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_STORAGE_QUERY_PROPERTY, + buffer, + buffer_len, + buffer, + buffer_len, + &returned_len, + NULL); + if (result) + break; + err = get_last_error_as_errno(); + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + /* Copy the returned log page data to the user's buffer */ + if (cmd->addr && cmd->data_len > 0) { + memcpy((void *)(uintptr_t)cmd->addr, + (PUCHAR)protocol_data + protocol_data->ProtocolDataOffset, + min(protocol_data->ProtocolDataLength, cmd->data_len)); + } + + /* Only 32-bits of return data. Assuming CQE DW0. */ + cmd->result = protocol_data->FixedProtocolReturnData; + +out_free_buffer: + free(buffer); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +static int submit_admin_identify(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + PSTORAGE_PROPERTY_QUERY query = NULL; + PSTORAGE_PROTOCOL_SPECIFIC_DATA protocol_data = NULL; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + PUCHAR buffer = NULL; + void *user_data = NULL; + int err = 0; + __u32 cns; + __u32 csi; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + /* + * Not all Controller or Namespace Structure values are supported + * on Windows, but allow the requested command to be issued and fail + * if not supported. + */ + cns = NVME_FIELD_DECODE(cmd->cdw10, + NVME_IDENTIFY_CDW10_CNS_SHIFT, + NVME_IDENTIFY_CDW10_CNS_MASK); + + /* Command Set Indicator values other than NVME_CSI_NVM not supported */ + csi = NVME_FIELD_DECODE(cmd->cdw11, + NVME_IDENTIFY_CDW11_CSI_SHIFT, + NVME_IDENTIFY_CDW11_CSI_MASK); + if (csi != NVME_CSI_NVM) { + err = -ENOTSUP; + goto out; + } + + buffer_len = FIELD_OFFSET(STORAGE_PROPERTY_QUERY, AdditionalParameters) + + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + cmd->data_len; + + buffer = (PUCHAR)malloc(buffer_len); + if (!buffer) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(buffer, buffer_len); + + query = (PSTORAGE_PROPERTY_QUERY)buffer; + protocol_data = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters; + + query->PropertyId = StorageAdapterProtocolSpecificProperty; + protocol_data->ProtocolType = ProtocolTypeNvme; + protocol_data->DataType = NVMeDataTypeIdentify; + protocol_data->ProtocolDataRequestValue = cns; + protocol_data->ProtocolDataRequestSubValue = cmd->nsid; + protocol_data->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA); + protocol_data->ProtocolDataLength = cmd->data_len; + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_STORAGE_QUERY_PROPERTY, + buffer, + buffer_len, + buffer, + buffer_len, + &returned_len, + NULL); + if (result) + break; + err = get_last_error_as_errno(); + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + /* Copy the returned data to the user's buffer */ + if (cmd->addr && cmd->data_len > 0) { + memcpy((void *)(uintptr_t)cmd->addr, + (char *)protocol_data + protocol_data->ProtocolDataOffset, + min(protocol_data->ProtocolDataLength, cmd->data_len)); + } + + /* Only 32-bits of return data. Assuming CQE DW0. */ + cmd->result = protocol_data->FixedProtocolReturnData; + +out_free_buffer: + free(buffer); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +static int submit_admin_set_features(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + PSTORAGE_PROPERTY_SET set_property = NULL; + PSTORAGE_PROTOCOL_SPECIFIC_DATA_EXT protocol_data = NULL; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + PUCHAR buffer = NULL; + void *user_data = NULL; + int err = 0; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + buffer_len = FIELD_OFFSET(STORAGE_PROPERTY_SET, AdditionalParameters) + + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA_EXT) + cmd->data_len; + + buffer = (PUCHAR)malloc(buffer_len); + if (!buffer) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(buffer, buffer_len); + + set_property = (PSTORAGE_PROPERTY_SET)buffer; + protocol_data = (PSTORAGE_PROTOCOL_SPECIFIC_DATA_EXT)set_property->AdditionalParameters; + + set_property->PropertyId = StorageAdapterProtocolSpecificProperty; + set_property->SetType = PropertyStandardSet; + + protocol_data->ProtocolType = ProtocolTypeNvme; + protocol_data->DataType = NVMeDataTypeFeature; + + /* + * Map NVMe Set Features command DWORDs to protocol data fields. + * STORAGE_PROTOCOL_SPECIFIC_DATA_EXT values for NVMeDataTypeFeature + * are documented in the STORAGE_PROTOCOL_NVME_DATA_TYPE enumeration. + */ + protocol_data->ProtocolDataValue = cmd->cdw10; + protocol_data->ProtocolDataSubValue = cmd->cdw11; + protocol_data->ProtocolDataSubValue2 = cmd->cdw12; + protocol_data->ProtocolDataSubValue3 = cmd->cdw13; + protocol_data->ProtocolDataSubValue4 = cmd->cdw14; + protocol_data->ProtocolDataSubValue5 = cmd->cdw15; + + protocol_data->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA_EXT); + protocol_data->ProtocolDataLength = cmd->data_len; + + /* Copy input data if present */ + if (cmd->addr && cmd->data_len > 0) { + memcpy((PUCHAR)protocol_data + protocol_data->ProtocolDataOffset, + (void *)(uintptr_t)cmd->addr, + cmd->data_len); + } + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_STORAGE_SET_PROPERTY, + buffer, + buffer_len, + buffer, + buffer_len, + &returned_len, + NULL); + if (result) + break; + err = get_last_error_as_errno(); + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + /* Only 32-bits of return data. Assuming CQE DW0. */ + cmd->result = protocol_data->FixedProtocolReturnData; + +out_free_buffer: + free(buffer); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +static int submit_admin_get_features(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + PSTORAGE_PROPERTY_QUERY query = NULL; + PSTORAGE_PROTOCOL_SPECIFIC_DATA protocol_data = NULL; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + PUCHAR buffer = NULL; + void *user_data = NULL; + int err = 0; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + buffer_len = FIELD_OFFSET(STORAGE_PROPERTY_QUERY, AdditionalParameters) + + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + cmd->data_len; + + buffer = (PUCHAR)malloc(buffer_len); + if (!buffer) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(buffer, buffer_len); + + query = (PSTORAGE_PROPERTY_QUERY)buffer; + protocol_data = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters; + + query->PropertyId = StorageAdapterProtocolSpecificProperty; + query->QueryType = PropertyStandardQuery; + + protocol_data->ProtocolType = ProtocolTypeNvme; + protocol_data->DataType = NVMeDataTypeFeature; + + /* + * Map NVMe Get Features command DWORDs to protocol data fields. + * STORAGE_PROTOCOL_SPECIFIC_DATA values for Get Features are documented + * in the STORAGE_PROTOCOL_NVME_DATA_TYPE enumeration documentation. + */ + protocol_data->ProtocolDataRequestValue = cmd->cdw10; + protocol_data->ProtocolDataRequestSubValue = cmd->cdw11; + protocol_data->ProtocolDataRequestSubValue2 = cmd->cdw12; + protocol_data->ProtocolDataRequestSubValue3 = cmd->cdw13; + protocol_data->ProtocolDataRequestSubValue4 = cmd->cdw14; + + protocol_data->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA); + protocol_data->ProtocolDataLength = cmd->data_len; + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_STORAGE_QUERY_PROPERTY, + buffer, + buffer_len, + buffer, + buffer_len, + &returned_len, + NULL); + if (result) + break; + err = get_last_error_as_errno(); + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + /* Copy the returned data to the user's buffer if present */ + if (cmd->addr && cmd->data_len > 0) { + memcpy((void *)(uintptr_t)cmd->addr, + (PUCHAR)protocol_data + protocol_data->ProtocolDataOffset, + min(protocol_data->ProtocolDataLength, cmd->data_len)); + } + + /* Only 32-bits of return data. Assuming CQE DW0. */ + cmd->result = protocol_data->FixedProtocolReturnData; + +out_free_buffer: + free(buffer); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +#ifndef STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER + +/* + * Definitions for values and types not yet included in mingw's winioctl.h. + * Values found in the 10.0.26100.0 Windows SDK winioctl.h. + */ + +#define STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER 0x00000001 + +/* Activate the existing firmware immediately without controller reset. */ +#define STORAGE_HW_FIRMWARE_REQUEST_FLAG_SWITCH_TO_FIRMWARE_WITHOUT_RESET 0x10000000 +/* Replace existing firmware and activate with controller reset. */ +#define STORAGE_HW_FIRMWARE_REQUEST_FLAG_REPLACE_AND_SWITCH_UPON_RESET 0x20000000 +/* Replace the existing firmware. Not activated. */ +#define STORAGE_HW_FIRMWARE_REQUEST_FLAG_REPLACE_EXISTING_IMAGE 0x40000000 +/* Activate the existing firmware with a controller reset. */ +#define STORAGE_HW_FIRMWARE_REQUEST_FLAG_SWITCH_TO_EXISTING_FIRMWARE 0x80000000 + +typedef struct _STORAGE_HW_FIRMWARE_ACTIVATE { + DWORD Version; + DWORD Size; + + DWORD Flags; + BYTE Slot; + BYTE Reserved0[3]; +} STORAGE_HW_FIRMWARE_ACTIVATE, *PSTORAGE_HW_FIRMWARE_ACTIVATE; + +typedef struct _STORAGE_HW_FIRMWARE_DOWNLOAD { + DWORD Version; + DWORD Size; + + DWORD Flags; + BYTE Slot; + BYTE Reserved[3]; + + DWORDLONG Offset; + DWORDLONG BufferSize; + + BYTE ImageBuffer[ANYSIZE_ARRAY]; +} STORAGE_HW_FIRMWARE_DOWNLOAD, *PSTORAGE_HW_FIRMWARE_DOWNLOAD; + +#endif + +static int submit_admin_fw_commit(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + PSTORAGE_HW_FIRMWARE_ACTIVATE firmware_activate = NULL; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + void *user_data = NULL; + int err = 0; + __u8 commit_action; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + buffer_len = sizeof(STORAGE_HW_FIRMWARE_ACTIVATE); + + firmware_activate = (PSTORAGE_HW_FIRMWARE_ACTIVATE)malloc(buffer_len); + if (!firmware_activate) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(firmware_activate, buffer_len); + + firmware_activate->Version = sizeof(STORAGE_HW_FIRMWARE_ACTIVATE); + firmware_activate->Size = sizeof(STORAGE_HW_FIRMWARE_ACTIVATE); + firmware_activate->Slot = NVME_FIELD_DECODE(cmd->cdw10, + NVME_FW_COMMIT_CDW10_FS_SHIFT, + NVME_FW_COMMIT_CDW10_FS_MASK); + + /* For NVMe devices, the target is the controller */ + firmware_activate->Flags = STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER; + + /* Set additional flags based on the commit action */ + commit_action = NVME_FIELD_DECODE(cmd->cdw10, + NVME_FW_COMMIT_CDW10_CA_SHIFT, + NVME_FW_COMMIT_CDW10_CA_MASK); + + switch (commit_action) { + case 0: /* Replace, no activate */ + firmware_activate->Flags |= + STORAGE_HW_FIRMWARE_REQUEST_FLAG_REPLACE_EXISTING_IMAGE; + break; + case 1: /* Replace and activate at next reset */ + firmware_activate->Flags |= + STORAGE_HW_FIRMWARE_REQUEST_FLAG_REPLACE_AND_SWITCH_UPON_RESET; + break; + case 2: /* Activate the current firmware at next reset */ + firmware_activate->Flags |= + STORAGE_HW_FIRMWARE_REQUEST_FLAG_SWITCH_TO_EXISTING_FIRMWARE; + break; + case 3: /* Activate the current firmware immediately without reset */ + firmware_activate->Flags |= + STORAGE_HW_FIRMWARE_REQUEST_FLAG_SWITCH_TO_FIRMWARE_WITHOUT_RESET; + break; + } + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_STORAGE_FIRMWARE_ACTIVATE, + firmware_activate, + buffer_len, + NULL, + 0, + &returned_len, + NULL); + if (result) + break; + err = get_last_error_as_errno(); + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + /* FW Commit doesn't return result data */ + cmd->result = 0; + +out_free_buffer: + free(firmware_activate); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +static int submit_admin_fw_download(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + PSTORAGE_HW_FIRMWARE_DOWNLOAD firmware_download = NULL; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + PUCHAR buffer = NULL; + void *user_data = NULL; + int err = 0; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + /* + * Allocate buffer for STORAGE_HW_FIRMWARE_DOWNLOAD structure. + * The structure contains the firmware image data inline at the end. + */ + buffer_len = FIELD_OFFSET(STORAGE_HW_FIRMWARE_DOWNLOAD, ImageBuffer) + + cmd->data_len; + + buffer = (PUCHAR)malloc(buffer_len); + if (!buffer) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(buffer, buffer_len); + + firmware_download = (PSTORAGE_HW_FIRMWARE_DOWNLOAD)buffer; + firmware_download->Version = sizeof(STORAGE_HW_FIRMWARE_DOWNLOAD); + firmware_download->Size = buffer_len; + + /* + * The NVMe command uses DWORD counts for size and offset. + * The Windows API uses byte counts, so convert accordingly. + * See ioctl.h/nvme_init_fw_download for encoding details. + */ + firmware_download->BufferSize = (DWORDLONG)(cmd->cdw10 + 1) << 2; + firmware_download->Offset = (DWORDLONG)cmd->cdw11 << 2; + + /* + * Assuming we need the CONTROLLER flag. + * TODO: Do we need to use the LAST_SEGMENT flag? + * If so, we will need a way to communicate in the cmd struct + * whether this is the last segment. + */ + firmware_download->Flags = STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER; + + /* + * Assuming that slot is not used for NVMe devices since it is not + * part of the NVMe FW Download command. Setting to 0. + * TODO: If needed, we will need a way to communicate it in the + * cmd struct. If not needed, just remove this setting completely. + */ + firmware_download->Slot = 0; + + if (cmd->addr && cmd->data_len > 0) { + memcpy(firmware_download->ImageBuffer, + (void *)(uintptr_t)cmd->addr, + cmd->data_len); + } + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_STORAGE_FIRMWARE_DOWNLOAD, + buffer, + buffer_len, + NULL, + 0, + &returned_len, + NULL); + if (result) + break; + err = get_last_error_as_errno(); + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + /* Firmware download doesn't return result data */ + cmd->result = 0; + +out_free_buffer: + free(buffer); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +/* SCSI operation code for sanitize command - from ddk/scsi.h */ +#define SCSIOP_SANITIZE 0x48 + +static int submit_admin_format_nvm_user_data_erase( + struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + /* + * User Data Erase: Use IOCTL_SCSI_PASS_THROUGH with SCSIOP_SANITIZE + * to erase user data. The sanitize operation erases all user data, + * with the contents being indeterminate after erase. + */ + PSCSI_PASS_THROUGH pass_through = NULL; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + PUCHAR buffer = NULL; + void *user_data = NULL; + int err = 0; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + /* Allocate buffer for SCSI_PASS_THROUGH */ + buffer_len = sizeof(SCSI_PASS_THROUGH); + buffer = (PUCHAR)malloc(buffer_len); + if (!buffer) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(buffer, buffer_len); + + pass_through = (PSCSI_PASS_THROUGH)buffer; + pass_through->Length = sizeof(SCSI_PASS_THROUGH); + pass_through->CdbLength = 6; + pass_through->DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; + pass_through->DataTransferLength = 0; + pass_through->TimeOutValue = (cmd->timeout_ms > 0) ? + ((cmd->timeout_ms + 999) / 1000) : 300; + pass_through->DataBufferOffset = 0; + + /* + * Build the Sanitize CDB (6 bytes) + * Byte 0: Operation code (0x48) + * Byte 1: Service Action + Immediate bit + * Bits 7-5: Reserved + * Bits 4-3: Service action + * 00 = Block Erase + * 01 = Crypto Scramble + * 10 = Overwrite + * Bit 2: Ancillary (reserved) + * Bit 1: IMMED (Immediate) + * Bit 0: Reserved + * Bytes 2-5: Reserved + * + * For NVMe User Data Erase, use Block Erase (service action 00) + */ + pass_through->Cdb[0] = SCSIOP_SANITIZE; + pass_through->Cdb[1] = 0x00; /* Block Erase, non-immediate */ + pass_through->Cdb[2] = 0; /* Reserved */ + pass_through->Cdb[3] = 0; /* Reserved */ + pass_through->Cdb[4] = 0; /* Reserved */ + pass_through->Cdb[5] = 0; /* Reserved */ + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_SCSI_PASS_THROUGH, + buffer, + buffer_len, + buffer, + buffer_len, + &returned_len, + NULL); + if (result) + break; + + if (!result) + err = get_last_error_as_errno(); + else + err = -EIO; + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + cmd->result = 0; + +out_free_buffer: + free(buffer); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +static int submit_admin_format_nvm_crypto_erase( + struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + BOOL result = FALSE; + ULONG returned_len = 0; + void *user_data = NULL; + int err = 0; + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_STORAGE_REINITIALIZE_MEDIA, + NULL, + 0, + NULL, + 0, + &returned_len, + NULL); + if (result) + break; + err = get_last_error_as_errno(); + } while (hdl->decide_retry(hdl, cmd, err)); + + cmd->result = 0; + +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +static int submit_admin_format_nvm(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + __u8 ses; + + /* For WinPE, use storage protocol command. */ + if (get_is_win_pe()) + return submit_storage_protocol_command(hdl, cmd); + + /* Namespace-specific format is not supported on Windows */ + if (cmd->nsid != NVME_NSID_ALL) + return -ENOTSUP; + + /* + * Extract the Secure Erase Settings (SES) from CDW10 and call the + * appropriate implementation based on the requested erase type. + */ + ses = NVME_FIELD_DECODE(cmd->cdw10, + NVME_FORMAT_CDW10_SES_SHIFT, + NVME_FORMAT_CDW10_SES_MASK); + + /* + * Per Microsoft StorNVMe documentation: + * - SES=0 (No Erase): Not supported on Windows + * - SES=1 (User Data Erase): Use IOCTL_SCSI_PASS_THROUGH with SANITIZE + * - SES=2 (Cryptographic Erase): Use IOCTL_STORAGE_REINITIALIZE_MEDIA + */ + switch (ses) { + case NVME_FORMAT_SES_NONE: + return -ENOTSUP; /* Not supported on Windows */ + case NVME_FORMAT_SES_USER_DATA_ERASE: + return submit_admin_format_nvm_user_data_erase(hdl, cmd); + case NVME_FORMAT_SES_CRYPTO_ERASE: + return submit_admin_format_nvm_crypto_erase(hdl, cmd); + default: + return -EINVAL; + } +} + +/* SCSI operation codes for security commands - from ddk/scsi.h */ +#define SCSIOP_SECURITY_PROTOCOL_IN 0xA2 +#define SCSIOP_SECURITY_PROTOCOL_OUT 0xB5 + +static int submit_admin_security_send_receive( + struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + PSCSI_PASS_THROUGH pass_through = NULL; + ULONG buffer_len = 0; + ULONG returned_len = 0; + BOOL result = FALSE; + PUCHAR buffer = NULL; + void *user_data = NULL; + int err = 0; + bool is_send = (cmd->opcode == nvme_admin_security_send); + + user_data = hdl->submit_entry(hdl, cmd); + if (hdl->ctx->dry_run) + goto out; + + if (hdl->fd == INVALID_HANDLE_VALUE || hdl->fd == NULL) { + err = -EBADF; + goto out; + } + + /* Allocate buffer for SCSI_PASS_THROUGH + data */ + buffer_len = sizeof(SCSI_PASS_THROUGH) + cmd->data_len; + buffer = (PUCHAR)malloc(buffer_len); + if (!buffer) { + err = -ENOMEM; + goto out; + } + + ZeroMemory(buffer, buffer_len); + + pass_through = (PSCSI_PASS_THROUGH)buffer; + pass_through->Length = sizeof(SCSI_PASS_THROUGH); + pass_through->CdbLength = 12; + pass_through->DataIn = is_send ? SCSI_IOCTL_DATA_OUT : SCSI_IOCTL_DATA_IN; + pass_through->DataTransferLength = cmd->data_len; + pass_through->TimeOutValue = (cmd->timeout_ms > 0) ? + ((cmd->timeout_ms + 999) / 1000) : 30; + pass_through->DataBufferOffset = sizeof(SCSI_PASS_THROUGH); + + if (is_send && cmd->data_len > 0) { + memcpy(buffer + pass_through->DataBufferOffset, + (void *)(uintptr_t)cmd->addr, cmd->data_len); + } + + /* + * Build the Security Protocol CDB (12 bytes) + * Per SPC-4: Security Protocol In/Out CDB is 12 bytes: + * Byte 0: Operation code (0xA2/0xB5) + * Byte 1: Security Protocol (SECP) + * Byte 2-3: Security Protocol Specific (SPSP) + * Byte 4: Reserved + * Byte 5: NSSF (NVMe Security Specific Field) + * Byte 6-9: Allocation Length (big-endian) + * Byte 10-11: Reserved + */ + pass_through->Cdb[0] = is_send ? + SCSIOP_SECURITY_PROTOCOL_OUT : SCSIOP_SECURITY_PROTOCOL_IN; + pass_through->Cdb[1] = NVME_FIELD_DECODE(cmd->cdw10, + NVME_SECURITY_SECP_SHIFT, + NVME_SECURITY_SECP_MASK); + pass_through->Cdb[2] = NVME_FIELD_DECODE(cmd->cdw10, + NVME_SECURITY_SPSP1_SHIFT, + NVME_SECURITY_SPSP1_MASK); + pass_through->Cdb[3] = NVME_FIELD_DECODE(cmd->cdw10, + NVME_SECURITY_SPSP0_SHIFT, + NVME_SECURITY_SPSP0_MASK); + pass_through->Cdb[4] = 0; /* Reserved */ + pass_through->Cdb[5] = NVME_FIELD_DECODE(cmd->cdw10, + NVME_SECURITY_NSSF_SHIFT, + NVME_SECURITY_NSSF_MASK); + /* Transfer/Allocation length (CDW11) in big-endian */ + pass_through->Cdb[6] = (cmd->cdw11 >> 24) & 0xFF; + pass_through->Cdb[7] = (cmd->cdw11 >> 16) & 0xFF; + pass_through->Cdb[8] = (cmd->cdw11 >> 8) & 0xFF; + pass_through->Cdb[9] = cmd->cdw11 & 0xFF; + pass_through->Cdb[10] = 0; /* Reserved */ + pass_through->Cdb[11] = 0; /* Reserved */ + + do { + err = 0; + result = DeviceIoControl(hdl->fd, + IOCTL_SCSI_PASS_THROUGH, + buffer, + buffer_len, + buffer, + buffer_len, + &returned_len, + NULL); + if (result && !pass_through->ScsiStatus) + break; + + if (!result) + err = get_last_error_as_errno(); + else + err = -EIO; + } while (hdl->decide_retry(hdl, cmd, err)); + + if (err) + goto out_free_buffer; + + /* Copy returned data to user buffer if this was a receive */ + if (!is_send && cmd->addr && cmd->data_len > 0) { + memcpy((void *)(uintptr_t)cmd->addr, + buffer + pass_through->DataBufferOffset, + pass_through->DataTransferLength); + cmd->data_len = pass_through->DataTransferLength; + } + + /* No result data returned by command. */ + cmd->result = 0; + +out_free_buffer: + free(buffer); +out: + hdl->submit_exit(hdl, cmd, err, user_data); + return err; +} + +/* + * Windows only supports a subset of NVMe IO command calls from user space + * and uses different IOCTLs for different commands instead of a single + * passthru interface. + * Passthru is supported using IOCTL_STORAGE_PROTOCOL_COMMAND, + * but only for vendor-specific commands and a small subset of IO commands. + * For supported commands and a mapping to the required IOCTLs, see: + * https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/stornvme-command-set-support + */ +__public int libnvme_submit_io_passthru(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + if (!hdl || !cmd) + return -EINVAL; + + switch (cmd->opcode) { + case nvme_cmd_flush: + return submit_io_flush(hdl, cmd); + case nvme_cmd_write: + return submit_io_write(hdl, cmd); + case nvme_cmd_read: + return submit_io_read(hdl, cmd); + case nvme_cmd_compare: + /* Only supported on WinPE */ + if (get_is_win_pe()) + return submit_storage_protocol_command(hdl, cmd); + else + return -ENOTSUP; + case 0x80 ... 0xFF: /* vendor-specific commands */ + return submit_storage_protocol_command(hdl, cmd); + default: + return -ENOTSUP; + } + return -ENOTSUP; +} + +/* + * Windows only supports a subset of NVMe admin command calls from user space + * and uses different IOCTLs for different commands instead of a single + * passthru interface. + * Passthru is supported using IOCTL_STORAGE_PROTOCOL_COMMAND, + * but only for vendor-specific commands and a small subset of admin commands. + * For supported commands and a mapping to the required IOCTLs, see: + * https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/stornvme-command-set-support + */ +__public int libnvme_submit_admin_passthru(struct libnvme_transport_handle *hdl, + struct libnvme_passthru_cmd *cmd) +{ + if (!hdl || !cmd) + return -EINVAL; + + if (hdl->type != LIBNVME_TRANSPORT_HANDLE_TYPE_DIRECT) + return -ENOTSUP; + + switch (cmd->opcode) { + case nvme_admin_get_log_page: + return submit_admin_get_log_page(hdl, cmd); + case nvme_admin_identify: + return submit_admin_identify(hdl, cmd); + case nvme_admin_set_features: + return submit_admin_set_features(hdl, cmd); + case nvme_admin_get_features: + return submit_admin_get_features(hdl, cmd); + case nvme_admin_ns_mgmt: + case nvme_admin_ns_attach: + /* Only supported on WinPE */ + if (get_is_win_pe()) + return submit_storage_protocol_command(hdl, cmd); + else + return -ENOTSUP; + case nvme_admin_fw_commit: + return submit_admin_fw_commit(hdl, cmd); + case nvme_admin_fw_download: + return submit_admin_fw_download(hdl, cmd); + case nvme_admin_dev_self_test: + return submit_storage_protocol_command(hdl, cmd); + case nvme_admin_format_nvm: + return submit_admin_format_nvm(hdl, cmd); + case nvme_admin_security_send: + case nvme_admin_security_recv: + return submit_admin_security_send_receive(hdl, cmd); + case nvme_admin_sanitize_nvm: + return submit_storage_protocol_command(hdl, cmd); + case 0xC0 ... 0xFF: /* vendor-specific commands */ + return submit_storage_protocol_command(hdl, cmd); + default: + return -ENOTSUP; + } +} diff --git a/libnvme/src/nvme/ioctl.c b/libnvme/src/nvme/ioctl.c index 2fcd868019..551aac68cc 100644 --- a/libnvme/src/nvme/ioctl.c +++ b/libnvme/src/nvme/ioctl.c @@ -13,9 +13,13 @@ #include #include -#include #include +#ifndef _WIN32 +#include +#include +#endif + #include #include #include @@ -25,6 +29,7 @@ #include "private.h" #include "compiler-attributes.h" +#ifndef _WIN32 static int nvme_verify_chr(struct libnvme_transport_handle *hdl) { static struct stat nvme_stat; @@ -93,6 +98,24 @@ __public int libnvme_get_nsid(struct libnvme_transport_handle *hdl, __u32 *nsid) return 0; } +__public int libnvme_update_block_size(struct libnvme_transport_handle *hdl, + int block_size) +{ + int ret; + libnvme_fd_t fd = libnvme_transport_handle_get_fd(hdl); + + ret = ioctl(fd, BLKBSZSET, &block_size); + if (ret < 0) + return -errno; + + ret = ioctl(fd, BLKRRPART); + if (ret < 0) + return -errno; + + return 0; +} +#endif + void *__libnvme_submit_entry(struct libnvme_transport_handle *hdl, struct libnvme_passthru_cmd *cmd) { @@ -110,6 +133,7 @@ bool __libnvme_decide_retry(struct libnvme_transport_handle *hdl, return false; } +#ifndef _WIN32 /* * The 64 bit version is the preferred version to use, but for backwards * compatibility keep a 32 version. @@ -200,3 +224,4 @@ __public int libnvme_submit_admin_passthru(struct libnvme_transport_handle *hdl, return -ENOTSUP; } +#endif /* !_WIN32 */ \ No newline at end of file diff --git a/libnvme/src/nvme/ioctl.h b/libnvme/src/nvme/ioctl.h index 7284ba39f8..cbdb5d5a3d 100644 --- a/libnvme/src/nvme/ioctl.h +++ b/libnvme/src/nvme/ioctl.h @@ -96,3 +96,15 @@ int libnvme_rescan_ns(struct libnvme_transport_handle *hdl); * Return: 0 if @nsid was set successfully or -1 with errno set otherwise. */ int libnvme_get_nsid(struct libnvme_transport_handle *hdl, __u32 *nsid); + +/** + * libnvme_update_block_size() - Update the block size + * @hdl: Transport handle + * @block_size: New block size + * + * Notify the kernel blkdev to update its block size after a block size change. + * This should only be used for namespace handles, not controllers. + * + * Return: 0 if the block size was updated or a negative error code otherwise. + */ +int libnvme_update_block_size(struct libnvme_transport_handle *hdl, int block_size); diff --git a/libnvme/src/nvme/lib-compat.h b/libnvme/src/nvme/lib-compat.h new file mode 100644 index 0000000000..53495500ab --- /dev/null +++ b/libnvme/src/nvme/lib-compat.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libnvme. + * Copyright (c) 2026 Micron Technology, Inc. + * + * Cross-platform compatibility definitions, types, and utilities. + * + * Authors: Broc Going + * Brandon Busacker + */ +#pragma once + +#if defined(_WIN32) || defined(_WIN64) + +#define WIN32_LEAN_AND_MEAN /* keeps windows.h from including winsock.*/ +#include + +#include +#include + +typedef HANDLE libnvme_fd_t; + +#define TEST_FD INVALID_HANDLE_VALUE +#define INIT_FD nullptr + +/* + * Set stdout and stderr to binary mode to prevent Windows text-mode + * translation from converting LF to CRLF and corrupting raw binary output. + * Call once at startup. + */ +static inline void libnvme_init(void) +{ + _setmode(_fileno(stdout), O_BINARY); + _setmode(_fileno(stderr), O_BINARY); +} + +#else + +typedef int libnvme_fd_t; +#define TEST_FD 0xFD +#define INIT_FD -1 + +/* Platform initialization - no-op on Linux */ +static inline void libnvme_init(void) {} + +#endif diff --git a/libnvme/src/nvme/lib-windows.c b/libnvme/src/nvme/lib-windows.c new file mode 100644 index 0000000000..3ec545746f --- /dev/null +++ b/libnvme/src/nvme/lib-windows.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2025 Micron Technology, Inc. + * + * Authors: Brandon Capener + */ + +#include "private.h" +#include "nvme/lib.h" +#include "compiler-attributes.h" + + +/* fstat implementation for Windows device HANDLE */ +static int __handle_fstat(HANDLE fd, struct stat *buf) +{ + BY_HANDLE_FILE_INFORMATION file_info; + ULARGE_INTEGER ull; + DWORD file_type; + + if (!buf) { + errno = EINVAL; + return -1; + } + + /* Check for invalid handle */ + if (fd == INVALID_HANDLE_VALUE || fd == NULL) { + errno = EBADF; + return -1; + } + + /* + * GetFileInformationByHandle() does not work for all device HANDLEs + * (e.g. raw \\\\.\\PhysicalDriveN). For those, fall back to file type. + */ + if (!GetFileInformationByHandle(fd, &file_info)) { + file_type = GetFileType(fd); + if (file_type == FILE_TYPE_DISK || file_type == FILE_TYPE_CHAR) { + memset(buf, 0, sizeof(*buf)); + buf->st_mode = S_IFBLK | 0600; + buf->st_nlink = 1; + return 0; + } + + errno = EBADF; + return -1; + } + + /* Fill in the stat structure */ + memset(buf, 0, sizeof(*buf)); + + /* Convert Windows file attributes to stat mode */ + if (file_info.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) + /* Windows device files should be marked as block devices */ + /* This is used by libnvme_verify_chr to check device type */ + buf->st_mode = S_IFBLK | 0600; + else if (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + buf->st_mode = S_IFDIR | 0755; + else + buf->st_mode = S_IFREG | 0644; + + /* File size */ + buf->st_size = (((off_t)file_info.nFileSizeHigh << 32) + | file_info.nFileSizeLow); + + /* Number of hard links */ + buf->st_nlink = file_info.nNumberOfLinks; + + /* Convert FILETIME to time_t for timestamps */ + /* Windows FILETIME is 100-nanosecond intervals since Jan 1, 1601 */ + /* Unix time_t is seconds since Jan 1, 1970 */ + ull.LowPart = file_info.ftLastWriteTime.dwLowDateTime; + ull.HighPart = file_info.ftLastWriteTime.dwHighDateTime; + buf->st_mtime = (time_t)((ull.QuadPart / 10000000ULL) - 11644473600ULL); + + ull.LowPart = file_info.ftLastAccessTime.dwLowDateTime; + ull.HighPart = file_info.ftLastAccessTime.dwHighDateTime; + buf->st_atime = (time_t)((ull.QuadPart / 10000000ULL) - 11644473600ULL); + + ull.LowPart = file_info.ftCreationTime.dwLowDateTime; + ull.HighPart = file_info.ftCreationTime.dwHighDateTime; + buf->st_ctime = (time_t)((ull.QuadPart / 10000000ULL) - 11644473600ULL); + + return 0; +} + +static int __libnvme_transport_handle_open_direct(struct libnvme_transport_handle *hdl, const char *name) +{ + char device_path[MAX_PATH]; + HANDLE h; + + /* Parse and open direct device */ + hdl->type = LIBNVME_TRANSPORT_HANDLE_TYPE_DIRECT; + + /* Convert device name to Windows path */ + if (strncmp(name, "\\\\.\\", 4) == 0) { + /* Already a Windows device path */ + snprintf(device_path, sizeof(device_path), "%s", name); + } else { + /* Assume it's a device name, prepend Windows device prefix */ + /* PhysicalDriveN format */ + snprintf(device_path, sizeof(device_path), "\\\\.\\%s", name); + } + + h = CreateFile(device_path, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (h == INVALID_HANDLE_VALUE) { + int err = GetLastError(); + /* Map Windows error to errno */ + switch (err) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + return -ENOENT; + case ERROR_ACCESS_DENIED: + return -EACCES; + default: + return -EIO; + } + } + + hdl->fd = h; + + __handle_fstat(hdl->fd, &hdl->stat); + + /* Windows doesn't distinguish 32/64-bit ioctl, assume 64-bit capable */ + hdl->ioctl_admin64 = true; + hdl->ioctl_io64 = true; + + return 0; +} + +/* Transport handle operations (linux.c) */ +__public int libnvme_open(struct libnvme_global_ctx *ctx, const char *name, + struct libnvme_transport_handle **hdlp) +{ + struct libnvme_transport_handle *hdl; + int ret; + + hdl = __libnvme_create_transport_handle(ctx); + if (!hdl) + return -ENOMEM; + + hdl->name = strdup(name); + if (!hdl->name) { + free(hdl); + return -ENOMEM; + } + + /* Handle test devices */ + if (!strncmp(name, "NVME_TEST_FD", 12)) { + hdl->type = LIBNVME_TRANSPORT_HANDLE_TYPE_DIRECT; + hdl->fd = TEST_FD; + + if (!strcmp(name, "NVME_TEST_FD64")) + hdl->ioctl_admin64 = true; + + *hdlp = hdl; + return 0; + } + + /* MI transport not supported on Windows */ + if (!strncmp(name, "mctp:", strlen("mctp:"))) { + libnvme_close(hdl); + return -ENOTSUP; + } else { + ret = __libnvme_transport_handle_open_direct(hdl, name); + } + + if (ret) { + libnvme_close(hdl); + return ret; + } + + *hdlp = hdl; + + return 0; +} + +__public void libnvme_close(struct libnvme_transport_handle *hdl) +{ + if (!hdl) + return; + + free(hdl->name); + + switch (hdl->type) { + case LIBNVME_TRANSPORT_HANDLE_TYPE_DIRECT: + /* Close Windows HANDLE if valid */ + if (hdl->fd && hdl->fd != TEST_FD) + CloseHandle(hdl->fd); + free(hdl); + break; + case LIBNVME_TRANSPORT_HANDLE_TYPE_MI: + /* MI not supported on Windows */ + free(hdl); + break; + case LIBNVME_TRANSPORT_HANDLE_TYPE_UNKNOWN: + free(hdl); + break; + } +} diff --git a/libnvme/src/nvme/lib.c b/libnvme/src/nvme/lib.c index bfec5713a0..2892fbe7e1 100644 --- a/libnvme/src/nvme/lib.c +++ b/libnvme/src/nvme/lib.c @@ -17,14 +17,20 @@ #include #endif +#ifndef _WIN32 #include +#endif #include #include "cleanup.h" #include "cleanup-linux.h" #include "private.h" + +#ifdef CONFIG_MI #include "private-mi.h" +#endif + #include "compiler-attributes.h" static bool libnvme_mi_probe_enabled_default(void) @@ -137,6 +143,7 @@ __public void libnvme_transport_handle_set_decide_retry(struct libnvme_transport hdl->decide_retry = __libnvme_decide_retry; } +#ifndef _WIN32 static int __nvme_transport_handle_open_direct( struct libnvme_transport_handle *hdl, const char *devname) { @@ -201,6 +208,7 @@ void __libnvme_transport_handle_close_direct( close(hdl->fd); free(hdl); } +#endif /* !_WIN32 */ struct libnvme_transport_handle *__libnvme_create_transport_handle( struct libnvme_global_ctx *ctx) @@ -219,6 +227,7 @@ struct libnvme_transport_handle *__libnvme_create_transport_handle( return hdl; } +#ifndef _WIN32 __public int libnvme_open(struct libnvme_global_ctx *ctx, const char *name, struct libnvme_transport_handle **hdlp) { @@ -280,8 +289,9 @@ __public void libnvme_close(struct libnvme_transport_handle *hdl) break; } } +#endif /* !_WIN32 */ -__public int libnvme_transport_handle_get_fd(struct libnvme_transport_handle *hdl) +__public libnvme_fd_t libnvme_transport_handle_get_fd(struct libnvme_transport_handle *hdl) { return hdl->fd; } diff --git a/libnvme/src/nvme/lib.h b/libnvme/src/nvme/lib.h index e1375db276..2ca5e93344 100644 --- a/libnvme/src/nvme/lib.h +++ b/libnvme/src/nvme/lib.h @@ -11,7 +11,10 @@ #include #include +#include #include +#include +#include enum libnvme_log_level { LIBNVME_LOG_ERR = 0, @@ -94,9 +97,10 @@ void libnvme_close(struct libnvme_transport_handle *hdl); * If the device handle is for a ioctl based device, * libnvme_transport_handle_get_fd will return a valid file descriptor. * - * Return: File descriptor for an IOCTL based transport handle, otherwise -1. + * Return: File descriptor for an IOCTL based transport handle, + * otherwise INIT_FD. */ -int libnvme_transport_handle_get_fd(struct libnvme_transport_handle *hdl); +libnvme_fd_t libnvme_transport_handle_get_fd(struct libnvme_transport_handle *hdl); /** * libnvme_transport_handle_get_name - Return name of the device diff --git a/libnvme/src/nvme/log.c b/libnvme/src/nvme/log.c index a436e005f7..6074253aa2 100644 --- a/libnvme/src/nvme/log.c +++ b/libnvme/src/nvme/log.c @@ -14,6 +14,8 @@ #include #include +#include + #include #include "cleanup.h" diff --git a/libnvme/src/nvme/malloc.h b/libnvme/src/nvme/malloc.h new file mode 100644 index 0000000000..207f401892 --- /dev/null +++ b/libnvme/src/nvme/malloc.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libnvme. + * Copyright (c) 2026 Micron Technology, Inc. + * + * Cross-platform compatibility for malloc.h. + * Provides functionality that may be missing on some platforms. + * Compatibility is not comprehensive. Only functionality required by + * nvme-cli and libnvme is included. + * + * Authors: Broc Going + * Brandon Capener + */ +#pragma once + +#include + +#ifdef _WIN32 + +/* malloc_usable_size implementation for Windows */ +static inline size_t malloc_usable_size(void *ptr) +{ + return _msize(ptr); +} + +#endif diff --git a/libnvme/src/nvme/mkdir.h b/libnvme/src/nvme/mkdir.h new file mode 100644 index 0000000000..aa402dfc98 --- /dev/null +++ b/libnvme/src/nvme/mkdir.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libnvme. + * Copyright (c) 2026 Micron Technology, Inc. + * + * Cross-platform compatibility for mkdir (sys/stat.h). + * + * Authors: Broc Going + */ +#pragma once + +#if defined(_WIN32) || defined(_WIN64) + +#include + +/* Windows mkdir doesn't take the mode parameter */ +#define mkdir(path, mode) _mkdir(path) + +#endif diff --git a/libnvme/src/nvme/private.h b/libnvme/src/nvme/private.h index a1c879f349..1a910d7865 100644 --- a/libnvme/src/nvme/private.h +++ b/libnvme/src/nvme/private.h @@ -10,6 +10,8 @@ #include #include +#include + #include #include "nvme/nvme-types.h" @@ -151,7 +153,7 @@ struct libnvme_transport_handle { struct libnvme_passthru_cmd *cmd, int err); /* direct */ - int fd; + libnvme_fd_t fd; struct stat stat; bool ioctl_admin64; bool ioctl_io64; diff --git a/libnvme/src/nvme/signal.h b/libnvme/src/nvme/signal.h new file mode 100644 index 0000000000..dfedd7d04e --- /dev/null +++ b/libnvme/src/nvme/signal.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libnvme. + * Copyright (c) 2026 Micron Technology, Inc. + * + * Cross-platform compatibility for signal.h. + * Provides functionality that may be missing on some platforms. + * Compatibility is not comprehensive. Only functionality required by + * nvme-cli and libnvme is included. + * + * Authors: Broc Going + * Brandon Busacker + */ +#pragma once + +#include + +#if defined(_WIN32) || defined(_WIN64) + +/* signal.h POSIX compatibility - Windows doesn't have sigaction */ + +struct sigaction { + void (*sa_handler)(int); + int sa_flags; + int sa_mask; /* simplified - normally sigset_t */ +}; + +static inline int sigemptyset(int *set) +{ + *set = 0; + return 0; +} + +/* + * Simplified signal handling using Windows signal() function + * This is sufficient for handling SIGINT with no mask or flags. + */ +static inline int sigaction(int signum, const struct sigaction *act, + struct sigaction *oldact) +{ + (void)oldact; /* ignore old action for simplicity */ + if (act && act->sa_handler) { + signal(signum, act->sa_handler); + return 0; + } + return -1; +} + +#endif diff --git a/libnvme/src/nvme/stdio.h b/libnvme/src/nvme/stdio.h new file mode 100644 index 0000000000..de11f2fdb7 --- /dev/null +++ b/libnvme/src/nvme/stdio.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libnvme. + * Copyright (c) 2026 Micron Technology, Inc. + * + * Cross-platform compatibility for stdio.h. + * Provides functionality that may be missing on some platforms. + * Compatibility is not comprehensive. Only functionality required by + * nvme-cli and libnvme is included. + * + * Authors: Broc Going + * Brandon Busacker +*/ +#pragma once + +#include + +#if defined(_WIN32) || defined(_WIN64) + +#include +#include +#include +#include + +/* stdio.h POSIX extensions */ + +/* dprintf implementation for Windows */ +static inline int dprintf(int fd, const char *format, ...) +{ + va_list args; + char buffer[4096]; + int result; + + va_start(args, format); + result = vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + if (fd == STDERR_FILENO) + fputs(buffer, stderr); + else if (fd == STDOUT_FILENO) + fputs(buffer, stdout); + return result; +} + +/* getline implementation for Windows */ +static inline ssize_t getline(char **lineptr, size_t *n, FILE *stream) +{ + char *bufptr = NULL; + char *p = bufptr; + size_t size; + int c; + + if (lineptr == NULL || stream == NULL || n == NULL) { + errno = EINVAL; + return -1; + } + + bufptr = *lineptr; + size = *n; + + c = fgetc(stream); + if (c == EOF) + return -1; + + if (bufptr == NULL) { + bufptr = (char *)malloc(128); + if (bufptr == NULL) { + errno = ENOMEM; + return -1; + } + size = 128; + } + + p = bufptr; + while (c != EOF) { + if ((size_t)(p - bufptr) + 1 >= size) { + size_t pos = (size_t)(p - bufptr); + + size = size + 128; + bufptr = (char *)realloc(bufptr, size); + if (bufptr == NULL) { + errno = ENOMEM; + return -1; + } + p = bufptr + pos; + } + *p++ = c; + if (c == '\n') + break; + c = fgetc(stream); + } + + *p = '\0'; + *lineptr = bufptr; + *n = size; + + return p - bufptr; +} + +/* open_memstream workaround for Windows - returns a temporary file instead */ +static inline FILE *open_memstream(char **ptr, size_t *sizeloc) +{ + FILE *f = tmpfile(); + + if (ptr) + *ptr = NULL; + if (sizeloc) + *sizeloc = 0; + return f; +} + +#endif diff --git a/libnvme/src/nvme/stdlib.h b/libnvme/src/nvme/stdlib.h new file mode 100644 index 0000000000..275fd5dfda --- /dev/null +++ b/libnvme/src/nvme/stdlib.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libnvme. + * Copyright (c) 2026 Micron Technology, Inc. + * + * Cross-platform compatibility for stdlib.h. + * Provides functionality that may be missing on some platforms. + * Compatibility is not comprehensive. Only functionality required by + * nvme-cli and libnvme is included. + * + * Authors: Broc Going + * Brandon Capener + */ +#pragma once + +#include + +#if defined(_WIN32) || defined(_WIN64) + +#include + +/* Aligned memory allocation function, use aligned_free to free. */ +static inline int posix_memalign(void **memptr, size_t alignment, size_t size) +{ + *memptr = _aligned_malloc(size, alignment); + return (*memptr == NULL) ? ENOMEM : 0; +} + +/* reallocarray implementation for Windows */ +static inline void *reallocarray(void *ptr, size_t nmemb, size_t size) +{ + size_t total_size; + + /* Check for multiplication overflow */ + if (nmemb != 0 && size > SIZE_MAX / nmemb) { + errno = ENOMEM; + return NULL; + } + + total_size = nmemb * size; + return realloc(ptr, total_size); +} + +#endif + +/* + * Cross-platform compatible free for aligned memory allocations. + * Use when posix_memalign is used to allocate memory. + */ +static inline void aligned_free(void *p) +{ +#if defined(_WIN32) || defined(_WIN64) + _aligned_free(p); +#else + free(p); +#endif +} diff --git a/libnvme/src/nvme/tree.c b/libnvme/src/nvme/tree.c index 42d025fcd1..9f58f2d7f3 100644 --- a/libnvme/src/nvme/tree.c +++ b/libnvme/src/nvme/tree.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -64,6 +63,7 @@ struct candidate_args { typedef bool (*ctrl_match_t)(struct libnvme_ctrl *c, struct candidate_args *candidate); +#ifndef _WIN32 static void __libnvme_free_ctrl(libnvme_ctrl_t c); static int libnvme_subsystem_scan_namespace(struct libnvme_global_ctx *ctx, struct libnvme_subsystem *s, char *name); @@ -406,6 +406,7 @@ __public struct libnvme_global_ctx *libnvme_host_get_global_ctx( { return h->ctx; } +#endif /* _WIN32 */ __public void libnvme_host_set_pdc_enabled(libnvme_host_t h, bool enabled) { @@ -420,6 +421,7 @@ __public bool libnvme_host_is_pdc_enabled(libnvme_host_t h, bool fallback) return fallback; } +#ifndef _WIN32 __public libnvme_subsystem_t libnvme_first_subsystem(libnvme_host_t h) { return list_top(&h->subsystems, struct libnvme_subsystem, entry); @@ -2425,7 +2427,7 @@ static int libnvme_ns_init(const char *path, struct libnvme_ns *ns) if (ret) return ret; } else { - __cleanup_free struct nvme_id_ns *id = NULL; + __cleanup_nvme_free struct nvme_id_ns *id = NULL; uint8_t flbas; id = __libnvme_alloc(sizeof(*ns)); @@ -2712,3 +2714,4 @@ __public struct libnvme_ns *libnvme_subsystem_lookup_namespace( } return NULL; } +#endif /* !_WIN32 */ diff --git a/libnvme/src/nvme/unistd.h b/libnvme/src/nvme/unistd.h new file mode 100644 index 0000000000..43153517f2 --- /dev/null +++ b/libnvme/src/nvme/unistd.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libnvme. + * Copyright (c) 2026 Micron Technology, Inc. + * + * Cross-platform compatibility for unistd.h. + * Provides functionality that may be missing on some platforms. + * Compatibility is not comprehensive. Only functionality required by + * nvme-cli and libnvme is included. + * + * Authors: Brandon Busacker + * Broc Going + */ +#pragma once + +#include + +#if defined(_WIN32) || defined(_WIN64) + +#define WIN32_LEAN_AND_MEAN /* keeps windows.h from including winsock.*/ +#include /* for gethostname */ + +#include + +/* unistd.h POSIX compatibility */ + +/* getpagesize implementation for Windows */ +static inline int getpagesize(void) +{ + SYSTEM_INFO si; + + GetSystemInfo(&si); + return si.dwPageSize; +} + +/* + * readlink stub - Windows doesn't have symbolic links in the same way + * NOTE: This is only used by micron-nvme.c, and can be removed once that + * has been refactored to not rely on Linux-specific sysfs paths. + */ +static inline ssize_t readlink(const char *path, char *buf, size_t bufsiz) +{ + errno = ENOTSUP; + return -1; +} + +/* fsync implementation for Windows */ +static inline int fsync(int fd) +{ + return _commit(fd); +} + +#endif diff --git a/libnvme/src/nvme/util.c b/libnvme/src/nvme/util.c index 97f45fd280..49c409cb57 100644 --- a/libnvme/src/nvme/util.c +++ b/libnvme/src/nvme/util.c @@ -19,17 +19,22 @@ #if defined(HAVE_NETDB) || defined(CONFIG_FABRICS) #include #include -#endif - #include #include +#endif + #include #include #include +#include +#include + #include #include +#include + #include #include "cleanup.h" @@ -43,6 +48,13 @@ #define LINE_MAX 2048 #endif +/* Windows errno.h doesn't define these*/ +#if defined (_WIN32) || defined(_WIN64) +#define EREMOTEIO 121 +#define EDQUOT 122 +#define ERESTART 85 +#endif + /* Source Code Control System, query version of binary with 'what' */ const char sccsid[] = "@(#)libnvme " GIT_VERSION; @@ -760,19 +772,55 @@ __public int libnvme_uuid_from_string(const char *str, unsigned char uuid[NVME_U } -__public int libnvme_random_uuid(unsigned char uuid[NVME_UUID_LEN]) +#if defined(_WIN32) || defined(_WIN64) + +#include + +/* Windows-specific UUID generation using BCryptGenRandom */ +static inline int random_uuid(unsigned char *uuid, size_t len) { - __cleanup_fd int f = -1; + NTSTATUS status; + + status = BCryptGenRandom(NULL, uuid, (ULONG)len, + BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (!BCRYPT_SUCCESS(status)) + return -EIO; + + return 0; +} + +#else + +/* Linux-specific UUID generation using /dev/urandom */ +static inline int random_uuid(unsigned char *uuid, size_t len) +{ + int f, ret = 0; ssize_t n; f = open("/dev/urandom", O_RDONLY); if (f < 0) return -errno; - n = read(f, uuid, NVME_UUID_LEN); + + n = read(f, uuid, len); if (n < 0) - return -errno; - else if (n != NVME_UUID_LEN) - return -EIO; + ret = -errno; + else if ((size_t)n != len) + ret = -EIO; + + close(f); + return ret; +} + +#endif + +__public int libnvme_random_uuid(unsigned char uuid[NVME_UUID_LEN]) +{ + int ret; + + /* Generate random bytes using platform-specific implementation */ + ret = random_uuid(uuid, NVME_UUID_LEN); + if (ret < 0) + return ret; /* * See https://www.rfc-editor.org/rfc/rfc4122#section-4.4 @@ -980,7 +1028,7 @@ void *__libnvme_realloc(void *p, size_t len) if (p && result) { memcpy(result, p, min(old_len, len)); - free(p); + aligned_free(p); } return result; diff --git a/libnvme/src/nvme/windows-stubs.c b/libnvme/src/nvme/windows-stubs.c new file mode 100644 index 0000000000..b57942664f --- /dev/null +++ b/libnvme/src/nvme/windows-stubs.c @@ -0,0 +1,653 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2025 Micron Technology, Inc. + * + * Windows stub implementations for Linux-specific functionality + * that is excluded from the Windows build (tree, filters, etc.) + */ + +#include +#include + +#include +#include + +#include "private.h" +#include "tree.h" +#include "compiler-attributes.h" + +/* Logging control for stub calls */ +static int stub_log_enabled = 0; + +static void stub_log(const char *func) +{ + if (stub_log_enabled) + fprintf(stderr, "libnvme-stub: %s() called (not supported on Windows)\n", func); +} + +void libnvme_stubs_set_debug(int enable) +{ + stub_log_enabled = enable; +} + +/* + * Stub implementations for tree functions (tree.c) + * Minimal support - just return NULL/errors + */ +void __libnvme_free_host(struct libnvme_host *h) +{ + stub_log(__func__); + (void)h; +} + +__public int libnvme_scan_ctrl(struct libnvme_global_ctx *ctx, const char *name, libnvme_ctrl_t *c) +{ + stub_log(__func__); + (void)ctx; + (void)name; + (void)c; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_scan_namespace(struct libnvme_global_ctx *ctx, + const char *name, libnvme_ns_t *ns) +{ + stub_log(__func__); + (void)ctx; + (void)name; + if (ns) + *ns = NULL; + return -ENOTSUP; +} + +__public int libnvme_scan_topology(struct libnvme_global_ctx *ctx, libnvme_scan_filter_t f, void *f_args) +{ + stub_log(__func__); + (void)ctx; + (void)f; + (void)f_args; + errno = ENOTSUP; + return -1; +} + +__public libnvme_host_t libnvme_first_host(struct libnvme_global_ctx *ctx) +{ + stub_log(__func__); + (void)ctx; + return NULL; +} + +__public libnvme_host_t libnvme_next_host(struct libnvme_global_ctx *ctx, libnvme_host_t h) +{ + stub_log(__func__); + (void)ctx; + (void)h; + return NULL; +} + +libnvme_host_t libnvme_lookup_host(struct libnvme_global_ctx *ctx, const char *hostnqn, const char *hostid) +{ + stub_log(__func__); + (void)ctx; + (void)hostnqn; + (void)hostid; + return NULL; +} + +__public libnvme_subsystem_t libnvme_first_subsystem(libnvme_host_t h) +{ + stub_log(__func__); + (void)h; + return NULL; +} + +__public libnvme_subsystem_t libnvme_next_subsystem(libnvme_host_t h, libnvme_subsystem_t s) +{ + stub_log(__func__); + (void)h; + (void)s; + return NULL; +} + +libnvme_subsystem_t libnvme_lookup_subsystem(struct libnvme_host *h, const char *name, const char *subsysnqn) +{ + stub_log(__func__); + (void)h; + (void)name; + (void)subsysnqn; + return NULL; +} + +libnvme_ctrl_t libnvme_lookup_ctrl(libnvme_subsystem_t s, + struct libnvmf_context *fctx, + libnvme_ctrl_t p) +{ + stub_log(__func__); + (void)s; + (void)fctx; + (void)p; + return NULL; +} + +__public libnvme_ns_t libnvme_ctrl_first_ns(libnvme_ctrl_t c) +{ + stub_log(__func__); + (void)c; + return NULL; +} + +__public libnvme_ns_t libnvme_ctrl_next_ns(libnvme_ctrl_t c, libnvme_ns_t n) +{ + stub_log(__func__); + (void)c; + (void)n; + return NULL; +} + +__public libnvme_path_t libnvme_ctrl_first_path(libnvme_ctrl_t c) +{ + stub_log(__func__); + (void)c; + return NULL; +} + +__public libnvme_path_t libnvme_ctrl_next_path(libnvme_ctrl_t c, libnvme_path_t p) +{ + stub_log(__func__); + (void)c; + (void)p; + return NULL; +} + +__public libnvme_subsystem_t libnvme_ns_get_subsystem(libnvme_ns_t n) +{ + stub_log(__func__); + (void)n; + return NULL; +} + +__public libnvme_ctrl_t libnvme_ns_get_ctrl(libnvme_ns_t n) +{ + stub_log(__func__); + (void)n; + return NULL; +} + +__public enum nvme_csi libnvme_ns_get_csi(libnvme_ns_t n) +{ + stub_log(__func__); + (void)n; + return 0; +} + +__public const uint8_t *libnvme_ns_get_eui64(libnvme_ns_t n) +{ + stub_log(__func__); + (void)n; + return NULL; +} + +__public const uint8_t *libnvme_ns_get_nguid(libnvme_ns_t n) +{ + stub_log(__func__); + (void)n; + return NULL; +} + +__public void libnvme_ns_get_uuid(libnvme_ns_t n, unsigned char out[NVME_UUID_LEN]) +{ + stub_log(__func__); + (void)n; + (void)out; +} + +int libnvme_ns_get_transport_handle(libnvme_ns_t n, + struct libnvme_transport_handle **hdl) +{ + stub_log(__func__); + (void)n; + (void)hdl; + return -ENOTSUP; +} + +__public int libnvme_ns_identify(libnvme_ns_t n, struct nvme_id_ns *ns) +{ + stub_log(__func__); + (void)n; + (void)ns; + errno = ENOTSUP; + return -1; +} + +__public const char *libnvme_ns_get_generic_name(libnvme_ns_t n) +{ + stub_log(__func__); + (void)n; + return ""; +} + +__public libnvme_ctrl_t libnvme_path_get_ctrl(libnvme_path_t p) +{ + stub_log(__func__); + (void)p; + return NULL; +} + +__public libnvme_ns_t libnvme_path_get_ns(libnvme_path_t p) +{ + stub_log(__func__); + (void)p; + return NULL; +} + +__public const char *libnvme_ctrl_get_state(libnvme_ctrl_t c) +{ + stub_log(__func__); + (void)c; + return ""; +} + +__public struct libnvme_fabrics_config *libnvme_ctrl_get_config(libnvme_ctrl_t c) +{ + stub_log(__func__); + (void)c; + return NULL; +} + +__public libnvme_subsystem_t libnvme_ctrl_get_subsystem(libnvme_ctrl_t c) +{ + stub_log(__func__); + (void)c; + return NULL; +} + +__public struct libnvme_transport_handle *libnvme_ctrl_get_transport_handle(libnvme_ctrl_t c) +{ + stub_log(__func__); + (void)c; + return NULL; +} + +__public void libnvme_free_ctrl(struct libnvme_ctrl *c) +{ + stub_log(__func__); + (void)c; +} + +__public int libnvme_disconnect_ctrl(libnvme_ctrl_t c) +{ + stub_log(__func__); + (void)c; + errno = ENOTSUP; + return -1; +} + +__public void libnvme_unlink_ctrl(struct libnvme_ctrl *c) +{ + stub_log(__func__); + (void)c; +} + +/* Subsystem functions (full name) - same as subsys */ +__public libnvme_host_t libnvme_subsystem_get_host(libnvme_subsystem_t s) +{ + stub_log(__func__); + (void)s; + return NULL; +} + +__public libnvme_ctrl_t libnvme_subsystem_first_ctrl(libnvme_subsystem_t s) +{ + stub_log(__func__); + (void)s; + return NULL; +} + +__public libnvme_ctrl_t libnvme_subsystem_next_ctrl(libnvme_subsystem_t s, libnvme_ctrl_t c) +{ + stub_log(__func__); + (void)s; + (void)c; + return NULL; +} + +__public libnvme_ns_t libnvme_subsystem_first_ns(libnvme_subsystem_t s) +{ + stub_log(__func__); + (void)s; + return NULL; +} + +__public libnvme_ns_t libnvme_subsystem_next_ns(libnvme_subsystem_t s, libnvme_ns_t n) +{ + stub_log(__func__); + (void)s; + (void)n; + return NULL; +} + +__public int libnvme_dump_tree(struct libnvme_global_ctx *ctx) +{ + stub_log(__func__); + (void)ctx; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_dump_config(struct libnvme_global_ctx *ctx, int fd) +{ + stub_log(__func__); + (void)ctx; + (void)fd; + errno = ENOTSUP; + return -1; +} + +/* + * TLS/PSK key management stubs (linux.c functions) + */ +__public int libnvme_export_tls_key_versioned(struct libnvme_global_ctx *ctx, + unsigned char version, unsigned char hmac, + const unsigned char *key_data, + size_t key_len, char **identity) +{ + stub_log(__func__); + (void)ctx; + (void)version; + (void)hmac; + (void)key_data; + (void)key_len; + (void)identity; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_export_tls_key(struct libnvme_global_ctx *ctx, + const unsigned char *key_data, int key_len, char **identity) +{ + stub_log(__func__); + (void)ctx; + (void)key_data; + (void)key_len; + (void)identity; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_import_tls_key_versioned(struct libnvme_global_ctx *ctx, + const char *encoded_key, + unsigned char *version, + unsigned char *hmac, + size_t *key_len, + unsigned char **key) +{ + stub_log(__func__); + (void)ctx; + (void)encoded_key; + (void)version; + (void)hmac; + (void)key_len; + (void)key; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_import_tls_key(struct libnvme_global_ctx *ctx, const char *encoded_key, + int *key_len, unsigned int *hmac, unsigned char **key) +{ + stub_log(__func__); + (void)ctx; + (void)encoded_key; + (void)key_len; + (void)hmac; + (void)key; + errno = ENOTSUP; + return -1; +} + +/* + * Additional stubs for nvme-cli linking + */ +/* Namespace property getters (tree.c) */ +__public const char *libnvme_ns_get_firmware(libnvme_ns_t n) +{ + stub_log(__func__); + (void)n; + return ""; +} + +__public const char *libnvme_ns_get_model(libnvme_ns_t n) +{ + stub_log(__func__); + (void)n; + return ""; +} + +__public const char *libnvme_ns_get_serial(libnvme_ns_t n) +{ + stub_log(__func__); + (void)n; + return ""; +} + +/* Namespace path iteration (tree.c) */ +__public libnvme_path_t libnvme_namespace_first_path(libnvme_ns_t ns) +{ + stub_log(__func__); + (void)ns; + return NULL; +} + +__public libnvme_path_t libnvme_namespace_next_path(libnvme_ns_t ns, libnvme_path_t p) +{ + stub_log(__func__); + (void)ns; + (void)p; + return NULL; +} + +/* + * Linux keyring and TLS key management stubs (linux.c) + * These are used by nvme-cli security commands + */ +__public int libnvme_read_key(struct libnvme_global_ctx *ctx, long keyring_id, + long key_id, int *len, unsigned char **key) +{ + stub_log(__func__); + (void)ctx; + (void)keyring_id; + (void)key_id; + (void)len; + (void)key; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_lookup_keyring(struct libnvme_global_ctx *ctx, + const char *keyring, long *key) +{ + stub_log(__func__); + (void)ctx; + (void)keyring; + (void)key; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_update_key(struct libnvme_global_ctx *ctx, long keyring_id, + const char *key_type, const char *identity, + unsigned char *key_data, int key_len, long *key) +{ + stub_log(__func__); + (void)ctx; + (void)keyring_id; + (void)key_type; + (void)identity; + (void)key_data; + (void)key_len; + (void)key; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_revoke_tls_key(struct libnvme_global_ctx *ctx, const char *keyring, + const char *key_type, const char *identity) +{ + stub_log(__func__); + (void)ctx; + (void)keyring; + (void)key_type; + (void)identity; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_scan_tls_keys(struct libnvme_global_ctx *ctx, const char *keyring, + libnvme_scan_tls_keys_cb_t cb, void *data) +{ + stub_log(__func__); + (void)ctx; + (void)keyring; + (void)cb; + (void)data; + errno = ENOTSUP; + return -1; +} + +__public char *libnvme_describe_key_serial(struct libnvme_global_ctx *ctx, + long key_id) +{ + stub_log(__func__); + (void)ctx; + (void)key_id; + return NULL; +} + +__public int libnvme_insert_tls_key_versioned(struct libnvme_global_ctx *ctx, + const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len, + long *key) +{ + stub_log(__func__); + (void)ctx; + (void)keyring; + (void)key_type; + (void)hostnqn; + (void)subsysnqn; + (void)version; + (void)hmac; + (void)configured_key; + (void)key_len; + (void)key; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_generate_tls_key_identity_compat(struct libnvme_global_ctx *ctx, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, unsigned char *configured_key, + int key_len, char **identity) +{ + stub_log(__func__); + (void)ctx; + (void)hostnqn; + (void)subsysnqn; + (void)version; + (void)hmac; + (void)configured_key; + (void)key_len; + (void)identity; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_insert_tls_key_compat(struct libnvme_global_ctx *ctx, + const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len, + long *key) +{ + stub_log(__func__); + (void)ctx; + (void)keyring; + (void)key_type; + (void)hostnqn; + (void)subsysnqn; + (void)version; + (void)hmac; + (void)configured_key; + (void)key_len; + (void)key; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_generate_tls_key_identity(struct libnvme_global_ctx *ctx, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len, + char **identity) +{ + stub_log(__func__); + (void)ctx; + (void)hostnqn; + (void)subsysnqn; + (void)version; + (void)hmac; + (void)configured_key; + (void)key_len; + (void)identity; + errno = ENOTSUP; + return -1; +} + +__public char *libnvme_read_hostnqn(void) +{ + stub_log(__func__); + /* No /etc/nvme/hostnqn equivalent on Windows */ + return NULL; +} + +__public int libnvme_gen_dhchap_key(struct libnvme_global_ctx *ctx, + char *hostnqn, enum libnvme_hmac_alg hmac, + unsigned int key_len, unsigned char *secret, + unsigned char *key) +{ + stub_log(__func__); + (void)ctx; + (void)hostnqn; + (void)hmac; + (void)key_len; + (void)secret; + (void)key; + errno = ENOTSUP; + return -1; +} + +__public int libnvme_create_raw_secret(struct libnvme_global_ctx *ctx, + const char *secret, size_t key_len, unsigned char **raw_secret) +{ + stub_log(__func__); + return -ENOTSUP; +} + +/* Hostnqn generation */ +__public char *libnvme_generate_hostnqn(void) +{ + stub_log(__func__); + /* Could implement UUID-based generation, but for now just fail */ + return NULL; +} + +/* Path property getters (tree.c) */ +__public int libnvme_path_get_queue_depth(struct libnvme_path *p) +{ + stub_log(__func__); + (void)p; + return 0; +} diff --git a/libnvme/test/ioctl/ana.c b/libnvme/test/ioctl/ana.c index f981993457..c363b210ce 100644 --- a/libnvme/test/ioctl/ana.c +++ b/libnvme/test/ioctl/ana.c @@ -11,9 +11,9 @@ #include #include "mock.h" +#include "nvme/types.h" #include "util.h" -#define TEST_FD 0xFD #define PDU_SIZE NVME_LOG_PAGE_PDU_SIZE static struct libnvme_transport_handle *test_hdl; diff --git a/libnvme/test/ioctl/discovery.c b/libnvme/test/ioctl/discovery.c index 6e99e47695..ddf9dab6ef 100644 --- a/libnvme/test/ioctl/discovery.c +++ b/libnvme/test/ioctl/discovery.c @@ -13,7 +13,6 @@ #include "mock.h" #include "util.h" -#define TEST_FD 0xFD #define HEADER_LEN 20 static struct libnvme_transport_handle *test_hdl; diff --git a/libnvme/test/ioctl/features.c b/libnvme/test/ioctl/features.c index 20db4f7389..36b356f38b 100644 --- a/libnvme/test/ioctl/features.c +++ b/libnvme/test/ioctl/features.c @@ -8,7 +8,6 @@ #include "mock.h" #include "util.h" -#define TEST_FD 0xFD #define TEST_TIMEOUT 1234 #define TEST_NSID 0x89ABCDEF #define TEST_CDW11 0x11111111 diff --git a/libnvme/test/ioctl/identify.c b/libnvme/test/ioctl/identify.c index 7fc679df89..d2699c5b47 100644 --- a/libnvme/test/ioctl/identify.c +++ b/libnvme/test/ioctl/identify.c @@ -8,7 +8,6 @@ #include "mock.h" #include "util.h" -#define TEST_FD 0xFD #define TEST_NSID 0x12345678 #define TEST_NVMSETID 0xABCD #define TEST_UUID 123 diff --git a/libnvme/test/ioctl/logs.c b/libnvme/test/ioctl/logs.c index a5c63d04c6..ac98435b51 100644 --- a/libnvme/test/ioctl/logs.c +++ b/libnvme/test/ioctl/logs.c @@ -5,7 +5,6 @@ #include "mock.h" #include "util.h" -#define TEST_FD 0xFD #define TEST_NSID 0x12345678 #define TEST_NVMSETID 0xABCD #define TEST_CSI NVME_CSI_KV diff --git a/libnvme/test/ioctl/mock.c b/libnvme/test/ioctl/mock.c index 894968cf8f..a656b2c804 100644 --- a/libnvme/test/ioctl/mock.c +++ b/libnvme/test/ioctl/mock.c @@ -21,7 +21,7 @@ struct mock_cmds { size_t remaining_cmds; }; -static int mock_fd = -1; +static libnvme_fd_t mock_fd = INIT_FD; static struct mock_cmds mock_admin_cmds = {.name = "admin"}; static struct mock_cmds mock_io_cmds = {.name = "IO"}; @@ -39,7 +39,7 @@ static void mock_cmds_done(const struct mock_cmds *mock_cmds) mock_cmds->remaining_cmds, mock_cmds->name); } -void set_mock_fd(int fd) +void set_mock_fd(libnvme_fd_t fd) { mock_fd = fd; } @@ -122,11 +122,11 @@ void end_mock_cmds(void) }) #if defined(HAVE_GLIBC_IOCTL) && HAVE_GLIBC_IOCTL == 1 -typedef int (*ioctl_func_t)(int, unsigned long, void *); -int ioctl(int fd, unsigned long request, ...) +typedef int (*ioctl_func_t)(libnvme_fd_t, unsigned long, void *); +int ioctl(libnvme_fd_t fd, unsigned long request, ...) #else -typedef int (*ioctl_func_t)(int, int, void *); -int ioctl(int fd, int request, ...) +typedef int (*ioctl_func_t)(libnvme_fd_t, int, void *); +int ioctl(libnvme_fd_t fd, int request, ...) #endif { ioctl_func_t real_ioctl = NULL; diff --git a/libnvme/test/ioctl/mock.h b/libnvme/test/ioctl/mock.h index c4b4bd6ac1..8304f76921 100644 --- a/libnvme/test/ioctl/mock.h +++ b/libnvme/test/ioctl/mock.h @@ -61,7 +61,7 @@ struct mock_cmd { * set_mock_fd() - sets the expected file descriptor for NVMe passthru ioctls() * @fd: file descriptor expected to be passed to ioctl() */ -void set_mock_fd(int fd); +void set_mock_fd(libnvme_fd_t fd); /** * set_mock_admin_cmds() - mocks NVMe admin passthru ioctl() invocations diff --git a/libnvme/test/meson.build b/libnvme/test/meson.build index bdb815498c..82f1cf48ea 100644 --- a/libnvme/test/meson.build +++ b/libnvme/test/meson.build @@ -57,25 +57,27 @@ if cxx_available endif -register = executable( - 'test-register', - ['register.c'], - dependencies: [ - config_dep, - ccan_dep, - libnvme_dep, - ], -) +if host_system != 'windows' + register = executable( + 'test-register', + ['register.c'], + dependencies: [ + config_dep, + ccan_dep, + libnvme_dep, + ], + ) -zns = executable( - 'test-zns', - ['zns.c'], - dependencies: [ - config_dep, - ccan_dep, - libnvme_test_dep, - ], -) + zns = executable( + 'test-zns', + ['zns.c'], + dependencies: [ + config_dep, + ccan_dep, + libnvme_test_dep, + ], + ) +endif # host_system != 'windows' if want_mi mi = executable( @@ -148,71 +150,75 @@ if want_fabrics test('libnvme - fabrics', test_fabrics) endif -if conf.get('HAVE_NETDB') - mock_ifaddrs = library( - 'mock-ifaddrs', - ['mock-ifaddrs.c', ], - ) - - # See comment in test/ioctl/meson.build explaining how LD_PRELOAD is used - mock_ifaddrs_env = environment() - mock_ifaddrs_env.append('LD_PRELOAD', mock_ifaddrs.full_path()) - mock_ifaddrs_env.set('ASAN_OPTIONS', 'verify_asan_link_order=0') - - tree = executable( - 'tree', - ['tree.c'], - dependencies: [ - config_dep, - ccan_dep, - libnvme_test_dep, - ], - link_with: mock_ifaddrs, - ) - - test('libnvme - tree', tree, env: mock_ifaddrs_env) - - test_util = executable( - 'test-util', - ['test-util.c'], +if host_system != 'windows' + + if conf.get('HAVE_NETDB') + mock_ifaddrs = library( + 'mock-ifaddrs', + ['mock-ifaddrs.c', ], + ) + + # See comment in test/ioctl/meson.build explaining how LD_PRELOAD is used + mock_ifaddrs_env = environment() + mock_ifaddrs_env.append('LD_PRELOAD', mock_ifaddrs.full_path()) + mock_ifaddrs_env.set('ASAN_OPTIONS', 'verify_asan_link_order=0') + + tree = executable( + 'tree', + ['tree.c'], + dependencies: [ + config_dep, + ccan_dep, + libnvme_test_dep, + ], + link_with: mock_ifaddrs, + ) + + test('libnvme - tree', tree, env: mock_ifaddrs_env) + + test_util = executable( + 'test-util', + ['test-util.c'], + dependencies: [ + config_dep, + ccan_dep, + libnvme_test_dep, + ], + ) + test('libnvme - util', test_util) + endif + + psk = executable( + 'test-psk', + ['psk.c'], dependencies: [ config_dep, ccan_dep, - libnvme_test_dep, + libnvme_dep, ], ) - test('libnvme - util', test_util) -endif -psk = executable( - 'test-psk', - ['psk.c'], - dependencies: [ - config_dep, - ccan_dep, - libnvme_dep, - ], -) + test('libnvme - psk', psk) -test('libnvme - psk', psk) + subdir('ioctl') + if want_fabrics + subdir('nbft') + endif -subdir('ioctl') -if want_fabrics - subdir('nbft') -endif + if json_c_dep.found() + subdir('sysfs') + subdir('config') + endif -if json_c_dep.found() - subdir('sysfs') - subdir('config') -endif + if openssl_dep.found() + hkdf_add1 = executable( + 'hkdf_add1', + ['hkdf_add1.c'], + dependencies: openssl_dep, + ) + endif -if openssl_dep.found() - hkdf_add1 = executable( - 'hkdf_add1', - ['hkdf_add1.c'], - dependencies: openssl_dep, - ) -endif +endif # host_system != 'windows' foreach hdr : [ 'endian', diff --git a/libnvme/test/nbft/meson.build b/libnvme/test/nbft/meson.build index 79b082a839..fc3b9bfa63 100644 --- a/libnvme/test/nbft/meson.build +++ b/libnvme/test/nbft/meson.build @@ -41,6 +41,8 @@ nbft_dump = executable( ], ) +# Shell-based diff tests only work on Linux/Unix systems +if host_system != 'windows' helper_data = configuration_data() helper_data.set('NBFT_DUMP_PATH', nbft_dump.full_path()) @@ -94,3 +96,5 @@ foreach table: tables_bad should_fail: true, ) endforeach + +endif # Unix-only shell script tests \ No newline at end of file diff --git a/logging.c b/logging.c index 3f4ddad463..9f4691541c 100644 --- a/logging.c +++ b/logging.c @@ -6,7 +6,6 @@ #include #include -#include #include #include diff --git a/meson.build b/meson.build index 1699f3e227..61a91da1ca 100644 --- a/meson.build +++ b/meson.build @@ -23,7 +23,6 @@ project( default_options: [ 'c_std=gnu99', 'buildtype=debugoptimized', - 'prefix=/usr/local', 'warning_level=1', 'sysconfdir=etc', 'wrap_mode=nofallback', @@ -56,7 +55,7 @@ want_fabrics = get_option('fabrics').disabled() == false and host_system != 'win want_mi = get_option('mi').disabled() == false and host_system != 'windows' want_json_c = get_option('json-c').disabled() == false and want_fabrics want_libkmod = get_option('libkmod').disabled() == false and host_system != 'windows' -want_tests = get_option('tests') and host_system != 'windows' +want_tests = get_option('tests') want_examples = get_option('examples') and host_system != 'windows' want_docs = get_option('docs') want_docs_build = get_option('docs-build') @@ -144,7 +143,9 @@ conf.set('CONFIG_FABRICS', want_fabrics, description: 'Is fabrics enabled') conf.set('CONFIG_MI', want_mi, description: 'Is MI enabled') if host_system == 'windows' + ws2_32_dep = cc.find_library('ws2_32', required: true) kernel32_dep = cc.find_library('kernel32', required: true) + bcrypt_dep = cc.find_library('bcrypt', required: true) # For BCryptGenRandom in nvme_uuid_random endif # Check for libjson-c availability @@ -171,6 +172,17 @@ conf.set('LIBNVME_VERSION', '"@0@"'.format(meson.project_version())) conf.set10('DEFAULT_PDC_ENABLED', get_option('pdc-enabled')) +# Windows-specific compiler flags +if host_system == 'windows' + add_project_arguments( + [ + '-DHAVE_GLIBC_IOCTL=0', + '-D_POSIX_THREAD_SAFE_FUNCTIONS', + ], + language : 'c', + ) +endif + # local (cross-compilable) implementations of ccan configure steps conf.set10( 'HAVE_BUILTIN_TYPES_COMPATIBLE_P', @@ -416,6 +428,8 @@ conf.set( conf.set('NVME_HAVE_SENDFILE', cc.has_function('sendfile')) +conf.set('HAVE_MMAP', cc.has_function('mmap')) + project_config_file = 'nvme-config.h' nvme_config_h = configure_file(output: project_config_file, configuration: conf) config_dep = declare_dependency( @@ -495,30 +509,31 @@ if want_nvme subdir('plugins') # declares: plugin_sources subdir('util') # declares: util_sources - sources = [] + sources = [ + 'logging.c', + 'nvme-cmds.c', + 'nvme-models.c', + 'nvme-print-binary.c', + 'nvme-print-stdout.c', + 'nvme-print.c', + 'nvme.c', + 'plugin.c', + ] if host_system == 'windows' sources += [ - 'nvme-dummy.c', # Dummy source file for Windows port bring up. + 'windows-stubs.c', ] else sources += [ 'libnvme-wrap.c', - 'logging.c', - 'nvme-cmds.c', - 'nvme-models.c', - 'nvme-print-binary.c', - 'nvme-print-stdout.c', - 'nvme-print.c', 'nvme-rpmb.c', - 'nvme.c', - 'plugin.c', ] + endif - if json_c_dep.found() - sources += [ - 'nvme-print-json.c', - ] - endif + if json_c_dep.found() + sources += [ + 'nvme-print-json.c', + ] endif if want_fabrics @@ -539,13 +554,14 @@ if want_nvme if host_system == 'windows' link_deps += [ + ws2_32_dep, kernel32_dep, ] else link_args_list = ['-ldl'] endif - executable( + nvme_exe = executable( 'nvme', sources, dependencies: link_deps, @@ -554,6 +570,19 @@ if want_nvme install_dir: sbindir, ) + if host_system == 'windows' and want_libnvme and not is_static + # Copy libnvme DLL next to nvme.exe so it can be found at + # runtime without modifying PATH. + cp = find_program('cp') + custom_target('copy-libnvme-dll', + input: libnvme, + output: fs.name(libnvme.full_path()), + command: [cp, '@INPUT@', '@OUTPUT@'], + depends: [libnvme, nvme_exe], + build_by_default: true, + ) + endif + if host_system != 'windows' subdir('unit') endif @@ -693,7 +722,9 @@ dep_dict = { } if host_system == 'windows' dep_dict += { + 'ws2_32': ws2_32_dep.found(), 'kernel32': kernel32_dep.found(), + 'bcrypt': bcrypt_dep.found(), } endif summary(dep_dict, section: 'Dependencies', bool_yn: true) diff --git a/nvme-dummy.c b/nvme-dummy.c deleted file mode 100644 index 15cf908226..0000000000 --- a/nvme-dummy.c +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -int main(int argc, char **argv) -{ - printf("This is a dummy executable for windows port bring up.\n"); - return 0; -} diff --git a/nvme-models.c b/nvme-models.c index 4a94906deb..6dbdf6fc9c 100644 --- a/nvme-models.c +++ b/nvme-models.c @@ -9,6 +9,9 @@ #include #include + +#include + #include "nvme-models.h" #include "nvme.h" diff --git a/nvme-print-json.c b/nvme-print-json.c index e5b9a304a2..66b64190b6 100644 --- a/nvme-print-json.c +++ b/nvme-print-json.c @@ -4,8 +4,12 @@ #include #include #include + +#ifdef CONFIG_FABRICS #include #include +#endif + #include #include @@ -1869,7 +1873,7 @@ void nvme_json_pel_vendor_specific_event(void *pevent_log_info, __u32 offset, { __u32 progress = 0; __u16 vsedl; - uint i; + __u32 i; struct nvme_vs_event_desc *vs_desc; struct json_object *vs_events = json_create_array(); diff --git a/nvme-print-stdout.c b/nvme-print-stdout.c index acdf1d7749..837f22e1dc 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -7,8 +7,14 @@ #include #include #include + +#ifdef CONFIG_FABRICS #include #include +#endif + +#include + #include #include #include @@ -1630,9 +1636,15 @@ static void stdout_registers_pmrmscu(uint32_t pmrmscu) static void stdout_ctrl_register_human(int offset, uint64_t value, bool support) { switch (offset) { - case NVME_REG_CAP: - stdout_registers_cap((struct nvme_bar_cap *)&value); + case NVME_REG_CAP: { + union { + uint64_t raw; + struct nvme_bar_cap cap; + } u = {}; + u.raw = value; + stdout_registers_cap(&u.cap); break; + } case NVME_REG_VS: stdout_registers_version(value); break; diff --git a/nvme-rpmb.c b/nvme-rpmb.c index a68e488039..913a45720a 100644 --- a/nvme-rpmb.c +++ b/nvme-rpmb.c @@ -194,7 +194,7 @@ static int read_file(const char *file, unsigned char **data, unsigned int *len) err = -errno; fprintf(stderr, "Failed to read data from file" " %s with %s\n", file, libnvme_strerror(errno)); - free(buf); + aligned_free(buf); goto out; } *data = buf; diff --git a/nvme.c b/nvme.c index 69bdb47751..a994c3a387 100644 --- a/nvme.c +++ b/nvme.c @@ -39,14 +39,13 @@ #include #include -#include - -#include +#ifdef HAVE_MMAP #include +#endif + #include #include - #include #include "common.h" @@ -257,6 +256,7 @@ struct nvme_args nvme_args = { }; static void *mmap_registers(struct libnvme_transport_handle *hdl, bool writable); +static int munmap_registers(void *addr); static OPT_VALS(feature_name) = { VAL_BYTE("arbitration", NVME_FEAT_FID_ARBITRATION), @@ -492,7 +492,7 @@ static int get_smart_log(int argc, char **argv, struct command *acmd, struct plu "(or optionally a namespace) in either decoded format " "(default) or binary."; - __cleanup_free struct nvme_smart_log *smart_log = NULL; + __cleanup_nvme_free struct nvme_smart_log *smart_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; const char *namespace = "(optional) desired namespace"; @@ -557,8 +557,8 @@ static int get_ana_log(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free struct nvme_id_ctrl *ctrl = NULL; - __cleanup_free struct nvme_ana_log *ana_log = NULL; + __cleanup_nvme_free struct nvme_id_ctrl *ctrl = NULL; + __cleanup_nvme_free struct nvme_ana_log *ana_log = NULL; size_t max_ana_log_len; __u32 ana_log_len; nvme_print_flags_t flags; @@ -681,7 +681,7 @@ static int get_log_telemetry_ctrl(struct libnvme_transport_handle *hdl, bool rae err = nvme_get_log_telemetry_ctrl(hdl, rae, 0, log, size); if (err) { - free(log); + nvme_free(log); return err; } @@ -701,7 +701,7 @@ static int get_log_telemetry_host(struct libnvme_transport_handle *hdl, size_t s err = nvme_get_log_telemetry_host(hdl, 0, log, size); if (err) { - free(log); + nvme_free(log); return err; } @@ -715,7 +715,7 @@ static int __create_telemetry_log_host(struct libnvme_transport_handle *hdl, struct nvme_telemetry_log **buf, bool da4_support) { - __cleanup_free struct nvme_telemetry_log *log = NULL; + __cleanup_nvme_free struct nvme_telemetry_log *log = NULL; int err; log = nvme_alloc(sizeof(*log)); @@ -777,7 +777,7 @@ static int __get_telemetry_log_ctrl(struct libnvme_transport_handle *hdl, return get_log_telemetry_ctrl(hdl, rae, *size, buf); free: - free(log); + nvme_free(log); return err; } @@ -787,7 +787,7 @@ static int __get_telemetry_log_host(struct libnvme_transport_handle *hdl, struct nvme_telemetry_log **buf, bool da4_support) { - __cleanup_free struct nvme_telemetry_log *log = NULL; + __cleanup_nvme_free struct nvme_telemetry_log *log = NULL; int err; log = nvme_alloc(sizeof(*log)); @@ -817,8 +817,8 @@ static int get_telemetry_log(int argc, char **argv, struct command *acmd, const char *mcda = "Host-init Maximum Created Data Area. Valid options are 0 ~ 4 " "If given, This option will override dgen. 0 : controller determines data area"; - __cleanup_free struct nvme_telemetry_log *log = NULL; - __cleanup_free struct nvme_id_ctrl *id_ctrl = NULL; + __cleanup_nvme_free struct nvme_telemetry_log *log = NULL; + __cleanup_nvme_free struct nvme_id_ctrl *id_ctrl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_fd int output = -1; @@ -981,7 +981,7 @@ static int get_endurance_log(int argc, char **argv, struct command *acmd, struct const char *desc = "Retrieves endurance groups log page and prints the log."; const char *group_id = "The endurance group identifier"; - __cleanup_free struct nvme_endurance_group_log *endurance_log = NULL; + __cleanup_nvme_free struct nvme_endurance_group_log *endurance_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -1039,7 +1039,7 @@ static int collect_effects_log(struct libnvme_transport_handle *hdl, enum nvme_c err = nvme_get_log_cmd_effects(hdl, csi, &node->effects); if (err) { - free(node); + nvme_free(node); return err; } list_add(list, &node->node); @@ -1107,7 +1107,7 @@ static int get_effects_log(int argc, char **argv, struct command *acmd, struct p if (bar) { cap = mmio_read64(bar + NVME_REG_CAP); - munmap(bar, getpagesize()); + munmap_registers(bar); } else { nvme_init_get_property(&cmd, NVME_REG_CAP); err = libnvme_submit_admin_passthru(hdl, &cmd); @@ -1146,7 +1146,7 @@ static int get_supported_log_pages(int argc, char **argv, struct command *acmd, { const char *desc = "Retrieve supported logs and print the table."; - __cleanup_free struct nvme_supported_log_pages *supports = NULL; + __cleanup_nvme_free struct nvme_supported_log_pages *supports = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -1191,7 +1191,7 @@ static int get_error_log(int argc, char **argv, struct command *acmd, struct plu const char *log_entries = "number of entries to retrieve"; const char *raw = "dump in binary format"; - __cleanup_free struct nvme_error_log_page *err_log = NULL; + __cleanup_nvme_free struct nvme_error_log_page *err_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct nvme_id_ctrl ctrl = { 0 }; @@ -1261,7 +1261,7 @@ static int get_fw_log(int argc, char **argv, struct command *acmd, struct plugin const char *desc = "Retrieve the firmware log for the " "specified device in either decoded format (default) or binary."; - __cleanup_free struct nvme_firmware_slot *fw_log = NULL; + __cleanup_nvme_free struct nvme_firmware_slot *fw_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -1309,7 +1309,7 @@ static int get_fw_log(int argc, char **argv, struct command *acmd, struct plugin static int get_changed_ns_list_log(int argc, char **argv, bool alloc) { __cleanup_free char *desc = NULL; - __cleanup_free struct nvme_ns_list *changed_ns_list_log = NULL; + __cleanup_nvme_free struct nvme_ns_list *changed_ns_list_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -1387,7 +1387,7 @@ static int get_pred_lat_per_nvmset_log(int argc, char **argv, "format(default),json or binary."; const char *nvmset_id = "NVM Set Identifier"; - __cleanup_free struct nvme_nvmset_predictable_lat_log *plpns_log = NULL; + __cleanup_nvme_free struct nvme_nvmset_predictable_lat_log *plpns_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -1447,8 +1447,8 @@ static int get_pred_lat_event_agg_log(int argc, char **argv, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free struct nvme_id_ctrl *ctrl = NULL; - __cleanup_free void *pea_log = NULL; + __cleanup_nvme_free struct nvme_id_ctrl *ctrl = NULL; + __cleanup_nvme_free void *pea_log = NULL; nvme_print_flags_t flags; __u32 log_size; int err; @@ -1528,7 +1528,7 @@ static int get_persistent_event_log(int argc, char **argv, "processing this persistent log page command."; const char *log_len = "number of bytes to retrieve"; - __cleanup_free struct nvme_persistent_event_log *pevent = NULL; + __cleanup_nvme_free struct nvme_persistent_event_log *pevent = NULL; struct nvme_persistent_event_log *pevent_collected = NULL; __cleanup_huge struct nvme_mem_huge mh = { 0, }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; @@ -1643,8 +1643,8 @@ static int get_endurance_event_agg_log(int argc, char **argv, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free struct nvme_id_ctrl *ctrl = NULL; - __cleanup_free void *endurance_log = NULL; + __cleanup_nvme_free struct nvme_id_ctrl *ctrl = NULL; + __cleanup_nvme_free void *endurance_log = NULL; nvme_print_flags_t flags; __u32 log_size; int err; @@ -1725,7 +1725,7 @@ static int get_lba_status_log(int argc, char **argv, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free void *lba_status = NULL; + __cleanup_nvme_free void *lba_status = NULL; nvme_print_flags_t flags; __u32 lslplen; int err; @@ -1781,7 +1781,7 @@ static int get_resv_notif_log(int argc, char **argv, "log page and prints it, for the given " "device in either decoded format(default), json or binary."; - __cleanup_free struct nvme_resv_notification_log *resv = NULL; + __cleanup_nvme_free struct nvme_resv_notification_log *resv = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -1825,8 +1825,8 @@ static int get_boot_part_log(int argc, char **argv, struct command *acmd, struct __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free struct nvme_boot_partition *boot = NULL; - __cleanup_free __u8 *bp_log = NULL; + __cleanup_nvme_free struct nvme_boot_partition *boot = NULL; + __cleanup_nvme_free __u8 *bp_log = NULL; nvme_print_flags_t flags; int err = -1; __cleanup_fd int output = -1; @@ -1914,7 +1914,7 @@ static int get_phy_rx_eom_log(int argc, char **argv, struct command *acmd, "Measurement log for the given device in decoded format " "(default), json or binary."; const char *controller = "Target Controller ID."; - __cleanup_free struct nvme_phy_rx_eom_log *phy_rx_eom_log = NULL; + __cleanup_nvme_free struct nvme_phy_rx_eom_log *phy_rx_eom_log = NULL; size_t phy_rx_eom_log_len; nvme_print_flags_t flags; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; @@ -2001,7 +2001,7 @@ static int get_media_unit_stat_log(int argc, char **argv, struct command *acmd, { const char *desc = "Retrieve the configuration and wear of media units and print it"; - __cleanup_free struct nvme_media_unit_stat_log *mus = NULL; + __cleanup_nvme_free struct nvme_media_unit_stat_log *mus = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -2054,7 +2054,7 @@ static int get_supp_cap_config_log(int argc, char **argv, struct command *acmd, { const char *desc = "Retrieve the list of Supported Capacity Configuration Descriptors"; - __cleanup_free struct nvme_supported_cap_config_list_log *cap_log = NULL; + __cleanup_nvme_free struct nvme_supported_cap_config_list_log *cap_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -2110,7 +2110,7 @@ static int io_mgmt_send(int argc, char **argv, struct command *acmd, struct plug __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_fd int dfd = STDIN_FILENO; - __cleanup_free void *buf = NULL; + __cleanup_nvme_free void *buf = NULL; struct libnvme_passthru_cmd cmd; int err = -1; @@ -2185,7 +2185,7 @@ static int io_mgmt_recv(int argc, char **argv, struct command *acmd, struct plug __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; - __cleanup_free void *buf = NULL; + __cleanup_nvme_free void *buf = NULL; struct libnvme_passthru_cmd cmd; __cleanup_fd int dfd = -1; int err = -1; @@ -2273,7 +2273,7 @@ static int get_log(int argc, char **argv, struct command *acmd, struct plugin *p __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free unsigned char *log = NULL; + __cleanup_nvme_free unsigned char *log = NULL; struct libnvme_passthru_cmd cmd; int err; nvme_print_flags_t flags; @@ -2475,7 +2475,7 @@ static int sanitize_log(int argc, char **argv, struct command *acmd, struct plug { const char *desc = "Retrieve sanitize log and show it."; - __cleanup_free struct nvme_sanitize_log_page *sanitize_log = NULL; + __cleanup_nvme_free struct nvme_sanitize_log_page *sanitize_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -2535,7 +2535,7 @@ static int get_fid_support_effects_log(int argc, char **argv, struct command *ac { const char *desc = "Retrieve FID Support and Effects log and show it."; - __cleanup_free struct nvme_fid_supported_effects_log *fid_support_log = NULL; + __cleanup_nvme_free struct nvme_fid_supported_effects_log *fid_support_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -2587,7 +2587,7 @@ static int get_mi_cmd_support_effects_log(int argc, char **argv, struct command { const char *desc = "Retrieve NVMe-MI Command Support and Effects log and show it."; - __cleanup_free struct nvme_mi_cmd_supported_effects_log *mi_cmd_support_log = NULL; + __cleanup_nvme_free struct nvme_mi_cmd_supported_effects_log *mi_cmd_support_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -2639,7 +2639,7 @@ static int list_ctrl(int argc, char **argv, struct command *acmd, struct plugin "given device is part of, or optionally controllers attached to a specific namespace."; const char *controller = "controller to display"; - __cleanup_free struct nvme_ctrl_list *cntlist = NULL; + __cleanup_nvme_free struct nvme_ctrl_list *cntlist = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; @@ -2699,7 +2699,7 @@ static int list_ns(int argc, char **argv, struct command *acmd, struct plugin *p const char *csi = "I/O command set identifier"; const char *all = "show all namespaces in the subsystem, whether attached or inactive"; - __cleanup_free struct nvme_ns_list *ns_list = NULL; + __cleanup_nvme_free struct nvme_ns_list *ns_list = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; enum nvme_identify_cns cns; @@ -2774,7 +2774,7 @@ static int id_ns_lba_format(int argc, char **argv, struct command *acmd, struct __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free struct nvme_id_ns *ns = NULL; + __cleanup_nvme_free struct nvme_id_ns *ns = NULL; nvme_print_flags_t flags; int err = -1; @@ -2829,7 +2829,7 @@ static int id_endurance_grp_list(int argc, char **argv, struct command *acmd, const char *desc = "Show endurance group list information for the given endurance group id"; const char *endurance_grp_id = "Endurance Group ID"; - __cleanup_free struct nvme_id_endurance_group_list *endgrp_list = NULL; + __cleanup_nvme_free struct nvme_id_endurance_group_list *endgrp_list = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; @@ -2878,7 +2878,7 @@ static bool is_ns_mgmt_support(struct libnvme_transport_handle *hdl) { int err; - __cleanup_free struct nvme_id_ctrl *ctrl = nvme_alloc(sizeof(*ctrl)); + __cleanup_nvme_free struct nvme_id_ctrl *ctrl = nvme_alloc(sizeof(*ctrl)); if (ctrl) return false; @@ -2977,7 +2977,7 @@ static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, s __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; - __cleanup_free struct nvme_ctrl_list *cntlist = NULL; + __cleanup_nvme_free struct nvme_ctrl_list *cntlist = NULL; __u16 list[NVME_ID_CTRL_LIST_MAX]; struct libnvme_passthru_cmd cmd; nvme_print_flags_t flags; @@ -3090,9 +3090,9 @@ static int detach_ns(int argc, char **argv, struct command *acmd, struct plugin static int parse_lba_num_si(struct libnvme_transport_handle *hdl, const char *opt, const char *val, __u8 flbas, __u64 *num, __u64 align) { - __cleanup_free struct nvme_ns_list *ns_list = NULL; - __cleanup_free struct nvme_id_ctrl *ctrl = NULL; - __cleanup_free struct nvme_id_ns *ns = NULL; + __cleanup_nvme_free struct nvme_ns_list *ns_list = NULL; + __cleanup_nvme_free struct nvme_id_ctrl *ctrl = NULL; + __cleanup_nvme_free struct nvme_id_ns *ns = NULL; __u32 nsid = 1; __u8 lbaf; unsigned int remainder; @@ -3196,11 +3196,11 @@ static int create_ns(int argc, char **argv, struct command *acmd, struct plugin const char *phndls = "Comma separated list of Placement Handle Associated RUH"; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free struct nvme_ns_mgmt_host_sw_specified *data = NULL; - __cleanup_free struct nvme_id_ns_granularity_list *gr_list = NULL; + __cleanup_nvme_free struct nvme_ns_mgmt_host_sw_specified *data = NULL; + __cleanup_nvme_free struct nvme_id_ns_granularity_list *gr_list = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; - __cleanup_free struct nvme_id_ctrl *id = NULL; - __cleanup_free struct nvme_id_ns *ns = NULL; + __cleanup_nvme_free struct nvme_id_ctrl *id = NULL; + __cleanup_nvme_free struct nvme_id_ns *ns = NULL; __u64 align_nsze = 1 << 20; /* Default 1 MiB */ __u64 align_ncap = align_nsze; struct libnvme_passthru_cmd cmd; @@ -3581,7 +3581,7 @@ int __id_ctrl(int argc, char **argv, struct command *acmd, struct plugin *plugin "controller attributes in hex-dump if requested."; const char *vendor_specific = "dump binary vendor field"; - __cleanup_free struct nvme_id_ctrl *ctrl = NULL; + __cleanup_nvme_free struct nvme_id_ctrl *ctrl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -3650,7 +3650,7 @@ static int nvm_id_ctrl(int argc, char **argv, struct command *acmd, "command to the given device and report information about " "the specified controller in various formats."; - __cleanup_free struct nvme_id_ctrl_nvm *ctrl_nvm = NULL; + __cleanup_nvme_free struct nvme_id_ctrl_nvm *ctrl_nvm = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; @@ -3695,8 +3695,8 @@ static int nvm_id_ns(int argc, char **argv, struct command *acmd, "command to the given device and report information about " "the specified namespace in various formats."; - __cleanup_free struct nvme_nvm_id_ns *id_ns = NULL; - __cleanup_free struct nvme_id_ns *ns = NULL; + __cleanup_nvme_free struct nvme_nvm_id_ns *id_ns = NULL; + __cleanup_nvme_free struct nvme_id_ns *ns = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -3769,8 +3769,8 @@ static int nvm_id_ns_lba_format(int argc, char **argv, struct command *acmd, str "command to the given device, returns capability field properties of " "the specified LBA Format index in the specified namespace in various formats."; - __cleanup_free struct nvme_nvm_id_ns *nvm_ns = NULL; - __cleanup_free struct nvme_id_ns *ns = NULL; + __cleanup_nvme_free struct nvme_nvm_id_ns *nvm_ns = NULL; + __cleanup_nvme_free struct nvme_id_ns *ns = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -3840,7 +3840,7 @@ static int ns_descs(int argc, char **argv, struct command *acmd, struct plugin * __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free void *nsdescs = NULL; + __cleanup_nvme_free void *nsdescs = NULL; nvme_print_flags_t flags; int err; @@ -3908,7 +3908,7 @@ static int id_ns(int argc, char **argv, struct command *acmd, struct plugin *plu __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free struct nvme_id_ns *ns = NULL; + __cleanup_nvme_free struct nvme_id_ns *ns = NULL; struct libnvme_passthru_cmd cmd; nvme_print_flags_t flags; int err; @@ -3991,7 +3991,7 @@ static int cmd_set_independent_id_ns(int argc, char **argv, struct command *acmd "Namespace command to the given device, returns properties of the " "specified namespace in human-readable or binary or json format."; - __cleanup_free struct nvme_id_independent_id_ns *ns = NULL; + __cleanup_nvme_free struct nvme_id_independent_id_ns *ns = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; @@ -4063,7 +4063,7 @@ static int id_ns_granularity(int argc, char **argv, struct command *acmd, struct "given device, returns namespace granularity list " "in either human-readable or binary format."; - __cleanup_free struct nvme_id_ns_granularity_list *granularity_list = NULL; + __cleanup_nvme_free struct nvme_id_ns_granularity_list *granularity_list = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -4104,7 +4104,7 @@ static int id_nvmset(int argc, char **argv, struct command *acmd, struct plugin "in either binary format or json format"; const char *nvmset_id = "NVM Set Identify value"; - __cleanup_free struct nvme_id_nvmset_list *nvmset = NULL; + __cleanup_nvme_free struct nvme_id_nvmset_list *nvmset = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; @@ -4157,7 +4157,7 @@ static int id_uuid(int argc, char **argv, struct command *acmd, struct plugin *p const char *raw = "show uuid in binary format"; const char *human_readable = "show uuid in readable format"; - __cleanup_free struct nvme_id_uuid_list *uuid_list = NULL; + __cleanup_nvme_free struct nvme_id_uuid_list *uuid_list = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -4215,7 +4215,7 @@ static int id_iocs(int argc, char **argv, struct command *acmd, struct plugin *p "in either human-readable or binary format."; const char *controller_id = "identifier of desired controller"; - __cleanup_free struct nvme_id_iocs *iocs = NULL; + __cleanup_nvme_free struct nvme_id_iocs *iocs = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; @@ -4270,7 +4270,7 @@ static int id_domain(int argc, char **argv, struct command *acmd, struct plugin "in either normal|json|binary format."; const char *domain_id = "identifier of desired domain"; - __cleanup_free struct nvme_id_domain_list *id_domain = NULL; + __cleanup_nvme_free struct nvme_id_domain_list *id_domain = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; @@ -4416,7 +4416,7 @@ static int primary_ctrl_caps(int argc, char **argv, struct command *acmd, struct "command to the given device and report the information in a " "decoded format (default), json or binary."; - __cleanup_free struct nvme_primary_ctrl_cap *caps = NULL; + __cleanup_nvme_free struct nvme_primary_ctrl_cap *caps = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; @@ -4473,7 +4473,7 @@ static int list_secondary_ctrl(int argc, char **argv, struct command *acmd, stru const char *controller = "lowest controller identifier to display"; const char *num_entries = "number of entries to retrieve"; - __cleanup_free struct nvme_secondary_ctrl_list *sc_list = NULL; + __cleanup_nvme_free struct nvme_secondary_ctrl_list *sc_list = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; @@ -4542,8 +4542,8 @@ static int sleep_self_test(unsigned int seconds) static int wait_self_test(struct libnvme_transport_handle *hdl) { static const char spin[] = {'-', '\\', '|', '/' }; - __cleanup_free struct nvme_self_test_log *log = NULL; - __cleanup_free struct nvme_id_ctrl *ctrl = NULL; + __cleanup_nvme_free struct nvme_self_test_log *log = NULL; + __cleanup_nvme_free struct nvme_id_ctrl *ctrl = NULL; int err, i = 0, p = 0, cnt = 0; int wthr; @@ -4678,7 +4678,7 @@ static int device_self_test(int argc, char **argv, struct command *acmd, struct } if (cfg.stc == NVME_ST_CODE_RESERVED) { - __cleanup_free struct nvme_self_test_log *log = NULL; + __cleanup_nvme_free struct nvme_self_test_log *log = NULL; log = nvme_alloc(sizeof(*log)); if (!log) @@ -4741,7 +4741,7 @@ static int self_test_log(int argc, char **argv, struct command *acmd, struct plu const char *dst_entries = "Indicate how many DST log entries to be retrieved, " "by default all the 20 entries will be retrieved"; - __cleanup_free struct nvme_self_test_log *log = NULL; + __cleanup_nvme_free struct nvme_self_test_log *log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; nvme_print_flags_t flags; @@ -4862,8 +4862,8 @@ static bool is_get_feature_result_set(enum nvme_features_id feature_id) static int get_feature_id_changed(struct libnvme_transport_handle *hdl, struct feat_cfg cfg, nvme_print_flags_t flags) { - __cleanup_free void *buf_def = NULL; - __cleanup_free void *buf = NULL; + __cleanup_nvme_free void *buf_def = NULL; + __cleanup_nvme_free void *buf = NULL; __u64 result_def = 0; __u64 result; int err_def = 0; @@ -5254,7 +5254,7 @@ static char *nvme_fw_status_reset_type(__u16 status) static bool fw_commit_support_mud(struct libnvme_transport_handle *hdl) { - __cleanup_free struct nvme_id_ctrl *ctrl = NULL; + __cleanup_nvme_free struct nvme_id_ctrl *ctrl = NULL; int err; ctrl = nvme_alloc(sizeof(*ctrl)); @@ -5802,8 +5802,9 @@ static int nvme_get_properties(struct libnvme_transport_handle *hdl, void **pbar static void *mmap_registers(struct libnvme_transport_handle *hdl, bool writable) { + void *membase = NULL; +#ifdef HAVE_MMAP char path[512]; - void *membase; int fd; int prot = PROT_READ; @@ -5832,9 +5833,19 @@ static void *mmap_registers(struct libnvme_transport_handle *hdl, bool writable) } close(fd); +#endif return membase; } +static int munmap_registers(void *addr) +{ +#ifdef HAVE_MMAP + return munmap(addr, getpagesize()); +#else + return 0; +#endif +} + static int show_registers(int argc, char **argv, struct command *acmd, struct plugin *plugin) { const char *desc = "Reads and shows the defined NVMe controller registers\n" @@ -5886,7 +5897,7 @@ static int show_registers(int argc, char **argv, struct command *acmd, struct pl if (cfg.fabrics) free(bar); else - munmap(bar, getpagesize()); + munmap_registers(bar); return 0; } @@ -6171,7 +6182,7 @@ static int get_register(int argc, char **argv, struct command *acmd, struct plug if (fabrics) free(bar); else - munmap(bar, getpagesize()); + munmap_registers(bar); return err; } @@ -6455,7 +6466,7 @@ static int set_register(int argc, char **argv, struct command *acmd, struct plug err = set_register_names(hdl, bar, opts, &cfg); if (bar) - munmap(bar, getpagesize()); + munmap_registers(bar); return err; } @@ -6589,8 +6600,8 @@ static int format_cmd(int argc, char **argv, struct command *acmd, struct plugin __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free struct nvme_id_ctrl *ctrl = NULL; - __cleanup_free struct nvme_id_ns *ns = NULL; + __cleanup_nvme_free struct nvme_id_ctrl *ctrl = NULL; + __cleanup_nvme_free struct nvme_id_ns *ns = NULL; nvme_print_flags_t flags = NORMAL; struct libnvme_passthru_cmd cmd; __u32 timeout_ms = 600000; @@ -6813,19 +6824,12 @@ static int format_cmd(int argc, char **argv, struct command *acmd, struct plugin * to the given one because blkdev will not * update by itself without re-opening fd. */ - if (ioctl(libnvme_transport_handle_get_fd(hdl), BLKBSZSET, - &block_size) < 0) { + err = libnvme_update_block_size(hdl, block_size); + if (err < 0) { nvme_show_error( "failed to set block size to %d", block_size); - return -errno; - } - - if (ioctl(libnvme_transport_handle_get_fd(hdl), - BLKRRPART) < 0) { - nvme_show_error( - "failed to re-read partition table"); - return -errno; + return err; } } } @@ -6858,7 +6862,7 @@ static int set_feature(int argc, char **argv, struct command *acmd, struct plugi __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free void *buf = NULL; + __cleanup_nvme_free void *buf = NULL; __cleanup_fd int ffd = STDIN_FILENO; int err; __u64 result; @@ -7004,7 +7008,7 @@ static int sec_send(int argc, char **argv, struct command *acmd, struct plugin * __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; - __cleanup_free void *sec_buf = NULL; + __cleanup_nvme_free void *sec_buf = NULL; __cleanup_fd int sec_fd = -1; unsigned int sec_size; int err; @@ -7115,7 +7119,7 @@ static int dir_send(int argc, char **argv, struct command *acmd, struct plugin * __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free void *buf = NULL; + __cleanup_nvme_free void *buf = NULL; struct libnvme_passthru_cmd cmd; __u32 dw12 = 0; __cleanup_fd int ffd = STDIN_FILENO; @@ -7352,12 +7356,15 @@ static void get_pif_sts(struct nvme_id_ns *ns, struct nvme_nvm_id_ns *nvm_ns, *pif = (elbaf & NVME_NVM_ELBAF_QPIF_MASK) >> 9; } + +#define ERR_IGNORE_INVALID_FIELD 0x2000 /* invalid field error - ignore */ + static int get_pi_info(struct libnvme_transport_handle *hdl, __u32 nsid, __u8 prinfo, __u64 ilbrt, __u64 lbst, unsigned int *logical_block_size, __u16 *metadata_size) { - __cleanup_free struct nvme_nvm_id_ns *nvm_ns = NULL; - __cleanup_free struct nvme_id_ns *ns = NULL; + __cleanup_nvme_free struct nvme_nvm_id_ns *nvm_ns = NULL; + __cleanup_nvme_free struct nvme_id_ns *ns = NULL; __u8 sts = 0, pif = 0; unsigned int lbs = 0; __u8 lba_index; @@ -7393,7 +7400,7 @@ static int get_pi_info(struct libnvme_transport_handle *hdl, * Keep the I/O commands behavior same as before. * Since the error returned by drives unsupported. */ - return -ENAVAIL; + return ERR_IGNORE_INVALID_FIELD; pi_size = (pif == NVME_NVM_PIF_16B_GUARD) ? 8 : 16; if (NVME_FLBAS_META_EXT(ns->flbas)) { @@ -7419,8 +7426,8 @@ static int init_pi_tags(struct libnvme_transport_handle *hdl, struct libnvme_passthru_cmd *cmd, __u32 nsid, __u64 ilbrt, __u64 lbst, __u16 lbat, __u16 lbatm) { - __cleanup_free struct nvme_nvm_id_ns *nvm_ns = NULL; - __cleanup_free struct nvme_id_ns *ns = NULL; + __cleanup_nvme_free struct nvme_nvm_id_ns *nvm_ns = NULL; + __cleanup_nvme_free struct nvme_id_ns *ns = NULL; __u8 sts = 0, pif = 0; int err = 0; @@ -7448,7 +7455,7 @@ static int init_pi_tags(struct libnvme_transport_handle *hdl, * Keep the I/O commands behavior same as before. * Since the error returned by drives unsupported. */ - return -ENAVAIL; + return ERR_IGNORE_INVALID_FIELD; if (invalid_tags(lbst, ilbrt, sts, pif)) return -EINVAL; @@ -7564,7 +7571,7 @@ static int write_zeroes(int argc, char **argv, err = init_pi_tags(hdl, &cmd, cfg.nsid, cfg.ilbrt, cfg.lbst, cfg.lbat, cfg.lbatm); - if (err && err != -ENAVAIL) + if (err && err != ERR_IGNORE_INVALID_FIELD) return err; err = libnvme_submit_io_passthru(hdl, &cmd); @@ -7603,7 +7610,7 @@ static int dsm(int argc, char **argv, struct command *acmd, struct plugin *plugi __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free struct nvme_dsm_range *dsm = NULL; + __cleanup_nvme_free struct nvme_dsm_range *dsm = NULL; struct libnvme_passthru_cmd cmd; __u32 ctx_attrs[256] = {0,}; __u32 nlbs[256] = {0,}; @@ -7744,7 +7751,7 @@ static int copy_cmd(int argc, char **argv, struct command *acmd, struct plugin * __u16 elbatms[256] = { 0 }; __u16 elbats[256] = { 0 }; - __cleanup_free union { + __cleanup_nvme_free union { struct nvme_copy_range_f0 f0[256]; struct nvme_copy_range_f1 f1[256]; struct nvme_copy_range_f2 f2[256]; @@ -7907,7 +7914,7 @@ static int copy_cmd(int argc, char **argv, struct command *acmd, struct plugin * cfg.fua, cfg.lr, 0, cfg.dspec, copy->f0); err = init_pi_tags(hdl, &cmd, cfg.nsid, cfg.ilbrt, cfg.lbst, cfg.lbat, cfg.lbatm); - if (err != 0 && err != -ENAVAIL) + if (err != 0 && err != ERR_IGNORE_INVALID_FIELD) return err; err = libnvme_submit_io_passthru(hdl, &cmd); if (err) { @@ -8218,8 +8225,8 @@ static int resv_report(int argc, char **argv, struct command *acmd, struct plugi __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; - __cleanup_free struct nvme_resv_status *status = NULL; - __cleanup_free struct nvme_id_ctrl *ctrl = NULL; + __cleanup_nvme_free struct nvme_resv_status *status = NULL; + __cleanup_nvme_free struct nvme_id_ctrl *ctrl = NULL; struct libnvme_passthru_cmd cmd; nvme_print_flags_t flags; int err, size; @@ -8314,9 +8321,9 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; unsigned long long buffer_size = 0, mbuffer_size = 0; - __cleanup_free struct nvme_nvm_id_ns *nvm_ns = NULL; + __cleanup_nvme_free struct nvme_nvm_id_ns *nvm_ns = NULL; __cleanup_huge struct nvme_mem_huge mh = { 0, }; - __cleanup_free struct nvme_id_ns *ns = NULL; + __cleanup_nvme_free struct nvme_id_ns *ns = NULL; unsigned int logical_block_size = 0; struct timeval start_time, end_time; __cleanup_free void *mbuffer = NULL; @@ -8746,7 +8753,7 @@ static int verify_cmd(int argc, char **argv, struct command *acmd, struct plugin cfg.block_count, control, 0, NULL, 0, NULL, 0); err = init_pi_tags(hdl, &cmd, cfg.nsid, cfg.ilbrt, cfg.lbst, cfg.lbat, cfg.lbatm); - if (err != 0 && err != -ENAVAIL) + if (err != 0 && err != ERR_IGNORE_INVALID_FIELD) return err; err = libnvme_submit_io_passthru(hdl, &cmd); if (err) { @@ -8772,7 +8779,7 @@ static int sec_recv(int argc, char **argv, struct command *acmd, struct plugin * __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; - __cleanup_free void *sec_buf = NULL; + __cleanup_nvme_free void *sec_buf = NULL; struct libnvme_passthru_cmd cmd; nvme_print_flags_t flags; int err; @@ -8865,7 +8872,7 @@ static int get_lba_status(int argc, char **argv, struct command *acmd, __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; struct libnvme_passthru_cmd cmd; - __cleanup_free void *buf = NULL; + __cleanup_nvme_free void *buf = NULL; nvme_print_flags_t flags; unsigned long buf_len; int err; @@ -9028,7 +9035,7 @@ static int dir_receive(int argc, char **argv, struct command *acmd, struct plugi nvme_print_flags_t flags = NORMAL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free void *buf = NULL; + __cleanup_nvme_free void *buf = NULL; struct libnvme_passthru_cmd cmd; __u32 dw12 = 0; int err; @@ -10558,7 +10565,7 @@ static int get_mgmt_addr_list_log(int argc, char **argv, struct command *acmd, s nvme_print_flags_t flags; int err = -1; - __cleanup_free struct nvme_mgmt_addr_list_log *ma_log = NULL; + __cleanup_nvme_free struct nvme_mgmt_addr_list_log *ma_log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -10596,7 +10603,7 @@ static int get_rotational_media_info_log(int argc, char **argv, struct command * nvme_print_flags_t flags; int err = -1; - __cleanup_free struct nvme_rotational_media_info_log *info = NULL; + __cleanup_nvme_free struct nvme_rotational_media_info_log *info = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -10672,7 +10679,7 @@ static int get_dispersed_ns_psub(struct libnvme_transport_handle *hdl, __u32 nsi return 0; err_free: - free(log); + nvme_free(log); return err; } @@ -10685,7 +10692,7 @@ static int get_dispersed_ns_participating_nss_log(int argc, char **argv, struct __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free struct nvme_dispersed_ns_participating_nss_log *log = NULL; + __cleanup_nvme_free struct nvme_dispersed_ns_participating_nss_log *log = NULL; struct config { __u32 namespace_id; @@ -10857,7 +10864,7 @@ static int get_reachability_group_desc(struct libnvme_transport_handle *hdl, str return 0; err_free: - free(log); + nvme_free(log); *logp = NULL; return err; } @@ -10893,7 +10900,7 @@ static int get_reachability_groups(struct libnvme_transport_handle *hdl, bool rg return 0; err_free: - free(log); + nvme_free(log); return err; } @@ -10905,7 +10912,7 @@ static int get_reachability_groups_log(int argc, char **argv, struct command *ac nvme_print_flags_t flags; int err; __u64 len = 0; - __cleanup_free struct nvme_reachability_groups_log *log = NULL; + __cleanup_nvme_free struct nvme_reachability_groups_log *log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -10968,7 +10975,7 @@ static int get_reachability_association_desc(struct libnvme_transport_handle *hd return 0; err_free: - free(log); + nvme_free(log); *logp = NULL; return err; } @@ -11004,7 +11011,7 @@ static int get_reachability_associations(struct libnvme_transport_handle *hdl, b return 0; err_free: - free(log); + nvme_free(log); return err; } @@ -11016,7 +11023,7 @@ static int get_reachability_associations_log(int argc, char **argv, struct comma nvme_print_flags_t flags; int err; __u64 len = 0; - __cleanup_free struct nvme_reachability_associations_log *log = NULL; + __cleanup_nvme_free struct nvme_reachability_associations_log *log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -11086,7 +11093,7 @@ static int get_host_discovery(struct libnvme_transport_handle *hdl, bool allhost return 0; err_free: - free(log); + nvme_free(log); return err; } @@ -11096,7 +11103,7 @@ static int get_host_discovery_log(int argc, char **argv, struct command *acmd, s const char *allhoste = "All Host Entries"; nvme_print_flags_t flags; int err; - __cleanup_free struct nvme_host_discover_log *log = NULL; + __cleanup_nvme_free struct nvme_host_discover_log *log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -11165,7 +11172,7 @@ static int get_ave_discovery(struct libnvme_transport_handle *hdl, bool rae, str return 0; err_free: - free(log); + nvme_free(log); return err; } @@ -11175,7 +11182,7 @@ static int get_ave_discovery_log(int argc, char **argv, struct command *acmd, st nvme_print_flags_t flags; int err; - __cleanup_free struct nvme_ave_discover_log *log = NULL; + __cleanup_nvme_free struct nvme_ave_discover_log *log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -11240,7 +11247,7 @@ static int get_pull_model_ddc_req(struct libnvme_transport_handle *hdl, return 0; err_free: - free(log); + nvme_free(log); return err; } @@ -11251,7 +11258,7 @@ static int get_pull_model_ddc_req_log(int argc, char **argv, struct command *acm nvme_print_flags_t flags; int err; - __cleanup_free struct nvme_pull_model_ddc_req_log *log = NULL; + __cleanup_nvme_free struct nvme_pull_model_ddc_req_log *log = NULL; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; @@ -11297,6 +11304,7 @@ int main(int argc, char **argv) { int err; + libnvme_init(); nvme.extensions->parent = &nvme; if (argc < 2) { general_help(&builtin, NULL); diff --git a/nvme.h b/nvme.h index e5ad01f950..91ac0b18d1 100644 --- a/nvme.h +++ b/nvme.h @@ -21,6 +21,8 @@ #include #include +#include + #include #include diff --git a/plugins/amzn/amzn-nvme.c b/plugins/amzn/amzn-nvme.c index 307ef48fdf..50f772a9e5 100644 --- a/plugins/amzn/amzn-nvme.c +++ b/plugins/amzn/amzn-nvme.c @@ -7,6 +7,7 @@ #include #include +#include #include #include "common.h" diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c index cf21cb7f36..bb4ea3cfeb 100644 --- a/plugins/fdp/fdp.c +++ b/plugins/fdp/fdp.c @@ -6,7 +6,9 @@ #include #include #include +#ifndef _WIN32 #include +#endif #include #include diff --git a/plugins/feat/feat-nvme.c b/plugins/feat/feat-nvme.c index 7f0f165b65..847fe39781 100644 --- a/plugins/feat/feat-nvme.c +++ b/plugins/feat/feat-nvme.c @@ -70,7 +70,7 @@ static int feat_get_nsid(struct libnvme_transport_handle *hdl, __u32 nsid, int err; __u32 len = 0; - __cleanup_free void *buf = NULL; + __cleanup_nvme_free void *buf = NULL; if (!NVME_CHECK(sel, GET_FEATURES_SEL, SUPPORTED)) libnvme_get_feature_length(fid, cdw11, NVME_DATA_TFR_CTRL_TO_HOST, &len); diff --git a/plugins/intel/intel-nvme.c b/plugins/intel/intel-nvme.c index 30da9c8931..2bbb8869c7 100644 --- a/plugins/intel/intel-nvme.c +++ b/plugins/intel/intel-nvme.c @@ -1278,7 +1278,7 @@ static int read_header(struct libnvme_passthru_cmd *cmd, __u8 *buf, cmd->cdw10 = 0x400; cmd->cdw12 = dw12; cmd->data_len = 0x1000; - cmd->addr = (unsigned long)(void *)buf; + cmd->addr = (uintptr_t)(void *)buf; return read_entire_cmd(cmd, 0x400, 0x400, -1, hdl, buf); } @@ -1421,7 +1421,7 @@ static int get_internal_log(int argc, char **argv, struct command *acmd, /* for 1.1 Fultondales will use old nlog, but current assert/event */ if ((intel->ver.major < 1 && intel->ver.minor < 1) || (intel->ver.major <= 1 && intel->ver.minor <= 1 && cfg.log == 0)) { - cmd.addr = (unsigned long)(void *)buf; + cmd.addr = (uintptr_t)(void *)buf; err = get_internal_log_old(buf, output, hdl, &cmd); goto out; } diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index 2ce7020e72..12e6a7685c 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -861,7 +861,7 @@ static int mb_selective_download(int argc, char **argv, struct command *acmd, st } out_free: - free(fw_buf); + aligned_free(fw_buf); out_close: close(fw_fd); out: diff --git a/plugins/meson.build b/plugins/meson.build index 92b0c7cc16..4c833682ee 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -7,36 +7,37 @@ all_plugins = { 'dell': ['plugins/dell/dell-nvme.c'], 'dera': ['plugins/dera/dera-nvme.c'], 'fdp': ['plugins/fdp/fdp.c'], - 'huawei': ['plugins/huawei/huawei-nvme.c'], - 'ibm': ['plugins/ibm/ibm-nvme.c'], 'innogrit': ['plugins/innogrit/innogrit-nvme.c'], 'inspur': ['plugins/inspur/inspur-nvme.c'], 'intel': ['plugins/intel/intel-nvme.c'], 'mangoboost': ['plugins/mangoboost/mangoboost-nvme.c'], 'memblaze': ['plugins/memblaze/memblaze-nvme.c'], 'micron': ['plugins/micron/micron-nvme.c'], - 'netapp': ['plugins/netapp/netapp-nvme.c'], 'nvidia': ['plugins/nvidia/nvidia-nvme.c'], - 'sandisk': ['plugins/sandisk/sandisk-nvme.c', 'plugins/sandisk/sandisk-utils.c'], - 'scaleflux': ['plugins/scaleflux/sfx-nvme.c'], 'seagate': ['plugins/seagate/seagate-nvme.c'], 'shannon': ['plugins/shannon/shannon-nvme.c'], 'ssstc': ['plugins/ssstc/ssstc-nvme.c'], 'toshiba': ['plugins/toshiba/toshiba-nvme.c'], 'transcend': ['plugins/transcend/transcend-nvme.c'], 'virtium': ['plugins/virtium/virtium-nvme.c'], - 'wdc': ['plugins/wdc/wdc-nvme.c', 'plugins/wdc/wdc-utils.c'], - 'ymtc': ['plugins/ymtc/ymtc-nvme.c'], - 'zns': ['plugins/zns/zns.c'], } -# Get the list of plugins to build if host_system != 'windows' - selected_plugins = get_option('plugins') -else - selected_plugins = [] + all_plugins += { + 'huawei': ['plugins/huawei/huawei-nvme.c'], + 'ibm': ['plugins/ibm/ibm-nvme.c'], + 'netapp': ['plugins/netapp/netapp-nvme.c'], + 'sandisk': ['plugins/sandisk/sandisk-nvme.c', 'plugins/sandisk/sandisk-utils.c'], + 'scaleflux': ['plugins/scaleflux/sfx-nvme.c'], + 'wdc': ['plugins/wdc/wdc-nvme.c', 'plugins/wdc/wdc-utils.c'], + 'ymtc': ['plugins/ymtc/ymtc-nvme.c'], + 'zns': ['plugins/zns/zns.c'], + } endif +# Get the list of plugins to build +selected_plugins = get_option('plugins') + # Build the plugin_sources list from simple plugins plugin_sources = [] foreach plugin_name : selected_plugins @@ -54,11 +55,11 @@ if 'feat' in selected_plugins subdir('feat') endif -if 'lm' in selected_plugins +if 'lm' in selected_plugins and host_system != 'windows' subdir('lm') endif -if 'ocp' in selected_plugins +if 'ocp' in selected_plugins and json_c_dep.found() subdir('ocp') endif diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 9330b18e26..80da8e2efe 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -25,6 +25,8 @@ #include #include +#include + #include #include "common.h" @@ -61,9 +63,15 @@ #define C6_log_size 512 #define C5_MicronWorkLoad_log_size 256 +#ifndef min #define min(x, y) ((x) > (y) ? (y) : (x)) +#endif #define SensorCount 8 +#ifndef NAME_MAX +#define NAME_MAX 260 +#endif + /* Plugin version major_number.minor_number.patch */ static const char *__version_major = "2"; static const char *__version_minor = "0"; @@ -236,7 +244,7 @@ static enum eDriveModel GetDriveModel(int idx) static int ZipAndRemoveDir(char *strDirName, char *strFileName) { int err = 0; - char strBuffer[PATH_MAX]; + char strBuffer[4096]; int nRet; bool is_tgz = false; struct stat sb; @@ -255,14 +263,14 @@ static int ZipAndRemoveDir(char *strDirName, char *strFileName) /* check if log file is created, if not print error message */ if (nRet < 0 || (stat(strFileName, &sb) == -1)) { if (is_tgz) - sprintf(strBuffer, "check if tar and gzip commands are installed"); + snprintf(strBuffer, sizeof(strBuffer), "check if tar and gzip commands are installed"); else - sprintf(strBuffer, "check if zip command is installed"); + snprintf(strBuffer, sizeof(strBuffer), "check if zip command is installed"); fprintf(stderr, "Failed to create log data package, %s!\n", strBuffer); } - sprintf(strBuffer, "rm -f -R \"%s\" >temp.txt 2>&1", strDirName); + snprintf(strBuffer, sizeof(strBuffer), "rm -f -R \"%s\" >temp.txt 2>&1", strDirName); nRet = system(strBuffer); if (nRet < 0) printf("Failed to remove temporary files!\n"); @@ -684,7 +692,7 @@ static int micron_selective_download(int argc, char **argv, } out_free: - free(fw_buf); + aligned_free(fw_buf); out: close(fw_fd); return err; @@ -2166,7 +2174,7 @@ static void GetTimestampInfo(const char *strOSDirName) return; num = strftime((char *)outstr, sizeof(outstr), - "Timestamp (UTC): %a, %d %b %Y %T %z", tmp); + "Timestamp (UTC): %a, %d %b %Y %H:%M:%S %z", tmp); num += sprintf((char *)(outstr + num), "\nPackage Version: 1.4"); if (num) { strPDir = strdup(strOSDirName); @@ -2273,7 +2281,7 @@ static void GetOSConfig(const char *strOSDirName) { FILE *fpOSConfig = NULL; char strBuffer[1024]; - char strFileName[PATH_MAX]; + char strFileName[4096]; int i; struct { @@ -2313,7 +2321,7 @@ static int micron_telemetry_log(struct libnvme_transport_handle *hdl, __u8 type, int *logSize, int da) { int err, bs = 512, offset = bs; - unsigned short data_area[4]; + unsigned short data_area[5] = { 0 }; unsigned char ctrl_init = (type == 0x8); __u8 *buffer = (unsigned char *)calloc(bs, 1); @@ -2334,8 +2342,10 @@ static int micron_telemetry_log(struct libnvme_transport_handle *hdl, __u8 type, data_area[1] = buffer[9] << 8 | buffer[8]; data_area[2] = buffer[11] << 8 | buffer[10]; data_area[3] = buffer[13] << 8 | buffer[12]; + data_area[4] = buffer[15] << 8 | buffer[14]; data_area[0] = data_area[1] > data_area[2] ? data_area[1] : data_area[2]; data_area[0] = data_area[3] > data_area[0] ? data_area[3] : data_area[0]; + data_area[0] = data_area[4] > data_area[0] ? data_area[4] : data_area[0]; if (!data_area[da]) { fprintf(stderr, "Requested telemetry data for 0x%X is empty\n", type); @@ -3647,7 +3657,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *acmd, const char *desc = "This retrieves the micron debug log package"; const char *package = "Log output data file name (required)"; const char *type = "telemetry log type - host or controller"; - const char *data_area = "telemetry log data area 1, 2 or 3"; + const char *data_area = "telemetry log data area 1, 2, 3, or 4"; unsigned char *dataBuffer = NULL; int bSize = 0; int maxSize = 0; @@ -3685,8 +3695,8 @@ static int micron_internal_logs(int argc, char **argv, struct command *acmd, goto out; } - if (cfg.data_area <= 0 || cfg.data_area > 3) { - printf("data area must be selected using -d option ie --d=1,2,3\n"); + if (cfg.data_area <= 0 || cfg.data_area > 4) { + printf("data area must be selected using -d option ie --d=1,2,3,4\n"); goto out; } telemetry_option = 1; diff --git a/plugins/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c index 6739f64f92..9618b149c4 100644 --- a/plugins/nbft/nbft-plugin.c +++ b/plugins/nbft/nbft-plugin.c @@ -2,7 +2,9 @@ #include #include +#ifndef _WIN32 #include +#endif #include diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index 71eeb237bf..7e3d50227b 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -831,12 +831,12 @@ static int netapp_ontapdevices_get_info(struct libnvme_transport_handle *hdl, fprintf(stderr, "Unable to identify namespace descriptor for %s (%s)\n", dev, err < 0 ? libnvme_strerror(-err) : libnvme_status_to_string(err, false)); - free(nsdescs); + aligned_free(nsdescs); return 0; } memcpy(item->uuid, nsdescs + sizeof(struct nvme_ns_id_desc), sizeof(item->uuid)); - free(nsdescs); + aligned_free(nsdescs); err = nvme_get_ontap_c2_log(hdl, item->nsid, item->log_data, ONTAP_C2_LOG_SIZE); if (err) { diff --git a/plugins/ocp/ocp-fw-activation-history.c b/plugins/ocp/ocp-fw-activation-history.c index 392cb8a816..a9c4e6f1e6 100644 --- a/plugins/ocp/ocp-fw-activation-history.c +++ b/plugins/ocp/ocp-fw-activation-history.c @@ -70,7 +70,8 @@ int ocp_fw_activation_history_log(int argc, char **argv, struct command *acmd, err = validate_output_format(nvme_args.output_format, &print_flag); if (err < 0) { - fprintf(stderr, "Error: Invalid output format.\n"); + fprintf(stderr, "ERROR : OCP : invalid output-format. " + "Valid values: normal|json|binary|tabular.\n"); return err; } diff --git a/plugins/ocp/ocp-hardware-component-log.c b/plugins/ocp/ocp-hardware-component-log.c index e13006e38e..b06426b584 100644 --- a/plugins/ocp/ocp-hardware-component-log.c +++ b/plugins/ocp/ocp-hardware-component-log.c @@ -250,7 +250,8 @@ static int get_hwcomp_log(struct libnvme_transport_handle *hdl, __u32 id, bool l ret = validate_output_format(nvme_args.output_format, &fmt); if (ret < 0) { - fprintf(stderr, "error: ocp: invalid output format\n"); + fprintf(stderr, "ERROR : OCP : invalid output-format. " + "Valid values: normal|json|binary|tabular.\n"); return ret; } diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 0d30227ec2..d277ec0b35 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -17,6 +17,7 @@ #include #include +#include #include "common.h" #include "logging.h" @@ -208,7 +209,8 @@ static int get_c3_log_page(struct libnvme_transport_handle *hdl, char *format) ret = validate_output_format(format, &fmt); if (ret < 0) { - fprintf(stderr, "ERROR : OCP : invalid output format\n"); + fprintf(stderr, "ERROR : OCP : invalid output-format. " + "Valid values: normal|json|binary|tabular.\n"); return ret; } @@ -291,7 +293,6 @@ int ocp_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, __u64 result; struct feature_latency_monitor buf = { 0 }; __u32 nsid = NVME_NSID_ALL; - struct stat nvme_stat; struct nvme_id_ctrl ctrl; const char *desc = "Set Latency Monitor feature."; @@ -348,11 +349,7 @@ int ocp_set_latency_monitor_feature(int argc, char **argv, struct command *acmd, if (err) return err; - err = fstat(libnvme_transport_handle_get_fd(hdl), &nvme_stat); - if (err < 0) - return err; - - if (S_ISBLK(nvme_stat.st_mode)) { + if (libnvme_transport_handle_is_blkdev(hdl)) { err = libnvme_get_nsid(hdl, &nsid); if (err < 0) { perror("invalid-namespace-id"); @@ -1192,7 +1189,7 @@ static int get_telemetry_log_page_data(struct libnvme_transport_handle *hdl, } memset(hdr, 0, bs); - fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); if (fd < 0) { fprintf(stderr, "Failed to open output file %s: %s!\n", output_file, libnvme_strerror(errno)); @@ -1338,7 +1335,7 @@ static int get_c9_log_page_data(struct libnvme_transport_handle *hdl, } if (save_bin) { - fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); if (fd < 0) { fprintf(stderr, "Failed to open output file %s: %s!\n", output_file, libnvme_strerror(errno)); @@ -1406,7 +1403,8 @@ int parse_ocp_telemetry_log(struct ocp_telemetry_parse_options *options) status = validate_output_format(options->output_format, &fmt); if (status < 0) { - nvme_show_error("Invalid output format\n"); + nvme_show_error("ERROR : OCP : invalid output-format. " + "Valid values: normal|json|binary|tabular."); return status; } @@ -1435,11 +1433,10 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; int err = 0; __u32 nsid = NVME_NSID_ALL; - struct stat nvme_stat; char sn[21] = {0,}; struct nvme_id_ctrl ctrl; bool is_support_telemetry_controller; - struct ocp_telemetry_parse_options opt; + struct ocp_telemetry_parse_options opt = {0}; int tele_type = 0; int tele_area = 0; char file_path_telemetry[PATH_MAX], file_path_string[PATH_MAX]; @@ -1461,11 +1458,7 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct if (opt.telemetry_type == 0) opt.telemetry_type = "host"; - err = fstat(libnvme_transport_handle_get_fd(hdl), &nvme_stat); - if (err < 0) - return err; - - if (S_ISBLK(nvme_stat.st_mode)) { + if (libnvme_transport_handle_is_blkdev(hdl)) { err = libnvme_get_nsid(hdl, &nsid); if (err < 0) return err; @@ -1566,8 +1559,12 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *acmd, struct opt.string_log = file_path_string; } + if (argconfig_parse_seen(opts, "output-format")) + opt.output_format = nvme_args.output_format; + if (!opt.output_format) { - nvme_show_result("Missing format. Using default format - JSON.\n"); + nvme_show_result("Missing output-format. Using default - json " + "(valid: normal|json|binary|tabular).\n"); opt.output_format = DEFAULT_OUTPUT_FORMAT_JSON; } @@ -1636,7 +1633,8 @@ static int get_c5_log_page(struct libnvme_transport_handle *hdl, char *format) ret = validate_output_format(format, &fmt); if (ret < 0) { - fprintf(stderr, "ERROR : OCP : invalid output format\n"); + fprintf(stderr, "ERROR : OCP : invalid output-format. " + "Valid values: normal|json|binary|tabular.\n"); return ret; } @@ -1737,7 +1735,8 @@ static int get_c1_log_page(struct libnvme_transport_handle *hdl, char *format) ret = validate_output_format(format, &fmt); if (ret < 0) { - fprintf(stderr, "ERROR : OCP : invalid output format\n"); + fprintf(stderr, "ERROR : OCP : invalid output-format. " + "Valid values: normal|json|binary|tabular.\n"); return ret; } @@ -1837,7 +1836,8 @@ static int get_c4_log_page(struct libnvme_transport_handle *hdl, char *format) ret = validate_output_format(format, &fmt); if (ret < 0) { - fprintf(stderr, "ERROR : OCP : invalid output format\n"); + fprintf(stderr, "ERROR : OCP : invalid output-format. " + "Valid values: normal|json|binary|tabular.\n"); return ret; } @@ -2430,7 +2430,8 @@ static int get_c9_log_page(struct libnvme_transport_handle *hdl, ret = validate_output_format(format, &fmt); if (ret < 0) { - fprintf(stderr, "ERROR : OCP : invalid output format\n"); + fprintf(stderr, "ERROR : OCP : invalid output-format. " + "Valid values: normal|json|binary|tabular.\n"); return ret; } @@ -2518,7 +2519,8 @@ static int get_c7_log_page(struct libnvme_transport_handle *hdl, char *format) ret = validate_output_format(format, &fmt); if (ret < 0) { - fprintf(stderr, "ERROR : OCP : invalid output format\n"); + fprintf(stderr, "ERROR : OCP : invalid output-format. " + "Valid values: normal|json|binary|tabular.\n"); return ret; } @@ -2629,7 +2631,7 @@ static int fw_activation_history_log(int argc, char **argv, struct command *acmd static int error_injection_get(struct libnvme_transport_handle *hdl, const __u8 sel, bool uuid, __u32 nsid) { - __cleanup_free struct erri_entry *entry = NULL; + __cleanup_nvme_free struct erri_entry *entry = NULL; struct erri_get_cq_entry cq_entry; const __u8 fid = OCP_FID_ERRI; __u64 result; @@ -2712,7 +2714,7 @@ static int get_error_injection(int argc, char **argv, struct command *acmd, stru static int error_injection_set(struct libnvme_transport_handle *hdl, struct erri_config *cfg, bool uuid, __u32 nsid) { - __cleanup_free struct erri_entry *entry = NULL; + __cleanup_nvme_free struct erri_entry *entry = NULL; __cleanup_fd int ffd = -1; __u32 data_len; __u8 uidx = 0; @@ -2935,7 +2937,7 @@ static int ocp_get_persistent_event_log(int argc, char **argv, "processing this persistent log page command."; const char *log_len = "number of bytes to retrieve"; - __cleanup_free struct nvme_persistent_event_log *pevent = NULL; + __cleanup_nvme_free struct nvme_persistent_event_log *pevent = NULL; struct nvme_persistent_event_log *pevent_collected = NULL; __cleanup_huge struct nvme_mem_huge mh = { 0, }; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; @@ -2968,7 +2970,8 @@ static int ocp_get_persistent_event_log(int argc, char **argv, err = validate_output_format(nvme_args.output_format, &flags); if (err < 0) { - nvme_show_error("Invalid output format"); + nvme_show_error("ERROR : OCP : invalid output-format. " + "Valid values: normal|json|binary|tabular."); return err; } diff --git a/plugins/ocp/ocp-smart-extended-log.c b/plugins/ocp/ocp-smart-extended-log.c index 7b1483870f..c46a09ec69 100644 --- a/plugins/ocp/ocp-smart-extended-log.c +++ b/plugins/ocp/ocp-smart-extended-log.c @@ -38,7 +38,8 @@ static int get_c0_log_page(struct libnvme_transport_handle *hdl, char *format, ret = validate_output_format(format, &fmt); if (ret < 0) { - fprintf(stderr, "ERROR : OCP : invalid output format\n"); + fprintf(stderr, "ERROR : OCP : invalid output-format. " + "Valid values: normal|json|binary|tabular.\n"); return ret; } diff --git a/plugins/shannon/shannon-nvme.c b/plugins/shannon/shannon-nvme.c index 54658e7743..4aa945c3b4 100644 --- a/plugins/shannon/shannon-nvme.c +++ b/plugins/shannon/shannon-nvme.c @@ -230,7 +230,7 @@ static int get_additional_feature(int argc, char **argv, struct command *acmd, s cfg.cdw11, 0, buf, cfg.data_len, &result); if (err > 0) nvme_show_status(err); - free(buf); + aligned_free(buf); return err; } @@ -255,7 +255,7 @@ static int set_additional_feature(int argc, char **argv, struct command *acmd, s const char *save = "specifies that the controller shall save the attribute"; __cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL; __cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL; - __cleanup_free void *buf = NULL; + __cleanup_nvme_free void *buf = NULL; int ffd = STDIN_FILENO; __u64 result; int err; diff --git a/plugins/solidigm/solidigm-internal-logs.c b/plugins/solidigm/solidigm-internal-logs.c index db2b230b1d..4a18b58dba 100644 --- a/plugins/solidigm/solidigm-internal-logs.c +++ b/plugins/solidigm/solidigm-internal-logs.c @@ -15,6 +15,7 @@ #include #include +#include #include #include "common.h" @@ -242,7 +243,7 @@ static int ilog_dump_assert_logs(struct libnvme_transport_handle *hdl, struct il struct libnvme_passthru_cmd cmd = { .opcode = 0xd2, .nsid = NVME_NSID_ALL, - .addr = (unsigned long)(void *)head_buf, + .addr = (__u64)(uintptr_t)head_buf, .cdw12 = ASSERTLOG, .cdw13 = 0, }; @@ -265,7 +266,7 @@ static int ilog_dump_assert_logs(struct libnvme_transport_handle *hdl, struct il close(output); return err; } - cmd.addr = (unsigned long)(void *)buf; + cmd.addr = (__u64)(uintptr_t)buf; if (ilog->cfg->verbose) { printf("Assert Log, cores: %d log size: %d header size: %d\n", ad->header.numcores, @@ -299,7 +300,7 @@ static int ilog_dump_event_logs(struct libnvme_transport_handle *hdl, struct ilo struct libnvme_passthru_cmd cmd = { .opcode = 0xd2, .nsid = NVME_NSID_ALL, - .addr = (unsigned long)(void *)head_buf, + .addr = (__u64)(uintptr_t)head_buf, .cdw12 = EVENTLOG, .cdw13 = 0, }; @@ -322,7 +323,7 @@ static int ilog_dump_event_logs(struct libnvme_transport_handle *hdl, struct ilo close(output); return err; } - cmd.addr = (unsigned long)(void *)buf; + cmd.addr = (__u64)(uintptr_t)buf; if (ilog->cfg->verbose) printf("Event Log, cores: %d log size: %d\n", core_num, ehdr->header.log_size * 4); @@ -374,7 +375,7 @@ static int ilog_dump_nlogs(struct libnvme_transport_handle *hdl, struct ilog *il struct libnvme_passthru_cmd cmd = { .opcode = 0xd2, .nsid = NVME_NSID_ALL, - .addr = (unsigned long)(void *)buf + .addr = (__u64)(uintptr_t)buf }; struct dump_select { @@ -776,7 +777,7 @@ static int ilog_dump_no_lsp_log_pages(struct libnvme_transport_handle *hdl, stru static int ilog_dump_pel(struct libnvme_transport_handle *hdl, struct ilog *ilog) { - __cleanup_free struct nvme_persistent_event_log *pevent = NULL; + __cleanup_nvme_free struct nvme_persistent_event_log *pevent = NULL; __cleanup_huge struct nvme_mem_huge mh = {0}; void *pevent_log_full; size_t max_data_tx; diff --git a/plugins/solidigm/solidigm-smart.c b/plugins/solidigm/solidigm-smart.c index 9e3cfbeafb..090230ae00 100644 --- a/plugins/solidigm/solidigm-smart.c +++ b/plugins/solidigm/solidigm-smart.c @@ -147,12 +147,12 @@ static void smart_log_item_print(struct nvme_additional_smart_log_item *item) le32_to_cpu(item->thermal_throttle.count)); return; case 0xF3: - printf("gain0: %u, loss0: %u, gain1: %u, loss1: %u, legacy:%lu\n", + printf("gain0: %u, loss0: %u, gain1: %u, loss1: %u, legacy:%llu\n", le16_to_cpu(pll_item->gainCount0), le16_to_cpu(pll_item->lossCount0), le16_to_cpu(pll_item->gainCount1), le16_to_cpu(pll_item->lossCount1), - int48_to_long(item->raw)); + (unsigned long long)int48_to_long(item->raw)); return; default: printf("%"PRIu64"\n", int48_to_long(item->raw)); diff --git a/plugins/solidigm/solidigm-workload-tracker.c b/plugins/solidigm/solidigm-workload-tracker.c index 14599d8d6b..8c78c8e5e4 100644 --- a/plugins/solidigm/solidigm-workload-tracker.c +++ b/plugins/solidigm/solidigm-workload-tracker.c @@ -244,8 +244,10 @@ static void wltracker_print_header(struct wltracker *wlt) printf("%-24s %u.%u\n", "Log page version:", le16_to_cpu(log->majorVersion), le16_to_cpu(log->minorVersion)); printf("%-24s %u\n", "Sample period(ms):", le32_to_cpu(log->samplePeriodInMilliseconds)); - printf("%-24s %lu\n", "timestamp_lastChange:", le64_to_cpu(log->timestamp_lastEntry)); - printf("%-24s %lu\n", "timestamp_triggered:", le64_to_cpu(log->timestamp_triggered)); + printf("%-24s %llu\n", "timestamp_lastChange:", + (unsigned long long)le64_to_cpu(log->timestamp_lastEntry)); + printf("%-24s %llu\n", "timestamp_triggered:", + (unsigned long long)le64_to_cpu(log->timestamp_triggered)); printf("%-24s 0x%x\n", "config:", le32_to_cpu(log->config.dword)); printf("%-24s %u\n", "Triggerthreshold:", le32_to_cpu(log->triggerthreshold)); printf("%-24s %u\n", "ValueTriggered:", le32_to_cpu(log->triggeredValue)); @@ -253,7 +255,7 @@ static void wltracker_print_header(struct wltracker *wlt) printf("%-24s %u\n", "Total log page entries:", le32_to_cpu(log->workloadLogCount)); printf("%-24s %u\n", "Trigger count:", log->triggeredEvents); if (nvme_args.verbose > 1) - printf("%-24s %ld\n", "Poll count:", wlt->poll_count); + printf("%-24s %zu\n", "Poll count:", wlt->poll_count); if (wlt->poll_count != 0) wltracker_print_field_names(wlt); } @@ -436,7 +438,7 @@ void wltracker_run_time_update(struct wltracker *wlt) printf("run_time: %lluus\n", wlt->run_time_us); } -static int stricmp(char const *a, char const *b) +static int sldgm_stricmp(char const *a, char const *b) { if (!a || !b) return 1; @@ -449,7 +451,7 @@ static int stricmp(char const *a, char const *b) static int find_option(char const *list[], int size, const char *val) { for (int i = 0; i < size; i++) { - if (!stricmp(val, list[i])) + if (!sldgm_stricmp(val, list[i])) return i; } return -EINVAL; @@ -467,7 +469,7 @@ static void join_options(char *dest, char const *list[], size_t list_size) static int find_field(struct field *fields, const char *val) { for (int i = 0; i < MAX_FIELDS; i++) { - if (!stricmp(val, fields[i].name)) + if (!sldgm_stricmp(val, fields[i].name)) return i; } return -EINVAL; diff --git a/plugins/toshiba/toshiba-nvme.c b/plugins/toshiba/toshiba-nvme.c index 6b67609243..76a9979d66 100644 --- a/plugins/toshiba/toshiba-nvme.c +++ b/plugins/toshiba/toshiba-nvme.c @@ -110,7 +110,7 @@ static int nvme_get_sct_status(struct libnvme_transport_handle *hdl, __u32 devic } } end: - free(data); + aligned_free(data); return err; } @@ -134,7 +134,7 @@ static int nvme_sct_command_transfer_log(struct libnvme_transport_handle *hdl, b memcpy(data + 2, &function_code, sizeof(function_code)); err = nvme_sct_op(hdl, OP_SCT_COMMAND_TRANSFER, DW10_SCT_COMMAND_TRANSFER, DW11_SCT_COMMAND_TRANSFER, data, data_len); - free(data); + aligned_free(data); return err; } @@ -314,7 +314,7 @@ static int nvme_get_internal_log(struct libnvme_transport_handle *hdl, end: if (o_fd >= 0) close(o_fd); - free(page_data); + aligned_free(page_data); return err; } @@ -411,7 +411,7 @@ static int nvme_get_vendor_log(struct libnvme_transport_handle *hdl, d(log, log_len, 16, 1); } end: - free(log); + aligned_free(log); return err; } diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 0cd20874d9..eb5c0b238a 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -31,6 +31,7 @@ #include #include +#include #include #include "common.h" @@ -2873,7 +2874,7 @@ static bool wdc_nvme_check_supported_log_page(struct libnvme_global_ctx *ctx, int err = -1; struct wdc_c2_cbs_data *cbs_data = NULL; - __cleanup_free struct nvme_supported_log_pages *supports = NULL; + __cleanup_nvme_free struct nvme_supported_log_pages *supports = NULL; /* Check log page id 0 (supported log pages) first */ supports = nvme_alloc(sizeof(*supports)); diff --git a/plugins/wdc/wdc-utils.c b/plugins/wdc/wdc-utils.c index 81ace2dfc7..98200c8219 100644 --- a/plugins/wdc/wdc-utils.c +++ b/plugins/wdc/wdc-utils.c @@ -25,6 +25,7 @@ #include #include +#include #include #include "nvme-cmds.h" diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index c89fc0a299..ff97b292d7 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -426,7 +426,7 @@ static int zone_mgmt_send(int argc, char **argv, struct command *acmd, struct pl if (cfg.file) close(ffd); free: - free(buf); + aligned_free(buf); return err; } @@ -1076,12 +1076,12 @@ static int zone_append(int argc, char **argv, struct command *acmd, struct plugi perror("zns zone-append"); free_meta: - free(mbuf); + aligned_free(mbuf); close_mfd: if (cfg.metadata) close(mfd); free_data: - free(buf); + aligned_free(buf); close_dfd: if (cfg.data) close(dfd); diff --git a/util/cleanup.h b/util/cleanup.h index 72acb4fa88..0795c96d4c 100644 --- a/util/cleanup.h +++ b/util/cleanup.h @@ -27,6 +27,12 @@ static inline void freep(void *p) } #define __cleanup_free __cleanup(freep) +static inline void nvme_freep(void *p) +{ + nvme_free(*(void **)p); +} +#define __cleanup_nvme_free __cleanup(nvme_freep) + #define __cleanup_huge __cleanup(nvme_free_huge) static inline void cleanup_fd(int *fd) diff --git a/util/mem-windows.c b/util/mem-windows.c new file mode 100644 index 0000000000..ffeb0a355c --- /dev/null +++ b/util/mem-windows.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libnvme. + * Copyright (c) 2025 Micron Technology, Inc. + * + * Authors: Brandon Capener + */ + +#include +#include + +#include + +#include +#include + +#include "mem.h" +#include "common.h" + +#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) +#define HUGE_MIN 0x80000 /* policy threshold when large pages unavailable */ + +void *nvme_alloc_huge(size_t len, struct nvme_mem_huge *mh) +{ + SIZE_T large_min = GetLargePageMinimum(); /* 0 if unsupported/unavailable */ + SIZE_T huge_min = large_min ? large_min : HUGE_MIN; + SIZE_T page_size = getpagesize(); + SIZE_T align; + + memset(mh, 0, sizeof(*mh)); + + len = ROUND_UP(len, page_size); + + /* + * For smaller allocations, use regular allocator. + */ + if (len < huge_min) { + mh->p = nvme_alloc(len); + if (!mh->p) + return NULL; + mh->posix_memalign = true; + mh->len = len; + return mh->p; + } + + /* + * Try large pages first when available. + * Requires SeLockMemoryPrivilege and size multiple of large_min. + */ + if (large_min) { + SIZE_T lp_len = ROUND_UP(len, large_min); + + mh->p = VirtualAlloc(NULL, lp_len, + MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, + PAGE_READWRITE); + if (mh->p != NULL) { + mh->len = lp_len; + mh->posix_memalign = false; + return mh->p; + } + } + + /* + * Fallback to regular VirtualAlloc. + */ + mh->p = VirtualAlloc(NULL, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (mh->p != NULL) { + mh->len = len; + mh->posix_memalign = false; + return mh->p; + } + + /* + * Final fallback: aligned heap allocation. + * Prefer large page size if known, otherwise page size. + */ + align = large_min ? large_min : page_size; + len = ROUND_UP(len, align); + if (posix_memalign(&mh->p, align, len)) + return NULL; + + mh->posix_memalign = true; + mh->len = len; + memset(mh->p, 0, mh->len); + return mh->p; +} + +void nvme_free_huge(struct nvme_mem_huge *mh) +{ + if (!mh || mh->len == 0) + return; + + if (mh->posix_memalign) + nvme_free(mh->p); + else + VirtualFree(mh->p, 0, MEM_RELEASE); + + mh->len = 0; + mh->p = NULL; +} diff --git a/util/mem.c b/util/mem.c index 2b97632d82..f0db3f985e 100644 --- a/util/mem.c +++ b/util/mem.c @@ -3,7 +3,14 @@ #include #include #include + +#ifdef HAVE_MMAP #include +#endif + +#include +#include +#include #include "mem.h" @@ -34,12 +41,18 @@ void *nvme_realloc(void *p, size_t len) if (p) { memcpy(result, p, min(old_len, len)); - free(p); + nvme_free(p); } return result; } +void nvme_free(void *p) +{ + aligned_free(p); +} + +#ifndef _WIN32 void *nvme_alloc_huge(size_t len, struct nvme_mem_huge *mh) { memset(mh, 0, sizeof(*mh)); @@ -102,10 +115,11 @@ void nvme_free_huge(struct nvme_mem_huge *mh) return; if (mh->posix_memalign) - free(mh->p); + nvme_free(mh->p); else munmap(mh->p, mh->len); mh->len = 0; mh->p = NULL; } +#endif diff --git a/util/mem.h b/util/mem.h index d13eb3a213..a792457cbc 100644 --- a/util/mem.h +++ b/util/mem.h @@ -7,6 +7,7 @@ void *nvme_alloc(size_t len); void *nvme_realloc(void *p, size_t len); +void nvme_free(void *p); struct nvme_mem_huge { size_t len; diff --git a/util/meson.build b/util/meson.build index 42412705d2..2d7c34525d 100644 --- a/util/meson.build +++ b/util/meson.build @@ -1,25 +1,25 @@ # SPDX-License-Identifier: GPL-2.0-or-later -util_sources = [] +util_sources = [ + 'util/argconfig.c', + 'util/base64.c', + 'util/crc32.c', + 'util/mem.c', + 'util/sighdl.c', + 'util/suffix.c', + 'util/types.c', + 'util/utils.c', + 'util/table.c' +] if host_system == 'windows' - util_sources += [] -else util_sources += [ - 'util/argconfig.c', - 'util/base64.c', - 'util/crc32.c', - 'util/mem.c', - 'util/sighdl.c', - 'util/suffix.c', - 'util/types.c', - 'util/utils.c', - 'util/table.c' + 'util/mem-windows.c', ] +endif - if json_c_dep.found() - util_sources += [ - 'util/json.c', - ] - endif +if json_c_dep.found() + util_sources += [ + 'util/json.c', + ] endif diff --git a/util/sighdl.c b/util/sighdl.c index 146591e5df..d40267ad5a 100644 --- a/util/sighdl.c +++ b/util/sighdl.c @@ -3,8 +3,11 @@ #include #include +#include + #include "sighdl.h" + bool nvme_sigint_received; static void nvme_sigint_handler(int signum) diff --git a/util/table.c b/util/table.c index 76366b93da..f3bf91ecb8 100644 --- a/util/table.c +++ b/util/table.c @@ -16,10 +16,11 @@ */ #include -#include #include #include +#include + #include "table.h" static int table_get_value_width(struct value *v) diff --git a/windows-stubs.c b/windows-stubs.c new file mode 100644 index 0000000000..0282be60fb --- /dev/null +++ b/windows-stubs.c @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Windows compatibility implementation for nvme-cli application. + * + * This file contains nvme-cli-specific Windows stubs for functionality + * not currently supported on Windows. + */ + +#ifdef _WIN32 + +#include + +/* ========== NVMe RPMB Command Stubs ========== */ +/* + * NVMe RPMB (Replay Protected Memory Block) operations are not currently + * supported on Windows. This stub provides an error message. + */ + +struct command; +struct plugin; + +int rpmb_cmd_option(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + (void)argc; (void)argv; (void)cmd; (void)plugin; + fprintf(stderr, "NVMe RPMB commands are not supported on Windows\n"); + return -1; +} + +#endif /* _WIN32 */