massive restructure

This commit is contained in:
2026-07-30 01:02:17 +02:00
parent 0b0634530f
commit 438ce4c58e
32 changed files with 3558 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
# ---> Nix
# Ignore build outputs from performing a nix-build or `nix build` command
result
result-*
# Ignore automatically generated direnv output
.direnv
+3
View File
@@ -0,0 +1,3 @@
# nixos-conf
my nixos config
Generated
+463
View File
@@ -0,0 +1,463 @@
{
"nodes": {
"bcdl-free": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1785350661,
"narHash": "sha256-IEFWX9+OQkvYry8ODdVmRZkD8K0WiPVLRAo/GItzBJs=",
"ref": "refs/heads/main",
"rev": "8b9fa0ef8047861ca1b8959afdf8079b39a64d53",
"revCount": 74,
"type": "git",
"url": "https://git.pizzasucht.net/KeksNino/free-bandcamp-downloader"
},
"original": {
"type": "git",
"url": "https://git.pizzasucht.net/KeksNino/free-bandcamp-downloader"
}
},
"crane": {
"locked": {
"lastModified": 1766774972,
"narHash": "sha256-8qxEFpj4dVmIuPn9j9z6NTbU+hrcGjBOvaxTzre5HmM=",
"owner": "ipetkov",
"repo": "crane",
"rev": "01bc1d404a51a0a07e9d8759cd50a7903e218c82",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1765835352,
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gsr-ui": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1785085647,
"narHash": "sha256-VerKUIpQHGnXgpmRXzh767X0BJNjpACWdVp391t8n1s=",
"owner": "rPlakama",
"repo": "gsr-ui-nix",
"rev": "1f9a46bcdde0e805ced86a0e995f1449f8f7a12f",
"type": "github"
},
"original": {
"owner": "rPlakama",
"repo": "gsr-ui-nix",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1785306346,
"narHash": "sha256-DScBkW0fOgpGPK2trNoX3ryLTlaC14+gglFo/BhGJ4g=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "e705714e918c3b11affcdd15db2cbe3a070420a0",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"home-manager_2": {
"inputs": {
"nixpkgs": [
"zen-browser",
"nixpkgs"
]
},
"locked": {
"lastModified": 1783963347,
"narHash": "sha256-r376E2XpakiXwModDHIxlvB6qLq4iFVEq730vxOO4JY=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "a45a7c451455a51ae740ec3bce4024b312809c29",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"millennium": {
"inputs": {
"millennium-src": "millennium-src",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"dir": "packages/nix",
"lastModified": 1785163789,
"narHash": "sha256-nHTGaYBPKqOY4SPTqRJNX8MfV/GuIjgPCUxDHHeinGc=",
"owner": "SteamClientHomebrew",
"repo": "Millennium",
"rev": "1375ceda332171907e64e9a9324f9a869f715d6c",
"type": "github"
},
"original": {
"dir": "packages/nix",
"owner": "SteamClientHomebrew",
"repo": "Millennium",
"type": "github"
}
},
"millennium-src": {
"flake": false,
"locked": {
"lastModified": 1783268941,
"narHash": "sha256-MJLJ0I48HXgfOpISTAbDMYMUdWj9A6M+jGQz4uAvwyw=",
"owner": "SteamClientHomebrew",
"repo": "Millennium",
"rev": "f37f05bdbd4727d873a1ad83ce72d062ef9a0c48",
"type": "github"
},
"original": {
"owner": "SteamClientHomebrew",
"repo": "Millennium",
"rev": "f37f05bdbd4727d873a1ad83ce72d062ef9a0c48",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1785350410,
"narHash": "sha256-wXYrsdit8Z54aOAUf7/83LDhkY9+RgSuFBO/rFJeHb8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "00d4d9bc4d406965d1e0081300dd8c854fd4ba11",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1765674936,
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1781577229,
"narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "567a49d1913ce81ac6e9582e3553dd90a955875f",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixpkgs",
"rev": "567a49d1913ce81ac6e9582e3553dd90a955875f",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1785332987,
"narHash": "sha256-Eiw4ie++bC7tDWyBEZmcbEeKv1xv0UAihkVTjD4C5VM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "97478ef76c1f4c40b12fd4a42c1feba41e60650d",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1767026758,
"narHash": "sha256-7fsac/f7nh/VaKJ/qm3I338+wAJa/3J57cOGpXi0Sbg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "346dd96ad74dc4457a9db9de4f4f57dab2e5731d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1783279667,
"narHash": "sha256-/NAkDSsve+GNM0Bt6tleJdCGfsTlK89nPjkVOzZMo0s=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f205b5574fd0cb7da5b702a2da51507b7f4fdd1b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_6": {
"locked": {
"lastModified": 1784007870,
"narHash": "sha256-djcLt/JJphyNt4eDY9XTly+/WbCK5lqWq9lSgCmJkkQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "18b9261cb3294b6d2a06d03f96872827b8fe2698",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"bcdl-free": "bcdl-free",
"gsr-ui": "gsr-ui",
"home-manager": "home-manager",
"millennium": "millennium",
"nixpkgs": "nixpkgs_3",
"sls-steam": "sls-steam",
"xremap-flake": "xremap-flake",
"yazi": "yazi",
"zen-browser": "zen-browser"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"yazi",
"nixpkgs"
]
},
"locked": {
"lastModified": 1783320166,
"narHash": "sha256-l7C/OsjcnWDOk2K3ssj+SBduwL67LashjBqis9+t468=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "20ee15370c9256669d66968b89ee20a4b0a4e673",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"sls-steam": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1785275929,
"narHash": "sha256-3IbhTuHEqh2tokPGNKnv6x0HcsjOu3hk1MbLYRtzyRQ=",
"owner": "AceSLS",
"repo": "SLSsteam",
"rev": "1906382a482f35b2bc9b124f3849538ab432e4f3",
"type": "github"
},
"original": {
"owner": "AceSLS",
"repo": "SLSsteam",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"xremap": {
"flake": false,
"locked": {
"lastModified": 1782465938,
"narHash": "sha256-BgRp0y5bkmMwRCdIKHrXIbv6Kl7Hn9IphburN7i7sE8=",
"owner": "k0kubun",
"repo": "xremap",
"rev": "6d1d06436ecd54002257029bc26a22ef058d4b63",
"type": "github"
},
"original": {
"owner": "k0kubun",
"ref": "v0.15.9",
"repo": "xremap",
"type": "github"
}
},
"xremap-flake": {
"inputs": {
"crane": "crane",
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs_4",
"xremap": "xremap"
},
"locked": {
"lastModified": 1782757039,
"narHash": "sha256-4CuFwWsQxoNY4Cxz4+GVEmRrMTR5dUOTKN2eNk1KiEI=",
"owner": "xremap",
"repo": "nix-flake",
"rev": "7f161bede2961279dfde194add6aa53d80a9d26d",
"type": "github"
},
"original": {
"owner": "xremap",
"repo": "nix-flake",
"type": "github"
}
},
"yazi": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_5",
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1785301727,
"narHash": "sha256-Z9g04KWv8aEowWW+QIiADE6ct36oIZmrykTzEh5sQ4M=",
"owner": "sxyazi",
"repo": "yazi",
"rev": "319f90e0eab185a231eef5562215ba322e320286",
"type": "github"
},
"original": {
"owner": "sxyazi",
"repo": "yazi",
"type": "github"
}
},
"zen-browser": {
"inputs": {
"home-manager": "home-manager_2",
"nixpkgs": "nixpkgs_6"
},
"locked": {
"lastModified": 1785224333,
"narHash": "sha256-8DNJUVM10ktK02V9ZIV064CH+fuPF272VCAZBn6arig=",
"owner": "0xc000022070",
"repo": "zen-browser-flake",
"rev": "5f1a4be4d280e1ae06d61a75775a234b1714da3f",
"type": "github"
},
"original": {
"owner": "0xc000022070",
"repo": "zen-browser-flake",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+88
View File
@@ -0,0 +1,88 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
sls-steam = {
url = "github:AceSLS/SLSsteam";
inputs.nixpkgs.follows = "nixpkgs";
};
zen-browser.url = "github:0xc000022070/zen-browser-flake";
xremap-flake.url = "github:xremap/nix-flake";
yazi.url = "github:sxyazi/yazi";
millennium.url = "github:SteamClientHomebrew/Millennium?dir=packages/nix";
gsr-ui = {
url = "github:rPlakama/gsr-ui-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
bcdl-free.url = "git+https://git.pizzasucht.net/KeksNino/free-bandcamp-downloader";
};
outputs =
{
nixpkgs,
home-manager,
zen-browser,
xremap-flake,
sls-steam,
yazi,
millennium,
gsr-ui,
bcdl-free,
...
}@inputs:
let
system = "x86_64-linux";
in
{
nixosConfigurations.desktop = nixpkgs.lib.nixosSystem {
specialArgs = {
inherit
sls-steam
bcdl-free
xremap-flake
system
;
};
modules = [
./hosts/desktop/configuration.nix
./hosts/desktop/hardware-configuration.nix
./modules/common.nix
home-manager.nixosModules.home-manager
gsr-ui.nixosModules.default
{
nixpkgs.overlays = [
yazi.overlays.default
millennium.overlays.default
];
home-manager.users.user = import ./hosts/desktop/home-manager/home.nix;
home-manager.extraSpecialArgs = {
inherit zen-browser;
};
programs.gpu-screen-recorder = {
package = gsr-ui.packages.${system}.gpu-screen-recorder;
enable = true;
ui.enable = true;
};
}
];
};
nixosConfigurations.music-server = nixpkgs.lib.nixosSystem {
specialArgs = { inherit inputs bcdl-free system; };
modules = [
./hosts/music-server/configuration.nix
./hosts/music-server/hardware-configuration.nix
./modules/common.nix
home-manager.nixosModules.home-manager
{
home-manager.users.user = import ./hosts/music-server/home-manager/home.nix;
}
];
};
};
}
+266
View File
@@ -0,0 +1,266 @@
{
pkgs,
sls-steam,
options,
config,
...
}:
let
cpuid-fault-emulation = pkgs.callPackage ../../pkgs/cpuid-fault-emulation {
kernel = config.boot.kernelPackages.kernel;
};
in
{
imports = [
./hardware-configuration.nix
./packages.nix
../../modules/scripts.nix
../../modules/desktop/xremap.nix
];
# DISKS
fileSystems."/media/gamedisk4" = {
device = "/dev/disk/by-uuid/D290EB87C8A1E82A";
fsType = "ntfs3";
options = [
"uid=1000"
"gid=100"
"rw"
"user"
"exec"
"umask=000"
"nofail"
];
};
fileSystems."/media/gamedisk" = {
device = "/dev/disk/by-uuid/5C7C14707C1446E4";
fsType = "ntfs3";
options = [
"uid=1000"
"gid=100"
"rw"
"user"
"exec"
"umask=000"
"nofail"
];
};
fileSystems."/media/gamedisk2" = {
device = "/dev/disk/by-uuid/945A7421DAD8A3CE";
fsType = "ntfs3";
options = [
"uid=1000"
"gid=100"
"rw"
"user"
"exec"
"umask=000"
"nofail"
];
};
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
boot.kernelParams = [
"iomem=relaxed"
"iommu=pt"
"amd_iommu=on"
"kvm.ignore_msrs=1"
"mitigations=off"
"amdgpu.ppfeaturemask=0xffffffff"
"clearcpuid=umip"
];
xdg = {
mime.defaultApplications = {
"inode/directory" = [ "nemo.desktop" ];
"application/x-gnome-saved-search" = [ "nemo.desktop" ];
};
};
hardware.graphics.enable = true;
hardware.graphics.enable32Bit = true;
services.hardware.openrgb.enable = true;
# Bootloader.
boot.loader.grub.enable = true;
boot.loader.grub.device = "nodev";
boot.loader.grub.efiSupport = true;
boot.extraModulePackages = [ cpuid-fault-emulation ];
environment.systemPackages = [
(pkgs.writeShellScriptBin "enable-hypervisor" ''
sudo modprobe -r kvm_amd kvm
sudo modprobe cpuid_fault_emulation
'')
(pkgs.writeShellScriptBin "disable-hypervisor" ''
sudo modprobe -r cpuid_fault_emulation
sudo modprobe kvm_amd kvm
'')
];
networking.hostName = "nixos-desktop";
networking.firewall.enable = false;
networking.networkmanager.enable = true;
# Set your time zone.
time.timeZone = "Europe/Berlin";
# Select internationalisation properties.
i18n.defaultLocale = "en_US.UTF-8";
i18n.extraLocaleSettings = {
LC_ADDRESS = "de_DE.UTF-8";
LC_IDENTIFICATION = "de_DE.UTF-8";
LC_MEASUREMENT = "de_DE.UTF-8";
LC_MONETARY = "de_DE.UTF-8";
LC_NAME = "de_DE.UTF-8";
LC_NUMERIC = "de_DE.UTF-8";
LC_PAPER = "de_DE.UTF-8";
LC_TELEPHONE = "de_DE.UTF-8";
LC_TIME = "de_DE.UTF-8";
};
console.keyMap = "de";
programs.fish.enable = true;
users.users.user = {
isNormalUser = true;
description = "user";
uid = 1000;
extraGroups = [
"networkmanager"
"wheel"
"docker"
];
shell = pkgs.fish;
};
nixpkgs.config.allowUnfree = true;
programs.niri.enable = true;
programs.hyprland = {
enable = true;
xwayland.enable = true;
};
# environment.loginShellInit = ''
# if [ "$(tty)" = "/dev/tty1" ] && [ -z "$WAYLAND_DISPLAY" ]; then
# exec start-hyprland
# fi
# '';
# NFS / MPD
fileSystems."/media/music" = {
device = "192.168.132.149:/media/music";
fsType = "nfs4";
};
boot.supportedFilesystems = [ "nfs4" ];
qt = {
enable = true;
platformTheme = "qt5ct";
style = "breeze";
};
virtualisation.docker.enable = true;
services.gvfs.enable = true;
services.goxlr-utility.enable = true;
services.lact.enable = true;
services.rsync.enable = true;
programs.kdeconnect.enable = true;
programs.neovim.enable = true;
programs.tmux.enable = true;
programs.bat.enable = true;
programs.nh.enable = true;
programs.git = {
enable = true;
config = {
signing = {
key = "51AE8C6E80E6AF3BDFBE2BF804B31D181764898D";
signByDefault = true;
};
user = {
name = "KeksNino";
email = "KeksNino@proton.me";
};
init = {
defaultBranch = "main";
};
push = {
autoSetupRemote = true;
};
commit = {
gpgsign = true;
};
tag = {
gpgsign = true;
};
};
};
programs.gnupg.agent = {
enable = true;
pinentryPackage = pkgs.pinentry-curses;
};
programs.steam = {
enable = true;
package = pkgs.millennium-steam.override {
extraEnv = {
LD_AUDIT = "${sls-steam.packages.${pkgs.stdenv.hostPlatform.system}.sls-steam}/library-inject.so:${
sls-steam.packages.${pkgs.stdenv.hostPlatform.system}.sls-steam
}/SLSsteam.so";
};
};
};
programs.nix-ld = {
enable = true;
libraries =
options.programs.nix-ld.libraries.default
++ (with pkgs; [
dbus # libdbus-1.so.3
fontconfig # libfontconfig.so.1
freetype # libfreetype.so.6
glib # libglib-2.0.so.0
libGL # libGL.so.1
libxkbcommon # libxkbcommon.so.0
libx11 # libX11.so.6
wayland
]);
};
services.pipewire = {
enable = true;
alsa.enable = true;
pulse.enable = true;
jack.enable = true;
};
security.pam.loginLimits = [
{
domain = "@users";
item = "rtprio";
type = "-";
value = "95";
}
{
domain = "@users";
item = "memlock";
type = "-";
value = "unlimited";
}
];
security.rtkit.enable = true;
system.stateVersion = "24.11";
system.autoUpgrade.enable = false;
system.autoUpgrade.allowReboot = true;
}
+68
View File
@@ -0,0 +1,68 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"usbhid"
"usb_storage"
"sd_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/ddabcce9-86a5-4dd8-84f1-3afa99a83752";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/16F5-2D1A";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
fileSystems."/media/gamedisk4" = {
device = "/dev/disk/by-uuid/D290EB87C8A1E82A";
fsType = "ntfs3";
};
fileSystems."/media/music" = {
device = "192.168.132.149:/media/music";
fsType = "nfs4";
};
fileSystems."/media/arch" = {
device = "/dev/disk/by-uuid/8873d98f-c461-4d34-8867-f4ebd5c205d2";
fsType = "ext4";
};
fileSystems."/media/gamedisk" = {
device = "/dev/disk/by-uuid/5C7C14707C1446E4";
fsType = "ntfs3";
};
swapDevices = [
{ device = "/dev/disk/by-uuid/5949c73f-30eb-4956-9e93-70301c88fad8"; }
];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}
+191
View File
@@ -0,0 +1,191 @@
{
pkgs,
config,
zen-browser,
...
}:
{
imports = [
./mpd.nix
zen-browser.homeModules.beta
];
home.username = "user";
home.homeDirectory = "/home/user";
xdg.userDirs = {
enable = true;
createDirectories = false;
extraConfig = {
SCREENSHOTS = "${config.home.homeDirectory}/Pictures/Screenshots";
};
};
xdg.mimeApps = {
enable = true;
defaultApplications = {
"inode/directory" = [ "nemo.desktop" ];
"image/*" = [ "imv-dir.desktop" ];
"image/gif" = [ "imv-dir.desktop" ];
"image/jpeg" = [ "imv-dir.desktop" ];
"image/png" = [ "imv-dir.desktop" ];
"image/webp" = [ "imv-dir.desktop" ];
};
};
dconf = {
settings = {
"org/cinnamon/desktop/applications/terminal" = {
exec = "kitty";
# exec-arg = ""; # argument
};
};
};
home.pointerCursor = {
enable = true;
gtk.enable = true;
package = pkgs.adwaita-icon-theme;
name = "Adwaita";
size = 20;
};
programs.zen-browser = {
enable = true;
setAsDefaultBrowser = true;
};
programs.yazi.enable = true;
programs.yazi.enableFishIntegration = true;
programs.fzf.enable = true;
programs.fzf.enableFishIntegration = true;
programs.eza = {
enable = true;
enableFishIntegration = true;
git = true;
icons = "always";
extraOptions = [
"-1"
];
};
programs.pywal = {
enable = true;
package = pkgs.pywal16;
};
programs.lazygit.enable = true;
programs.lazygit.enableFishIntegration = true;
programs.rbw = {
enable = true;
settings = {
pinentry = pkgs.pinentry-curses;
email = "KeksNino@proton.me";
base_url = "https://vw.pizzasucht.net";
};
};
programs.distrobox.enable = true;
services.podman.enable = true;
programs.rofi.enable = true;
programs.rofi.plugins = [ pkgs.rofi-calc ];
services.polkit-gnome.enable = true;
services.cliphist.enable = true;
programs.btop.enable = true;
services.udiskie.enable = true;
programs.cava.enable = true;
programs.equibop.enable = true;
services.awww.enable = true;
services.mpdris2.enable = true;
programs.fastfetch.enable = true;
programs.prismlauncher.enable = true;
programs.fd.enable = true;
programs.uv.enable = true;
programs.satty.enable = true;
programs.mpvpaper.enable = true;
programs.obsidian.enable = true;
programs.gcc.enable = true;
programs.mpv.enable = true;
programs.imv.enable = true;
programs.aria2.enable = true;
programs.qutebrowser = {
enable = true;
settings = {
colors.webpage.preferred_color_scheme = "dark";
auto_save.session = true;
scrolling.smooth = true;
content.autoplay = false;
content.blocking.method = "both";
url.default_page = "https://priv.au";
};
extraConfig = ''
c.qt.args = ["disable-features=AcceleratedVideoDecodeLinuxGL"]
'';
keyBindings = {
normal = {
"M" = "hint links spawn mpv {hint-url}";
};
};
searchEngines = {
DEFAULT = "https://priv.au/search?q={}";
};
};
home.sessionPath = [
"${config.home.homeDirectory}/.local/bin"
];
home.sessionVariables = {
EDITOR = "nvim";
};
programs.fish = {
enable = true;
interactiveShellInit = ''
nitch
set -g fish_key_bindings fish_hybrid_key_bindings
set fish_greeting
'';
shellAliases = {
ls = "eza -al --no-time --no-user --no-permissions --no-filesize";
ll = "eza -la";
cat = "bat";
cd = "z";
cp = "cp -v";
ff = "fastfetch";
grep = "grep -i";
hyprland = "start-hyprland";
mv = "mv -v";
n = "nvim";
py = "uv run";
r = "rsync -avh --info=progress2";
rm = "gomi";
ssh = "kitty +kitten ssh";
nd = "nix develop";
};
};
programs.zoxide.enable = true;
programs.zoxide.enableFishIntegration = true;
home.file.".local/share/Steam/steam.cfg".text = ''
BootStrapperInhibitAll=enable
BootStrapperForceSelfUpdate=disable
'';
gtk = {
enable = true;
# theme = {
# name = "Materia-dark-compact";
# package = pkgs.materia-theme-transparent;
# };
colorScheme = "dark";
};
home.stateVersion = "26.05";
}
+225
View File
@@ -0,0 +1,225 @@
{
services.mpd = {
enable = true;
musicDirectory = "/home/user/Music";
playlistDirectory = "/home/user/Music/Playlists";
dbFile = "/home/user/.config/mpd/database";
# log_file = "~/.config/mpd/log";
extraConfig = ''
save_absolute_paths_in_playlists "yes"
metadata_to_use "+comment"
audio_output {
type "pipewire"
name "Pipewire Sound Server"
}
audio_output {
type "fifo"
name "my_fifo"
path "/tmp/mpd.fifo"
format "44100:16:2"
}
auto_update "yes"
'';
network.listenAddress = "/home/user/.config/mpd/socket";
};
services.mpdscribble = {
enable = true;
endpoints = {
"last.fm" = {
passwordFile = "/home/user/lastfm_password";
username = "KeksNino";
};
};
};
programs.rmpc.enable = true;
programs.rmpc.config = ''
#![enable(implicit_some)]
#![enable(unwrap_newtypes)]
#![enable(unwrap_variant_newtypes)]
(
address: "/home/user/.config/mpd/socket",
password: None,
theme: Some("default"),
cache_dir: Some("/home/user/Music/rmpc"),
on_song_change: None,
volume_step: 5,
max_fps: 60,
scrolloff: 0,
wrap_navigation: false,
enable_mouse: true,
enable_config_hot_reload: true,
status_update_interval_ms: 1000,
rewind_to_start_sec: None,
reflect_changes_to_playlist: false,
select_current_song_on_change: false,
browser_song_sort: [Track, Disc, Artist, Title],
directories_sort: SortFormat(group_by_type: true, reverse: false),
album_art: (
method: Kitty,
max_size_px: (width: 1200, height: 1200),
disabled_protocols: ["http://", "https://"],
vertical_align: Center,
horizontal_align: Center,
),
keybinds: (
global: {
":": CommandMode,
",": VolumeDown,
"s": Stop,
".": VolumeUp,
"<Tab>": NextTab,
"<S-Tab>": PreviousTab,
"1": SwitchToTab("Queue"),
"2": SwitchToTab("Files"),
"3": SwitchToTab("Playlists"),
"4": SwitchToTab("Artists"),
"5": SwitchToTab("Albums"),
"6": SwitchToTab("Search"),
//"7": SwitchToTab("Album Artists"),
"q": Quit,
">": NextTrack,
"p": TogglePause,
"<": PreviousTrack,
"f": SeekForward,
"z": ToggleRepeat,
"x": ToggleRandom,
"c": ToggleConsume,
"v": ToggleSingle,
"b": SeekBack,
"~": ShowHelp,
"u": Update,
"U": Rescan,
"I": ShowCurrentSongInfo,
"O": ShowOutputs,
"P": ShowDecoders,
"R": AddRandom,
"<C-p>": ExternalCommand(command: ["/home/user/.scripts/check-playlist.sh"], description: "Check if selected song is already in a playlist"),
},
navigation: {
"k": Up,
"j": Down,
"h": Left,
"l": Right,
"<Up>": Up,
"<Down>": Down,
"<Left>": Left,
"<Right>": Right,
"<C-k>": PaneUp,
"<C-j>": PaneDown,
"<C-h>": PaneLeft,
"<C-l>": PaneRight,
"<C-u>": UpHalf,
"N": PreviousResult,
"a": Add,
"A": AddAll,
"r": Rename,
"n": NextResult,
"g": Top,
"<Space>": Select,
"<C-Space>": InvertSelection,
"G": Bottom,
"<CR>": Confirm,
"i": FocusInput,
"J": MoveDown,
"<C-d>": DownHalf,
"/": EnterSearch,
"<C-c>": Close,
"<Esc>": Close,
"K": MoveUp,
"D": Delete,
"B": ShowInfo,
},
queue: {
"D": DeleteAll,
"<CR>": Play,
"<C-s>": Save,
"a": AddToPlaylist,
"d": Delete,
"C": JumpToCurrent,
"X": Shuffle,
},
),
search: (
case_sensitive: false,
mode: Contains,
tags: [
(value: "any", label: "Any Tag"),
(value: "artist", label: "Artist"),
(value: "album", label: "Album"),
(value: "albumartist", label: "Album Artist"),
(value: "title", label: "Title"),
(value: "filename", label: "Filename"),
(value: "genre", label: "Genre"),
],
),
artists: (
album_display_mode: SplitByDate,
album_sort_by: Date,
),
cava: (
framerate: 60,
autosens: true,
sensitivity: 110, // default 100
lower_cutoff_freq: 50, // not passed to cava if not provided
higher_cutoff_freq: 10000, // not passed to cava if not provided
input: (
method: Fifo,
source: "/tmp/mpd.fifo",
sample_rate: 44100,
channels: 2,
sample_bits: 16,
),
smoothing: (
noise_reduction: 77,
monstercat: true,
waves: false,
),
eq: []
),
tabs: [
(
name: "Queue",
pane: Split(
direction: Horizontal,
panes: [
(size: "40%", pane: Pane(AlbumArt)),
(size: "60%", pane: Split(
direction: Vertical,
panes: [
(size: "95%", pane: Pane(Queue)),
(size: "30%", pane: Pane(Cava)),
],
)),
],
),
),
(
name: "Files",
pane: Pane(Directories),
),
(
name: "Playlists",
pane: Pane(Playlists),
),
(
name: "Artists",
pane: Pane(Artists),
),
(
name: "Albums",
pane: Pane(Albums),
),
(
name: "Search",
pane: Pane(Search),
),
//(
// name: "Album Artists",
// pane: Pane(AlbumArtists),
//),
],
)
'';
}
+69
View File
@@ -0,0 +1,69 @@
{ pkgs, ... }: {
environment.systemPackages = with pkgs; [
wget
kitty
gdu
jamesdsp
grimblast
nitch
wl-clipboard
unzip
_7zz
tree-sitter
nodejs
nixfmt
decibels
nwg-look
mpc
python3
basedpyright
waypaper
renoise
pwvucontrol
lsof
picard
exiftool
tlrc
libnotify
trash-cli
spek
ffmpeg
# wine
ripgrep
nemo-with-extensions
papirus-icon-theme
rust-analyzer
scdl
motrix-next
onefetch
smartmontools
qpwgraph
pipewire.jack
sysz
gomi
noctalia-shell
discordchatexporter-desktop
act
ayugram-desktop
playerctl
musicpresence
nixd
libsForQt5.qt5ct
qt6Packages.qt6ct
kdePackages.breeze
kdePackages.breeze.qt5
gnumake
samrewritten
gale
losslesscut
tree
stow
];
fonts.packages = with pkgs; [
nerd-fonts.caskaydia-mono
nerd-fonts.symbols-only
nerd-fonts.jetbrains-mono
];
}
+87
View File
@@ -0,0 +1,87 @@
{
pkgs,
...
}:
{
imports = [
./hardware-configuration.nix
./packages.nix
../../modules/server
];
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
fileSystems."/media/music" = {
device = "/dev/disk/by-uuid/8e6ae06e-a4b5-4ac8-ad15-cb288b14715f";
fsType = "ext4";
};
fileSystems."/home/user/backup" = {
device = "/dev/disk/by-uuid/db7d96f1-5c72-42dc-abc1-00de9b73f017";
fsType = "ext4";
};
networking.hostId = "6054e036";
networking.hostName = "nixos-music";
networking.networkmanager.enable = true;
networking.firewall.enable = false;
time.timeZone = "Europe/Berlin";
i18n.defaultLocale = "en_US.UTF-8";
console = {
font = "Lat2-Terminus16";
keyMap = "de";
};
programs.fish.enable = true;
users.users.user = {
isNormalUser = true;
description = "user";
uid = 1000;
extraGroups = [
"networkmanager"
"wheel"
"docker"
];
shell = pkgs.fish;
};
services.tailscale.enable = true;
services.rsync.enable = true;
programs.nix-ld.enable = true;
programs.nh.enable = true;
programs.neovim.enable = true;
programs.tmux.enable = true;
programs.bat.enable = true;
programs.git = {
enable = true;
config = {
user = {
name = "KeksNino";
email = "KeksNino@proton.me";
};
init = {
defaultBranch = "main";
};
push = {
autoSetupRemote = true;
};
};
};
services.openssh = {
enable = true;
openFirewall = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "no";
};
};
system.stateVersion = "26.05";
}
@@ -0,0 +1,61 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"usb_storage"
"uas"
"sd_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "zpool/root";
fsType = "zfs";
};
fileSystems."/var" = {
device = "zpool/var";
fsType = "zfs";
};
fileSystems."/nix" = {
device = "zpool/nix";
fsType = "zfs";
};
fileSystems."/home" = {
device = "zpool/home";
fsType = "zfs";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/FCFD-4472";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}
+52
View File
@@ -0,0 +1,52 @@
{
programs.beets.enable = true;
# programs.beets.package = pkgs.beets.override {
# pluginOverrides = {
# filetote = {
# enable = true;
# };
# };
# };
programs.beets.settings = {
directory = "/home/user/Music/Tracks";
plugins = [
"fetchart"
"badfiles"
"edit"
"fish"
"hook"
];
import = {
copy = false;
move = true;
autotag = false;
write = true;
};
paths = {
default = "$albumartist/$album/$track. $artist - $title";
};
fetchart = {
auto = true;
sources = [ "filesystem" ];
};
badfiles = {
check_on_import = true;
};
hook = {
hooks = [
{
event = "item_imported";
command = "echo test";
}
];
};
};
}
+61
View File
@@ -0,0 +1,61 @@
{ pkgs, config, ... }:
{
imports = [
./beets.nix
];
home.username = "user";
home.homeDirectory = "/home/user";
programs.yazi.enable = true;
programs.yazi.enableFishIntegration = true;
programs.fzf.enable = true;
programs.fzf.enableFishIntegration = true;
programs.zoxide.enable = true;
programs.zoxide.enableFishIntegration = true;
programs.gcc.enable = true;
programs.eza = {
enable = true;
enableFishIntegration = true;
git = true;
icons = "always";
extraOptions = [
"-1"
];
};
programs.btop.enable = true;
programs.fastfetch.enable = true;
programs.fd.enable = true;
home.sessionVariables = {
EDITOR = "nvim";
};
programs.fish = {
enable = true;
interactiveShellInit = ''
set -g fish_key_bindings fish_hybrid_key_bindings
set fish_greeting
'';
shellAliases = {
ls = "eza -al --no-time --no-user --no-permissions --no-filesize";
ll = "eza -la";
cat = "bat";
cd = "z";
cp = "cp -v";
ff = "fastfetch";
grep = "grep -i";
mv = "mv -v";
n = "nvim";
r = "rsync -avh --info=progress2";
rm = "gomi";
};
};
home.stateVersion = "26.05";
}
+30
View File
@@ -0,0 +1,30 @@
{ pkgs, ... }: {
environment.systemPackages = (
with pkgs;
[
wget
nixfmt
tree-sitter
lsof
exiftool
trash-cli
ffmpeg
unzip
scdl
aria2
nodejs
gomi
bun
croc
mp3val
flac
(python314.withPackages (
ps: with ps; [
requests
python-dotenv
]
))
]
);
}
+9
View File
@@ -0,0 +1,9 @@
{ system, bcdl-free, ... }:
{
environment.systemPackages = [ bcdl-free.packages.${system}.default ];
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.backupFileExtension = "backup";
}
+37
View File
@@ -0,0 +1,37 @@
{ xremap-flake, ... }:
{
imports = [
xremap-flake.nixosModules.default
];
services.xremap.enable = true;
services.xremap.withHypr = true;
services.xremap.config.modmap = [
{
name = "Global";
remap = {
"CapsLock" = "Esc";
};
}
];
services.xremap.config.keymap = [
{
name = "curly braces";
remap = {
"KEY_SEMICOLON" = "RIGHTALT-KEY_7";
}; # ä -> }
remap = {
"KEY_APOSTROPHE" = "RIGHTALT-KEY_0";
}; # ö -> {
remap = {
"RIGHTALT-KEY_A" = "KEY_APOSTROPHE";
}; # altgr + a -> ä
remap = {
"RIGHTALT-KEY_W" = "KEY_SEMICOLON";
}; # altgr + w -> ö
}
];
}
+25
View File
@@ -0,0 +1,25 @@
{ pkgs, ... }:
{
environment.systemPackages = [
(pkgs.writeShellScriptBin "scan" ''
find . -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.m4a" -o -iname "*.ogg" -o -iname "*.wav" -o -iname "*.aiff" \) -print0 |
xargs -0 -P"$(nproc)" -I{} bash -c '
if ffprobe -v quiet -show_entries format_tags \
-of default=nw=1:nk=1 "$1" 2>/dev/null |
grep -q -E "Visit https?://[^ ]+"; then
echo "Match found in: $1"
fi
' _ {} | sort
'')
(pkgs.writeShellScriptBin "2scan" ''
find . -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.m4a" -o -iname "*.ogg" -o -iname "*.wav" -o -iname "*.aiff" \) -print0 |
xargs -0 -P"$(nproc)" -I{} sh -c '
if ! ffprobe -v quiet -show_entries format_tags \
-of default=nw=1:nk=1 "$1" 2>/dev/null |
grep -q -E "Visit https?://[^ ]+"; then
echo "Match found in: $1"
fi
' _ {}
'')
];
}
+11
View File
@@ -0,0 +1,11 @@
{
imports = [
./slskd.nix
./navidrome.nix
./nfs.nix
./services.nix
./smb.nix
./zfs.nix
../scripts.nix
];
}
+20
View File
@@ -0,0 +1,20 @@
{ lib, ... }:
{
services.navidrome = {
enable = true;
user = "user";
settings = {
MusicFolder = "/home/user/Music";
PlaylistsPath = "Playlists";
Address = "0.0.0.0";
ScanSchedule = "@every 24h";
EnableInsightsCollector = false;
Scanner.PurgeMissing = "always";
};
};
systemd.services.navidrome.serviceConfig = {
ProtectHome = lib.mkForce false;
};
}
+15
View File
@@ -0,0 +1,15 @@
{
services.nfs.server = {
enable = true;
exports = {
"/media/music" = {
"192.168.132.0/24" = [
"rw"
"async"
"no_subtree_check"
"fsid=1"
];
};
};
};
}
+31
View File
@@ -0,0 +1,31 @@
{ pkgs, ... }:
{
systemd.timers."bc-wishlist-scraper" = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "daily";
Unit = "bc-wishlist-scraper.service";
};
};
systemd.services."bc-wishlist-scraper" = {
script = ''
${pkgs.bash}/bin/bash /home/user/bc-wishlist-scraper/run.sh
'';
};
systemd.user.timers."scdl" = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "daily";
Unit = "scdl.service";
};
};
systemd.user.services."scdl" = {
script = ''
cd ~/Music/scdl && ${pkgs.scdl}/bin/scdl me -f -c --download-archive archive.txt
'';
};
}
+118
View File
@@ -0,0 +1,118 @@
{ pkgs, ... }:
{
services.slskd = {
enable = true;
environmentFile = /home/user/slskd_secrets;
user = "user";
settings = {
remote_configuration = true;
remote_file_management = true;
directories = {
downloads = "/media/music/Soulseek";
};
shares = {
directories = [
"/media/music/Tracks"
"/media/music/Tracks2"
"/media/music/samples"
];
};
rooms = [
"Rare Music"
"nicotine"
"! ! LGBTQ+ ! !"
"lossless"
"Linux"
"Breakcore"
"Jungle"
];
transfers = {
upload = {
speed_limit = 3700;
};
groups = {
blacklisted = {
members = [
"relay"
"smbd"
];
};
user_defined = {
my_buddies = {
upload = {
priority = 1;
strategy = "firstinfirstout";
slots = 1000;
};
members = [
"noamifur"
"mesityl"
"diphenhydramine"
"monfi"
];
};
};
};
};
soulseek = {
description = ''
Fuck DRM. Fuck Spotify
Discord: keksnino
https://www.last.fm/user/KeksNino
'';
listen_port = 32895;
};
feature = {
swagger = true;
};
integrations = {
webhooks = {
discord_relay = {
call = {
url = "http://localhost:5001/slskd_webhook";
};
on = [ "UploadFileComplete" ];
timeout = 5000;
retry = {
attempts = 3;
};
};
};
};
retention = {
files = {
incomplete = 20160;
};
};
};
};
systemd.user.services.slskd-speed = {
enable = true;
description = "Soulseek True Speed Shower";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
Environment = "PYTHONUNBUFFERED=1";
Restart = "on-failure";
RestartSec = 30;
StandardOutput = "journal";
StandardError = "journal";
EnvironmentFile = "/home/user/slskd_secrets";
ExecStart = ''
${pkgs.python3}/bin/python3 /home/user/slskd-speed.py \
-u "$SLSKD_SLSK_USERNAME" \
-p "$SLSKD_SLSK_PASSWORD" \
-s 30mbit/s \
--slskd-url http://localhost:5030 \
--slskd-key "$SLSKD_API_KEY" \
--loop \
--poll-interval 30 \
--interval 900
'';
};
};
}
+24
View File
@@ -0,0 +1,24 @@
{
services.samba = {
enable = true;
openFirewall = true;
settings = {
global = {
"workgroup" = "WORKGROUP";
"server string" = "smbnix";
"security" = "user";
"interfaces" = "lo eno1 tailscale0";
"hosts allow" = "192.168.132.0/24 100.64.0.0/10 127.0.0.1 localhost";
"hosts deny" = "0.0.0.0/0";
"guest account" = "nobody";
};
"Music" = {
"path" = "/media/music";
"browseable" = "yes";
"read only" = "no";
"guest ok" = "no";
"valid users" = "user";
};
};
};
}
+39
View File
@@ -0,0 +1,39 @@
{
boot.loader.grub = {
enable = true;
zfsSupport = true;
efiSupport = true;
efiInstallAsRemovable = true;
mirroredBoots = [
{
devices = [ "nodev" ];
path = "/boot";
}
];
};
fileSystems."/" = {
device = "zpool/root";
fsType = "zfs";
};
fileSystems."/nix" = {
device = "zpool/nix";
fsType = "zfs";
};
fileSystems."/var" = {
device = "zpool/var";
fsType = "zfs";
};
fileSystems."/home" = {
device = "zpool/home";
fsType = "zfs";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/FCFD-4472";
fsType = "vfat";
};
boot.zfs.forceImportRoot = false;
services.zfs.autoScrub.enable = true;
swapDevices = [ ];
}
+28
View File
@@ -0,0 +1,28 @@
{ stdenv, kernel }:
stdenv.mkDerivation {
pname = "cpuid_fault_emulation";
version = "0.1";
src = ./source;
nativeBuildInputs = kernel.moduleBuildDependencies;
hardeningDisable = [
"pic"
"format"
];
# Patch the hardcoded host paths to point into the Nix store
postPatch = ''
substituteInPlace Makefile \
--replace '/lib/modules/$(KERNEL)/build' '${kernel.dev}/lib/modules/${kernel.modDirVersion}/build' \
--replace '$(shell uname -r)' '${kernel.modDirVersion}'
'';
installPhase = ''
install -D cpuid_fault_emulation.ko \
$out/lib/modules/${kernel.modDirVersion}/extra/cpuid_fault_emulation.ko
'';
meta.platforms = [ "x86_64-linux" ];
}
@@ -0,0 +1,23 @@
obj-m += cpuid_fault_emulation.o
cpuid_fault_emulation-y := src/cpuid_fault_emulation.o src/capture_context.o src/run_vm.o
PWD := $(CURDIR)
ccflags-y += -I$(src)/inc
asflags-y += -I$(src)/inc
NOCLANG := CONFIG_CLANG_VERSION=0
KERNEL := $(if $(KERNEL),$(KERNEL),$(shell uname -r))
ifneq ($(shell grep "CONFIG_CLANG_VERSION" /lib/modules/$(KERNEL)/build/.config), $(NOCLANG))
LLVM := LLVM=1
endif
all:
echo $(MAKE) $(LLVM) -C /lib/modules/$(KERNEL)/build M=$(PWD) modules
$(MAKE) $(LLVM) -C /lib/modules/$(KERNEL)/build M=$(PWD) modules
clean:
$(MAKE) -C /lib/modules/$(KERNEL)/build M=$(PWD) clean
@@ -0,0 +1,7 @@
PACKAGE_NAME="cpuid_fault_emulation"
PACKAGE_VERSION="0.1"
BUILT_MODULE_NAME="cpuid_fault_emulation"
BUILT_MODULE_NAME="cpuid_fault_emulation"
DEST_MODULE_LOCATION="/updates"
AUTOINSTALL="yes"
@@ -0,0 +1,101 @@
#ifndef HOST_STATE_H
#define HOST_STATE_H
#define GUEST_RAX_OFFSET 0x00
#define GUEST_RBX_OFFSET 0x08
#define GUEST_RCX_OFFSET 0x10
#define GUEST_RDX_OFFSET 0x18
#define GUEST_RSI_OFFSET 0x20
#define GUEST_RDI_OFFSET 0x28
#define GUEST_RSP_OFFSET 0x30
#define GUEST_RBP_OFFSET 0x38
#define GUEST_R8_OFFSET 0x40
#define GUEST_R9_OFFSET 0x48
#define GUEST_R10_OFFSET 0x50
#define GUEST_R11_OFFSET 0x58
#define GUEST_R12_OFFSET 0x60
#define GUEST_R13_OFFSET 0x68
#define GUEST_R14_OFFSET 0x70
#define GUEST_R15_OFFSET 0x78
#define GUEST_RIP_OFFSET 0x80
#define GUEST_RFLAGS_OFFSET 0x88
#define HOST_STACK_OFFSET 0x000
#define HOST_STACK_SIZE 0x1000
#define HOST_VMCB_OFFSET 0x1000
#define HOST_HOST_VMCB_OFFSET 0x2000
#define HOST_HOST_SAVE_OFFSET 0x3000
#define HOST_VMCB_PA_OFFSET 0x4000
#define HOST_HOST_VMCB_PA_OFFSET 0x4008
#define HOST_REGISTER_OFFSET 0x4010
#ifndef __ASSEMBLER__
#include "vmcb_layout.h"
struct GuestRegisters {
u64 rax;
u64 rbx;
u64 rcx;
u64 rdx;
u64 rsi;
u64 rdi;
u64 rsp;
u64 rbp;
u64 r8;
u64 r9;
u64 r10;
u64 r11;
u64 r12;
u64 r13;
u64 r14;
u64 r15;
} __attribute__((packed));
static_assert(GUEST_RAX_OFFSET == offsetof(struct GuestRegisters, rax));
static_assert(GUEST_RBX_OFFSET == offsetof(struct GuestRegisters, rbx));
static_assert(GUEST_RCX_OFFSET == offsetof(struct GuestRegisters, rcx));
static_assert(GUEST_RDX_OFFSET == offsetof(struct GuestRegisters, rdx));
static_assert(GUEST_RSI_OFFSET == offsetof(struct GuestRegisters, rsi));
static_assert(GUEST_RDI_OFFSET == offsetof(struct GuestRegisters, rdi));
static_assert(GUEST_RSP_OFFSET == offsetof(struct GuestRegisters, rsp));
static_assert(GUEST_RBP_OFFSET == offsetof(struct GuestRegisters, rbp));
static_assert(GUEST_R8_OFFSET == offsetof(struct GuestRegisters, r8));
static_assert(GUEST_R9_OFFSET == offsetof(struct GuestRegisters, r9));
static_assert(GUEST_R10_OFFSET == offsetof(struct GuestRegisters, r10));
static_assert(GUEST_R11_OFFSET == offsetof(struct GuestRegisters, r11));
static_assert(GUEST_R12_OFFSET == offsetof(struct GuestRegisters, r12));
static_assert(GUEST_R13_OFFSET == offsetof(struct GuestRegisters, r13));
static_assert(GUEST_R14_OFFSET == offsetof(struct GuestRegisters, r14));
static_assert(GUEST_R15_OFFSET == offsetof(struct GuestRegisters, r15));
struct InitialContext {
struct GuestRegisters regs;
u64 rip;
u64 rflags;
} __attribute__((packed));
static_assert(GUEST_RIP_OFFSET == offsetof(struct InitialContext, rip));
static_assert(GUEST_RFLAGS_OFFSET == offsetof(struct InitialContext, rflags));
struct ProcessorStatus {
u8 host_stack[HOST_STACK_SIZE];
struct VMCB vmcb;
struct VMCB host_vmcb;
u8 host_save[0x1000];
u64 vmcb_pa;
u64 host_vmcb_pa;
struct GuestRegisters guest_registers;
void* page_ptr;
u64 prev_hsave_pa;
bool cpuid_fault;
} __attribute__((packed));
static_assert(offsetof(struct ProcessorStatus, host_stack) == HOST_STACK_OFFSET);
static_assert(offsetof(struct ProcessorStatus, vmcb) == HOST_VMCB_OFFSET);
static_assert(offsetof(struct ProcessorStatus, host_vmcb) == HOST_HOST_VMCB_OFFSET);
static_assert(offsetof(struct ProcessorStatus, host_save) == HOST_HOST_SAVE_OFFSET);
static_assert(offsetof(struct ProcessorStatus, vmcb_pa) == HOST_VMCB_PA_OFFSET);
static_assert(offsetof(struct ProcessorStatus, host_vmcb_pa) == HOST_HOST_VMCB_PA_OFFSET);
static_assert(offsetof(struct ProcessorStatus, guest_registers) == HOST_REGISTER_OFFSET);
#endif
#endif
@@ -0,0 +1,574 @@
#ifndef VMCB_LAYOUT_H
#define VMCB_LAYOUT_H
#include <linux/types.h>
struct InterceptVector0 {
u32 read_cr0 : 1;
u32 read_cr1 : 1;
u32 read_cr2 : 1;
u32 read_cr3 : 1;
u32 read_cr4 : 1;
u32 read_cr5 : 1;
u32 read_cr6 : 1;
u32 read_cr7 : 1;
u32 read_cr8 : 1;
u32 read_cr9 : 1;
u32 read_cr10 : 1;
u32 read_cr11 : 1;
u32 read_cr12 : 1;
u32 read_cr13 : 1;
u32 read_cr14 : 1;
u32 read_cr15 : 1;
u32 write_cr0 : 1;
u32 write_cr1 : 1;
u32 write_cr2 : 1;
u32 write_cr3 : 1;
u32 write_cr4 : 1;
u32 write_cr5 : 1;
u32 write_cr6 : 1;
u32 write_cr7 : 1;
u32 write_cr8 : 1;
u32 write_cr9 : 1;
u32 write_cr10 : 1;
u32 write_cr11 : 1;
u32 write_cr12 : 1;
u32 write_cr13 : 1;
u32 write_cr14 : 1;
u32 write_cr15 : 1;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector0) == 4);
struct InterceptVector1 {
u32 read_dr0 : 1;
u32 read_dr1 : 1;
u32 read_dr2 : 1;
u32 read_dr3 : 1;
u32 read_dr4 : 1;
u32 read_dr5 : 1;
u32 read_dr6 : 1;
u32 read_dr7 : 1;
u32 read_dr8 : 1;
u32 read_dr9 : 1;
u32 read_dr10 : 1;
u32 read_dr11 : 1;
u32 read_dr12 : 1;
u32 read_dr13 : 1;
u32 read_dr14 : 1;
u32 read_dr15 : 1;
u32 write_dr0 : 1;
u32 write_dr1 : 1;
u32 write_dr2 : 1;
u32 write_dr3 : 1;
u32 write_dr4 : 1;
u32 write_dr5 : 1;
u32 write_dr6 : 1;
u32 write_dr7 : 1;
u32 write_dr8 : 1;
u32 write_dr9 : 1;
u32 write_dr10 : 1;
u32 write_dr11 : 1;
u32 write_dr12 : 1;
u32 write_dr13 : 1;
u32 write_dr14 : 1;
u32 write_dr15 : 1;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector1) == 4);
struct InterceptVector2 {
u32 exception_vector0 : 1;
u32 exception_vector1 : 1;
u32 exception_vector2 : 1;
u32 exception_vector3 : 1;
u32 exception_vector4 : 1;
u32 exception_vector5 : 1;
u32 exception_vector6 : 1;
u32 exception_vector7 : 1;
u32 exception_vector8 : 1;
u32 exception_vector9 : 1;
u32 exception_vector10 : 1;
u32 exception_vector11 : 1;
u32 exception_vector12 : 1;
u32 exception_vector13 : 1;
u32 exception_vector14 : 1;
u32 exception_vector15 : 1;
u32 exception_vector16 : 1;
u32 exception_vector17 : 1;
u32 exception_vector18 : 1;
u32 exception_vector19 : 1;
u32 exception_vector20 : 1;
u32 exception_vector21 : 1;
u32 exception_vector22 : 1;
u32 exception_vector23 : 1;
u32 exception_vector24 : 1;
u32 exception_vector25 : 1;
u32 exception_vector26 : 1;
u32 exception_vector27 : 1;
u32 exception_vector28 : 1;
u32 exception_vector29 : 1;
u32 exception_vector30 : 1;
u32 exception_vector31 : 1;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector2) == 4);
struct InterceptVector3 {
u32 intr : 1;
u32 nmi : 1;
u32 smi : 1;
u32 init : 1;
u32 vintr : 1;
u32 cr0_writes : 1;
u32 idtr_read : 1;
u32 gdtr_read : 1;
u32 ldtr_read : 1;
u32 tr_read : 1;
u32 idtr_write : 1;
u32 gdtr_write : 1;
u32 ldtr_write : 1;
u32 tr_write : 1;
u32 rdtsc_inst : 1;
u32 rdpmc_inst : 1;
u32 pushf_inst : 1;
u32 popf_inst : 1;
u32 cpuid_inst : 1;
u32 rsm_inst : 1;
u32 iret_inst : 1;
u32 intn_inst : 1;
u32 invd_inst : 1;
u32 pause_inst : 1;
u32 hlt_inst : 1;
u32 invlpg_inst : 1;
u32 invlpga_inst : 1;
u32 ioio_prot : 1;
u32 msr_prot : 1;
u32 task_switches : 1;
u32 ferr_freeze : 1;
u32 shutdown : 1;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector3) == 4);
struct InterceptVector4 {
u32 vmrun_inst : 1;
u32 vmcall_inst : 1;
u32 vmload_inst : 1;
u32 vmsave_inst : 1;
u32 stgi_inst : 1;
u32 clgi_inst : 1;
u32 skinit_inst : 1;
u32 rdtscp_inst : 1;
u32 icebp_inst : 1;
u32 wbinvd_inst : 1;
u32 monitor_inst : 1;
u32 mwait_inst_uncond : 1;
u32 mwait_inst_armed : 1;
u32 xsetbv_inst : 1;
u32 rdpru_inst : 1;
u32 efer_write : 1;
u32 write_cr0_after : 1;
u32 write_cr1_after : 1;
u32 write_cr2_after : 1;
u32 write_cr3_after : 1;
u32 write_cr4_after : 1;
u32 write_cr5_after : 1;
u32 write_cr6_after : 1;
u32 write_cr7_after : 1;
u32 write_cr8_after : 1;
u32 write_cr9_after : 1;
u32 write_cr10_after : 1;
u32 write_cr11_after : 1;
u32 write_cr12_after : 1;
u32 write_cr13_after : 1;
u32 write_cr14_after : 1;
u32 write_cr15_after : 1;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector4) == 4);
struct InterceptVector5 {
u32 invlpgb_inst_all : 1;
u32 invlpgb_inst_ill : 1;
u32 invpcid_inst : 1;
u32 mcommit_inst : 1;
u32 tlbsync_inst : 1;
u32 bus_lock_op : 1;
u32 hlt_inst_npen : 1;
u32 reserved : 25;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector5) == 4);
struct TLBControl {
u32 tlb_control : 8;
u32 allow_larger_rap : 1;
u32 clear_rap : 1;
u32 reserved : 22;
} __attribute__((packed));
static_assert(sizeof(struct TLBControl) == 4);
struct VirtualInterruptControl {
u64 v_tpr : 4;
u64 v_tpr_mbz : 4;
u64 v_irq : 1;
u64 v_gif : 1;
u64 reserved_0 : 1;
u64 v_nmi : 1;
u64 v_nmi_mask : 1;
u64 reserved_1 : 3;
u64 v_intr_prio : 4;
u64 v_ign_tpr : 1;
u64 reserved_2 : 3;
u64 v_intr_masking : 1;
u64 v_gif_enable : 1;
u64 v_nmi_enable : 1;
u64 reserved_3 : 3;
u64 x2avic_enable : 1;
u64 avic_enable : 1;
u64 v_intr_vector : 8;
u64 reserved_4 : 24;
} __attribute__((packed));
static_assert(sizeof(struct VirtualInterruptControl) == 8);
struct InterruptShadow {
u64 interrupt_shadow : 1;
u64 guest_interrupt_mask : 1;
u64 reserved : 62;
} __attribute__((packed));
static_assert(sizeof(struct InterruptShadow) == 8);
struct VirtualizationControl0 {
u64 np_enable : 1;
u64 sev_enable : 1;
u64 sev_es : 1;
u64 guest_mode_execute_trap : 1;
u64 ssscheck_en : 1;
u64 virtual_transparent_encryption : 1;
u64 read_only_guest_page_tables : 1;
u64 invlpg_tlbsync_enable : 1;
u64 reserved : 56;
} __attribute__((packed));
static_assert(sizeof(struct VirtualizationControl0) == 8);
struct AVICAPICBar {
u64 avic_apic_bar : 52;
u64 reserved : 12;
} __attribute__((packed));
static_assert(sizeof(struct AVICAPICBar) == 8);
struct VirtualizationControl1 {
u64 lbr_virtualization_enable : 1;
u64 vmsave_vmload_virtualization_enable : 1;
u64 ibs_virtualization_enable : 1;
u64 pmc_virtualization_enable : 1;
u64 ignored : 60;
} __attribute__((packed));
static_assert(sizeof(struct VirtualizationControl1) == 8);
struct GuestInstruction {
u8 bytes_fetched;
u8 guest_instruction[0xF];
} __attribute__((packed));
static_assert(sizeof(struct GuestInstruction) == 0x10);
struct AVICAPICBackingPage {
u64 avic_apic_backing_page_pointer : 52;
u64 reserved : 12;
} __attribute__((packed));
static_assert(sizeof(struct AVICAPICBackingPage) == 8);
struct AVICLogicalTablePointer {
u64 reserved_0 : 12;
u64 avic_logical_table_pointer : 40;
u64 reserved_1 : 12;
} __attribute__((packed));
static_assert(sizeof(struct AVICLogicalTablePointer) == 8);
struct AVICPhysicalTablePointer {
u64 avic_physical_max_index : 12;
u64 avic_physical_table_pointer : 40;
u64 reserved_0 : 12;
} __attribute__((packed));
static_assert(sizeof(struct AVICPhysicalTablePointer) == 8);
struct VMSAPointer {
u64 reserved_0 : 12;
u64 vmsa_pointer : 40;
u64 reserved_1 : 12;
} __attribute__((packed));
static_assert(sizeof(struct VMSAPointer) == 8);
struct AllowedSEVFeatures {
u64 allowed_sev_features_mask : 62;
u64 reserved_0 : 1;
u64 allowed_sev_features_en : 1;
} __attribute__((packed));
static_assert(sizeof(struct AllowedSEVFeatures) == 8);
struct RequestedIRR {
u64 irr[4];
} __attribute__((packed));
static_assert(sizeof(struct RequestedIRR) == 0x20);
struct VMCBControlArea {
struct InterceptVector0 intercept_vector0;
struct InterceptVector1 intercept_vector1;
struct InterceptVector2 intercept_vector2;
struct InterceptVector3 intercept_vector3;
struct InterceptVector4 intercept_vector4;
struct InterceptVector5 intercept_vector5;
u8 reserved_0[0x24];
u16 pause_filter_threshold;
u16 pause_filter_count;
u64 iopm_base_pa;
u64 msrpm_base_pa;
u64 tsc_offset;
u32 guest_asid;
struct TLBControl tlb_control;
struct VirtualInterruptControl v_intr;
struct InterruptShadow interrupt_shadow;
u64 exitcode;
u64 exitinfo1;
u64 exitinfo2;
u64 exitintinfo;
struct VirtualizationControl0 virtualization_control0;
struct AVICAPICBar avic_apic_bar;
u64 guest_ghcb_pa;
u64 eventinj;
u64 h_cr3;
struct VirtualizationControl1 virtualization_control1;
u32 vmcb_clean_bits;
u8 reserved_1[0x4];
u64 nrip;
struct GuestInstruction guest_instruction;
struct AVICAPICBackingPage avicapic_backing_page;
u8 reserved_2[0x8];
struct AVICLogicalTablePointer avic_logical_table;
struct AVICPhysicalTablePointer avic_physical_table;
u8 reserved_3[0x08];
struct VMSAPointer vmsa_pointer;
u64 vmgexit_rax;
u8 vmgexit_cpl;
u8 reserved_4[0x07];
u16 bus_lock_threshold_counter;
u8 reserved_5[0x12];
u32 update_irr;
struct AllowedSEVFeatures allowed_sev_features;
u64 guest_sev_features;
u8 reserved_6[0x8];
struct RequestedIRR requested_irr;
u8 reserved_7[0x270];
u8 host_reserved[0x20];
} __attribute__((packed));
static_assert(offsetof(struct VMCBControlArea, intercept_vector0) == 0x000);
static_assert(offsetof(struct VMCBControlArea, intercept_vector1) == 0x004);
static_assert(offsetof(struct VMCBControlArea, intercept_vector2) == 0x008);
static_assert(offsetof(struct VMCBControlArea, intercept_vector3) == 0x00C);
static_assert(offsetof(struct VMCBControlArea, intercept_vector4) == 0x010);
static_assert(offsetof(struct VMCBControlArea, intercept_vector5) == 0x014);
static_assert(offsetof(struct VMCBControlArea, pause_filter_threshold) == 0x03C);
static_assert(offsetof(struct VMCBControlArea, pause_filter_count) == 0x03E);
static_assert(offsetof(struct VMCBControlArea, intercept_vector5) == 0x014);
static_assert(offsetof(struct VMCBControlArea, iopm_base_pa) == 0x040);
static_assert(offsetof(struct VMCBControlArea, msrpm_base_pa) == 0x048);
static_assert(offsetof(struct VMCBControlArea, tsc_offset) == 0x050);
static_assert(offsetof(struct VMCBControlArea, guest_asid) == 0x058);
static_assert(offsetof(struct VMCBControlArea, tlb_control) == 0x05C);
static_assert(offsetof(struct VMCBControlArea, v_intr) == 0x060);
static_assert(offsetof(struct VMCBControlArea, interrupt_shadow) == 0x068);
static_assert(offsetof(struct VMCBControlArea, exitcode) == 0x070);
static_assert(offsetof(struct VMCBControlArea, exitinfo1) == 0x078);
static_assert(offsetof(struct VMCBControlArea, exitinfo2) == 0x080);
static_assert(offsetof(struct VMCBControlArea, exitintinfo) == 0x088);
static_assert(offsetof(struct VMCBControlArea, virtualization_control0) == 0x090);
static_assert(offsetof(struct VMCBControlArea, avic_apic_bar) == 0x098);
static_assert(offsetof(struct VMCBControlArea, guest_ghcb_pa) == 0x0A0);
static_assert(offsetof(struct VMCBControlArea, eventinj) == 0x0A8);
static_assert(offsetof(struct VMCBControlArea, h_cr3) == 0x0B0);
static_assert(offsetof(struct VMCBControlArea, virtualization_control1) == 0x0B8);
static_assert(offsetof(struct VMCBControlArea, vmcb_clean_bits) == 0x0C0);
static_assert(offsetof(struct VMCBControlArea, nrip) == 0x0C8);
static_assert(offsetof(struct VMCBControlArea, guest_instruction) == 0x0D0);
static_assert(offsetof(struct VMCBControlArea, avicapic_backing_page) == 0x0E0);
static_assert(offsetof(struct VMCBControlArea, avic_logical_table) == 0x0F0);
static_assert(offsetof(struct VMCBControlArea, avic_physical_table) == 0x0F8);
static_assert(offsetof(struct VMCBControlArea, vmsa_pointer) == 0x108);
static_assert(offsetof(struct VMCBControlArea, vmgexit_rax) == 0x110);
static_assert(offsetof(struct VMCBControlArea, vmgexit_cpl) == 0x118);
static_assert(offsetof(struct VMCBControlArea, bus_lock_threshold_counter) == 0x120);
static_assert(offsetof(struct VMCBControlArea, update_irr) == 0x134);
static_assert(offsetof(struct VMCBControlArea, allowed_sev_features) == 0x138);
static_assert(offsetof(struct VMCBControlArea, guest_sev_features) == 0x140);
static_assert(offsetof(struct VMCBControlArea, requested_irr) == 0x150);
static_assert(offsetof(struct VMCBControlArea, host_reserved) == 0x3E0);
static_assert(sizeof(struct VMCBControlArea) == 0x400);
struct SegmentDescriptor {
u16 selector;
u16 attrib;
u32 limit;
u64 base;
} __attribute__((packed));
static_assert(offsetof(struct SegmentDescriptor, selector) == 0);
static_assert(offsetof(struct SegmentDescriptor, attrib) == 2);
static_assert(offsetof(struct SegmentDescriptor, limit) == 4);
static_assert(offsetof(struct SegmentDescriptor, base) == 8);
static_assert(sizeof(struct SegmentDescriptor) == 0x10);
struct LBRSTACK {
u8 lbr_stack_from_to[256];
} __attribute__((packed));
static_assert(sizeof(struct LBRSTACK) == 0x100);
struct VMCBStateSaveArea {
struct SegmentDescriptor es;
struct SegmentDescriptor cs;
struct SegmentDescriptor ss;
struct SegmentDescriptor ds;
struct SegmentDescriptor fs;
struct SegmentDescriptor gs;
struct SegmentDescriptor gdtr;
struct SegmentDescriptor ldtr;
struct SegmentDescriptor idtr;
struct SegmentDescriptor tr;
u8 reserved_0[0x2B];
u8 cpl;
u8 reserved_1[0x4];
u64 efer;
u8 reserved_2[0x8];
u64 perf_ctl0;
u64 perf_ctr0;
u64 perf_ctl1;
u64 perf_ctr1;
u64 perf_ctl2;
u64 perf_ctr2;
u64 perf_ctl3;
u64 perf_ctr3;
u64 perf_ctl4;
u64 perf_ctr4;
u64 perf_ctl5;
u64 perf_ctr5;
u8 reserved_3[0x8];
u64 cr4;
u64 cr3;
u64 cr0;
u64 dr7;
u64 dr6;
u64 rflags;
u64 rip;
u8 reserved_4[0x40];
u64 instr_retired_ctr;
u64 perf_ctr_global_sts;
u64 perf_ctr_global_ctl;
u64 rsp;
u64 s_cet;
u64 ssp;
u64 isst_addr;
u64 rax;
u64 star;
u64 lstar;
u64 cstar;
u64 sfmask;
u64 kernel_gs_base;
u64 sysenter_cs;
u64 sysenter_esp;
u64 sysenter_eip;
u64 cr2;
u8 reserved_5[0x20];
u64 g_pat;
u64 dbgctl;
u64 br_from;
u64 br_to;
u64 lastexcpfrom;
u64 lastexcpto;
u64 dbgextnctl;
u8 reserved_6[0x40];
u64 spec_ctrl;
u8 reserved_7[0x388];
struct LBRSTACK lbr_stack_from_to;
u64 lbr_select;
u64 ibs_fetch_ctl;
u64 ibs_fetch_linaddr;
u64 ibs_op_ctl;
u64 ibs_op_rip;
u64 ibs_op_data;
u64 ibs_op_data2;
u64 ibs_op_data3;
u64 ibs_dc_linaddr;
u64 bp_ibstgt_rip;
u64 ic_ibs_extd_ctl;
u8 reserved_9[0x38];
} __attribute__((packed));
static_assert(offsetof(struct VMCBStateSaveArea, es) == 0x000);
static_assert(offsetof(struct VMCBStateSaveArea, cs) == 0x010);
static_assert(offsetof(struct VMCBStateSaveArea, ss) == 0x020);
static_assert(offsetof(struct VMCBStateSaveArea, ds) == 0x030);
static_assert(offsetof(struct VMCBStateSaveArea, fs) == 0x040);
static_assert(offsetof(struct VMCBStateSaveArea, gs) == 0x050);
static_assert(offsetof(struct VMCBStateSaveArea, gdtr) == 0x060);
static_assert(offsetof(struct VMCBStateSaveArea, ldtr) == 0x070);
static_assert(offsetof(struct VMCBStateSaveArea, idtr) == 0x080);
static_assert(offsetof(struct VMCBStateSaveArea, tr) == 0x090);
static_assert(offsetof(struct VMCBStateSaveArea, cpl) == 0x0CB);
static_assert(offsetof(struct VMCBStateSaveArea, efer) == 0x0D0);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl0) == 0x0E0);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr0) == 0x0E8);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl1) == 0x0F0);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr1) == 0x0F8);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl2) == 0x100);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr2) == 0x108);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl3) == 0x110);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr3) == 0x118);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl4) == 0x120);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr4) == 0x128);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl5) == 0x130);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr5) == 0x138);
static_assert(offsetof(struct VMCBStateSaveArea, cr4) == 0x148);
static_assert(offsetof(struct VMCBStateSaveArea, cr3) == 0x150);
static_assert(offsetof(struct VMCBStateSaveArea, cr0) == 0x158);
static_assert(offsetof(struct VMCBStateSaveArea, dr7) == 0x160);
static_assert(offsetof(struct VMCBStateSaveArea, dr6) == 0x168);
static_assert(offsetof(struct VMCBStateSaveArea, rflags) == 0x170);
static_assert(offsetof(struct VMCBStateSaveArea, rip) == 0x178);
static_assert(offsetof(struct VMCBStateSaveArea, instr_retired_ctr) == 0x1C0);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr_global_sts) == 0x1C8);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr_global_ctl) == 0x1D0);
static_assert(offsetof(struct VMCBStateSaveArea, rsp) == 0x1D8);
static_assert(offsetof(struct VMCBStateSaveArea, rax) == 0x1F8);
static_assert(offsetof(struct VMCBStateSaveArea, star) == 0x200);
static_assert(offsetof(struct VMCBStateSaveArea, lstar) == 0x208);
static_assert(offsetof(struct VMCBStateSaveArea, cstar) == 0x210);
static_assert(offsetof(struct VMCBStateSaveArea, sfmask) == 0x218);
static_assert(offsetof(struct VMCBStateSaveArea, kernel_gs_base) == 0x220);
static_assert(offsetof(struct VMCBStateSaveArea, sysenter_cs) == 0x228);
static_assert(offsetof(struct VMCBStateSaveArea, sysenter_esp) == 0x230);
static_assert(offsetof(struct VMCBStateSaveArea, sysenter_eip) == 0x238);
static_assert(offsetof(struct VMCBStateSaveArea, cr2) == 0x240);
static_assert(offsetof(struct VMCBStateSaveArea, g_pat) == 0x268);
static_assert(offsetof(struct VMCBStateSaveArea, dbgctl) == 0x270);
static_assert(offsetof(struct VMCBStateSaveArea, br_from) == 0x278);
static_assert(offsetof(struct VMCBStateSaveArea, br_to) == 0x280);
static_assert(offsetof(struct VMCBStateSaveArea, lastexcpfrom) == 0x288);
static_assert(offsetof(struct VMCBStateSaveArea, lastexcpto) == 0x290);
static_assert(offsetof(struct VMCBStateSaveArea, dbgextnctl) == 0x298);
static_assert(offsetof(struct VMCBStateSaveArea, spec_ctrl) == 0x2E0);
static_assert(offsetof(struct VMCBStateSaveArea, lbr_stack_from_to) == 0x670);
static_assert(offsetof(struct VMCBStateSaveArea, lbr_select) == 0x770);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_fetch_ctl) == 0x778);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_fetch_linaddr) == 0x780);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_op_ctl) == 0x788);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_op_rip) == 0x790);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_op_data) == 0x798);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_op_data2) == 0x7A0);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_op_data3) == 0x7A8);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_dc_linaddr) == 0x7B0);
static_assert(offsetof(struct VMCBStateSaveArea, bp_ibstgt_rip) == 0x7B8);
static_assert(offsetof(struct VMCBStateSaveArea, ic_ibs_extd_ctl) == 0x7C0);
static_assert(sizeof(struct VMCBStateSaveArea) == 0x800);
struct VMCB {
struct VMCBControlArea control_area;
struct VMCBStateSaveArea state_area;
u8 reserved_0 [0x400];
} __attribute__((packed));
static_assert(offsetof(struct VMCB, control_area) == 0x000);
static_assert(offsetof(struct VMCB, state_area) == 0x400);
static_assert(sizeof(struct VMCB) == 0x1000);
#endif
@@ -0,0 +1,40 @@
#include <linux/linkage.h>
#include <asm/asm.h>
#include <host_state.h>
#define save_to_offset(val, REG) movq val , GUEST_ ## REG ## _OFFSET (% _ASM_ARG1 )
SYM_FUNC_START(capture_context)
#ifdef ENDBR
ENDBR
#endif
save_to_offset($0, RAX) /* save 0 for guest rax */
save_to_offset(%rbx, RBX)
save_to_offset(%rcx, RCX)
save_to_offset(%rdx, RDX)
save_to_offset(%rsi, RSI)
save_to_offset(%rdi, RDI)
save_to_offset(%rbp, RBP)
leaq 8(%rsp),%rax /* rsp after ret */
save_to_offset(%rax, RSP)
save_to_offset(%r8, R8)
save_to_offset(%r9, R9)
save_to_offset(%r10, R10)
save_to_offset(%r11, R11)
save_to_offset(%r12, R12)
save_to_offset(%r13, R13)
save_to_offset(%r14, R14)
save_to_offset(%r15, R15)
movq (%rsp), %rax /* return addr*/
save_to_offset(%rax, RIP)
pushfq
popq %rax /* rflags */
save_to_offset(%rax, RFLAGS)
mov $1, %rax;
RET
SYM_FUNC_END(capture_context)
@@ -0,0 +1,663 @@
#include <linux/init.h> /* Needed for the macros */
#include <linux/module.h> /* Needed by all modules */
#include <linux/printk.h> /* Needed for pr_info() */
#include <linux/string.h> /* for memset() and memcpy() */
#include <linux/bits.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
#include <linux/cpuhplock.h>
#else
#include <linux/cpu.h>
#endif
#include <linux/suspend.h>
#include <linux/types.h>
#include <asm/desc.h>
#include <asm/msr.h>
#include <linux/mm.h>
#include <host_state.h>
#include <vmcb_layout.h>
#define MSR_PM_BASE1 0x00000000
#define MSR_PM_BASE2 0xc0000000
#define MSR_PM_BASE3 0xc0010000
#define SVM_MSR_VM_HSAVE_PA 0xc0010117
#define UNLOAD_HV_MAGIC 0x40067420
#define SVM_SUPPORT_LEAF 0x80000001
#define SVM_SUPPORT_BIT 2
#define SVM_MSR_VM_CR 0xc0010114
#define VM_CR_SVMDIS_BIT 4
#define VMEXIT_CPUID 0x72
#define VMEXIT_MSRPROT 0x7C
/* VECTOR 8 bits = 0x0D (GP) */
/* TYPE 3 bits = 0x3 (Fault) */
/* EV 1 bit = 0x1 (Error Code Valid) */
/* reserved 19 bits */
/* V 1 bit = 0x1 (Valid) */
/* ERRORCODE 32 bits = 0x0 */
#define GP0_EVENTINJ (0x0D | (0x3 << 8) | (0x1 << 11) | (0x1 << 31))
#ifndef page_to_phys
#define page_to_phys(page) PFN_PHYS(page_to_pfn(page))
#endif
/* The hypervisor doesn't handle nested virtualization, so attempting to
* virtualize a kernel currently using virtualization features will probably
* result in an immediate crash. If you know better you can tell the module
* to attempt the virtualization anyway */
static int ignore_svm_enabled = 0;
module_param(ignore_svm_enabled, int, 0);
static struct {
struct page* msrpm_page;
u64 msrpm_pa;
bool prev_cpuid_fault_cap;
bool prev_svm_enabled;
} shared_data;
inline static void vmsave(u64 vmcb_pa) {
asm volatile ("vmsave" : : "a" (vmcb_pa) : "memory");
}
inline static void vmload(u64 vmcb_pa) {
asm volatile ("vmload" : : "a" (vmcb_pa) : "memory");
}
inline static void do_cpuid(u32* eax, u32* ebx, u32* ecx, u32* edx) {
asm volatile ("cpuid"
: "+a"(*eax), "=b"(*ebx), "+c"(*ecx), "=d"(*edx)
:
: "memory");
}
inline static void do_cpuid_leaf(u32 leaf, u32* eax, u32* ebx,
u32* ecx, u32* edx) {
asm volatile ("cpuid"
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
: "a"(leaf)
: "memory");
}
inline static void do_wrmsr(u32 msr, u64 val) {
asm volatile ("wrmsr" : : "c"(msr), "a"((u32)val), "d" ((u32)(val >> 32)) : "memory");
}
inline static u64 do_rdmsr(u32 msr) {
u32 low, high;
asm volatile ("rdmsr" : "=a"(low), "=d" (high) : "c"(msr));
return low | ((u64)high) << 32;
}
/* Raw writes and reads for cr registers. The kernel has it's own safer
* versions, however, all the writes we do should use values provided by the
* kernel, so these consistent raw versions are cleaner to use. */
#define define_cr_ops(cr) \
inline static u64 do_read_ ## cr (void) {\
u64 val; \
asm volatile ("movq %%" #cr ", %0" : "=r" (val) : :); \
return val; \
} \
inline static void do_write_ ## cr (u64 val) {\
asm volatile ("movq %0, %%" #cr : :"r" (val) :); \
}
define_cr_ops(cr0);
define_cr_ops(cr2);
define_cr_ops(cr3);
define_cr_ops(cr4);
/* Checks the relevant CPUID bits and MSR registers to determine SVM support
* since the hypervisor doesn't use features like NPT or decode assists,
* very few checks are needed */
static bool check_svm_support(void) {
u32 eax, ebx, ecx, edx;
/* check for "AuthenticAMD" on leaf 0 */
do_cpuid_leaf(0, &eax, &ebx, &ecx, &edx);
if (!(ebx == 'htuA' && edx == 'itne' && ecx == 'DMAc'))
return false;
/* check for SVM support */
do_cpuid_leaf(SVM_SUPPORT_LEAF, &eax, &ebx, &ecx, &edx);
if (!(ecx & (1U << SVM_SUPPORT_BIT)))
return false;
/* check if SVM is disabled by the BIOS */
if (do_rdmsr(SVM_MSR_VM_CR) & BIT_ULL(VM_CR_SVMDIS_BIT))
return false;
return true;
}
/* capture_context saves all GPR's, as well as RIP and RFLAGS as they are at
* it's return, except for RAX, which is saved as 0, but returned as 1 */
noinline_for_stack u64 capture_context(struct InitialContext* registers);
/* run_vm repeatedly runs the VMRUN instruction, effectively continuing to
* run the guest (the OS), when an intercepted event happens, it calls the
* handle_vm_exit function with the current state of the guest. If the call
* returns a non-zero value it begins the process of devirtualizing the
* processor */
__attribute__((noreturn)) void run_vm(struct ProcessorStatus* status);
/* called when the guest would execute a CPUID instruction*/
static u64 handle_cpuid(struct ProcessorStatus* status) {
u32 eax = status->guest_registers.rax;
u32 ebx;
u32 ecx = status->guest_registers.rcx;
u32 edx;
if(status->vmcb.state_area.cpl == 0) {
switch (eax) {
case UNLOAD_HV_MAGIC:
status->vmcb.state_area.rip += 2;
return 1; /* tell the hypervisor to devirtualize the processor */
}
} else {
if(status->cpuid_fault) { /* if CPUID should fault */
status->vmcb.control_area.eventinj = GP0_EVENTINJ; /* inject GP */
return 0;
}
}
/* otherwise, run CPUID and increase RIP */
do_cpuid(&eax, &ebx, &ecx, &edx);
status->guest_registers.rax = eax;
status->guest_registers.rbx = ebx;
status->guest_registers.rcx = ecx;
status->guest_registers.rdx = edx;
status->vmcb.state_area.rip += 2;
return 0;
}
/* called when the guest would execute a read or write to a protected MSR */
static u64 handle_msr(struct ProcessorStatus* status) {
/* the msr the guest is trying to access */
u32 msr = status->guest_registers.rcx;
bool write_access = (status->vmcb.control_area.exitinfo1 != 0);
/* to emulate cpuid_fault, writes and reads to the corresponding MSR are
* intercepted */
if (write_access) {
u64 value = (status->guest_registers.rdx << 32) |
(u32)status->guest_registers.rax;
#ifdef MSR_K7_HWCR_CPUID_USER_DIS_BIT
/* If the MSR to enable / disable cpuid_fault is being written to */
if(msr == MSR_K7_HWCR) {
/* update whether the hypervisor should inject a fault on CPUID */
status->cpuid_fault = value & BIT_ULL(MSR_K7_HWCR_CPUID_USER_DIS_BIT);
/* write the value to the register, with the cpuid_fault bit clear */
do_wrmsr(msr, value & ~BIT_ULL(MSR_K7_HWCR_CPUID_USER_DIS_BIT));
}
#else
/* Older kernels will attempt to enable cpuid_fault using the intel
* MSR */
if(msr == MSR_MISC_FEATURES_ENABLES) {
status->cpuid_fault = value & MSR_MISC_FEATURES_ENABLES_CPUID_FAULT;
}
#endif
else {
/* If we don't care about the MSR, do the write directly */
do_wrmsr(msr, value);
}
} else {
u64 value;
#ifdef MSR_K7_HWCR_CPUID_USER_DIS_BIT
/* If the MSR to enable / disable cpuid_fault is being read */
if(msr == MSR_K7_HWCR) {
value = do_rdmsr(msr);
/* reflect the current cpuid_fault status to the guest */
if(status->cpuid_fault)
value |= BIT_ULL(MSR_K7_HWCR_CPUID_USER_DIS_BIT);
}
#else
/* Older kernels will attempt to enable cpuid_faulting using the intel
* MSR */
if(msr == MSR_MISC_FEATURES_ENABLES) {
value = 0;
if(status->cpuid_fault)
value |= MSR_MISC_FEATURES_ENABLES_CPUID_FAULT;
}
#endif
else {
/* If we don't care about the MSR, do the read directly */
value = do_rdmsr(msr);
}
status->guest_registers.rax = (u32)value;
status->guest_registers.rdx = (u32)(value >> 32);
}
status->vmcb.state_area.rip += 2;
return 0;
}
/* consistent functions to write segment selectors */
#define define_sel_ops(sel) \
inline static u64 read_ ## sel ## _sel(void) {\
u16 sel; \
asm volatile ("movw %%" #sel ", %0" : "=r" (sel) : :); \
return sel; \
} \
inline static void write_ ## sel ## _sel(u16 sel) {\
asm volatile ("movw %0, %%" #sel : :"r" (sel) :); \
}
define_sel_ops(ds);
define_sel_ops(es);
define_sel_ops(ss);
inline static u64 read_cs_sel(void) {
u16 cs;
asm volatile("movw %%cs, %0" : "=r"(cs) : :);
return cs;
}
/* a direct move into cs is not a valid instruction, so the value is written
* via a far jump */
inline static void write_cs_sel(u16 cs) {
asm volatile("movzwq %0,%%rax\n\t"
"pushq %%rax\n\t"
"leaq curr_ip(%%rip),%%rax\n\t"
"pushq %%rax\n\t"
"lretq\n\t"
"curr_ip:": : "r"(cs) : "rax");
};
/* To end the virtualization the host needs to put itself the guest was just
* in, this effectively does the opposite to setup_guest_state */
static void restore_host_state(struct ProcessorStatus* status) {
struct VMCB* guest_vmcb = &status->vmcb;
struct desc_ptr gdt_ptr;
struct desc_ptr idt_ptr;
/* Load other state that might have changed */
vmload(status->vmcb_pa);
/* Restore the hsave address before the processor was virtualized */
do_wrmsr(SVM_MSR_VM_HSAVE_PA, status->prev_hsave_pa);
do_wrmsr(MSR_EFER, guest_vmcb->state_area.efer);
idt_ptr.address = guest_vmcb->state_area.idtr.base;
idt_ptr.size = guest_vmcb->state_area.idtr.limit;
gdt_ptr.address = guest_vmcb->state_area.gdtr.base;
gdt_ptr.size = guest_vmcb->state_area.gdtr.limit;
/* Restore gdt and idt. Done for correctness */
native_load_gdt(&gdt_ptr);
native_load_idt(&idt_ptr);
/* Load segment selectors from the guest. Also for correctness */
write_es_sel(guest_vmcb->state_area.es.selector);
write_ss_sel(guest_vmcb->state_area.ss.selector);
write_ds_sel(guest_vmcb->state_area.ds.selector);
write_cs_sel(guest_vmcb->state_area.cs.selector);
/* run_vm will resume running the OS by setting this rip and rsp */
status->guest_registers.rax = guest_vmcb->state_area.rip;
status->guest_registers.rsp = guest_vmcb->state_area.rsp;
/* will be used by the OS to cleanup the per processor data */
status->guest_registers.rbx = (u64)status;
}
/* Everytime an intercepted instruction is excecuted (currently only
* wrmsr/rdmsr to the cpuid_fault register, cpuid and runvm (required by
* the processor)), the guest's state is saved and the hypervisor calls
* this function */
u64 handle_vm_exit(struct ProcessorStatus* status) {
status->guest_registers.rsp = status->vmcb.state_area.rsp;
status->guest_registers.rax = status->vmcb.state_area.rax;
u64 exitcode = status->vmcb.control_area.exitcode;
u64 result = 0;
switch(exitcode) {
case VMEXIT_CPUID:
result = handle_cpuid(status);
break;
case VMEXIT_MSRPROT:
result = handle_msr(status);
break;
}
if(result) { /* begin the devirtualization process */
restore_host_state(status);
}
status->vmcb.state_area.rsp = status->guest_registers.rsp;
status->vmcb.state_area.rax = status->guest_registers.rax;
return result; /* tell run_vm if it should keep running the guest */
}
/* Used to initialize the segment registers (cs, ds, es, ss) in the VMCB */
static void set_descriptor_from_gdt(struct desc_ptr* gdtr, u16 selector,
struct SegmentDescriptor* desc) {
desc->selector = selector;
u64 idx = selector & ~0b111; /* Mask out the TI and RPL bits */
if(!idx) {
desc->attrib = 0;
desc->limit = 0;
desc->base = 0;
return;
}
/* Read the 8 bytes of the descriptor from the gdt, there's also 16-byte
* descriptors (e.g. TSS, LDT), but the function is not used for them, so
* they're not handled */
u64 raw = *(u64*) (gdtr->address + idx);
/* Concatenate the attribute bits */
desc->attrib = ((raw & (0xFFL << 40)) >> 40) |
((raw & (0xFL << 52)) >> 44);
/* Concatenate the limit bits */
desc->limit = (raw & 0xFFFF) | ((raw & (0xFL << 48)) >> 32);
/* If the granularity is 4kb, store the effective limit */
if(raw & (0x1L << 55)) desc->limit = (desc->limit << 12) | 0xFFFL;
/* Concatenate the base address bits */
desc->base = ((raw & (0xFFFFL << 16)) >> 16) |
((raw & (0xFFL << 32)) >> 16) |
((raw & (0xFFL << 56)) >> 32);
}
/* This setups all of the state needed to run a vm with the current status
* of the processor */
static void setup_guest_state(struct page* host_status_page,
struct InitialContext* initial_context) {
u64 host_status_pa = page_to_phys(host_status_page);
u64 guest_vmcb_pa = host_status_pa + HOST_VMCB_OFFSET;
u64 host_vmcb_pa = host_status_pa + HOST_HOST_VMCB_OFFSET;
u64 host_save_pa = host_status_pa + HOST_HOST_SAVE_OFFSET;
struct ProcessorStatus* host_status = page_address(host_status_page);
host_status->vmcb_pa = guest_vmcb_pa;
host_status->host_vmcb_pa = host_vmcb_pa;
/* used to cleanup after the processor is devirtualized */
host_status->page_ptr = host_status_page;
host_status->guest_registers = initial_context->regs;
struct VMCB* guest_vmcb = &host_status->vmcb;
struct desc_ptr gdt_ptr;
struct desc_ptr idt_ptr;
native_store_gdt(&gdt_ptr);
store_idt(&idt_ptr);
/* intercept MSRs defined by MSR prot */
guest_vmcb->control_area.intercept_vector3.msr_prot = 1;
guest_vmcb->control_area.msrpm_base_pa = shared_data.msrpm_pa;
/* intercept the CPUID instruction */
guest_vmcb->control_area.intercept_vector3.cpuid_inst = 1;
/* intercept the VMRUN instruction (required to run VMRUN) */
guest_vmcb->control_area.intercept_vector4.vmrun_inst = 1;
/* the guest uses it's own address space, 1 is the safest value to use */
guest_vmcb->control_area.guest_asid = 1;
u32 eax, ebx, ecx, edx;
do_cpuid_leaf(0x80000008, &eax, &ebx, &ecx, &edx);
if(ebx & (1 << 3)) {
do_cpuid_leaf(0x8000000a, &eax, &ebx, &ecx, &edx);
if(edx & (1 << 24)) {
guest_vmcb->control_area.virtualization_control0.invlpg_tlbsync_enable = 1;
}
}
guest_vmcb->state_area.gdtr.base = gdt_ptr.address;
guest_vmcb->state_area.gdtr.limit = gdt_ptr.size;
guest_vmcb->state_area.idtr.base = idt_ptr.address;
guest_vmcb->state_area.idtr.limit = idt_ptr.size;
u16 cs_selector = read_cs_sel();
set_descriptor_from_gdt(&gdt_ptr, cs_selector,
&guest_vmcb->state_area.cs);
u16 ds_selector = read_ds_sel();
set_descriptor_from_gdt(&gdt_ptr, ds_selector,
&guest_vmcb->state_area.ds);
u16 es_selector = read_es_sel();
set_descriptor_from_gdt(&gdt_ptr, es_selector,
&guest_vmcb->state_area.es);
u16 ss_selector = read_ss_sel();
set_descriptor_from_gdt(&gdt_ptr, ss_selector,
&guest_vmcb->state_area.ss);
guest_vmcb->state_area.efer = do_rdmsr(MSR_EFER);
guest_vmcb->state_area.cr0 = do_read_cr0();
guest_vmcb->state_area.cr2 = do_read_cr2();
guest_vmcb->state_area.cr3 = do_read_cr3();
guest_vmcb->state_area.cr4 = do_read_cr4();
guest_vmcb->state_area.rflags = initial_context->rflags;
guest_vmcb->state_area.rsp = initial_context->regs.rsp;
guest_vmcb->state_area.rip = initial_context->rip;
guest_vmcb->state_area.rax = initial_context->regs.rax;
vmsave(guest_vmcb_pa);
/* Save the hsave address before the hypervisor is loaded */
/* (If the kvm_amd kernel module is loaded, this value will already be
* setup and changing it and trying to run a kvm vm will crash the OS) */
host_status->prev_hsave_pa = do_rdmsr(SVM_MSR_VM_HSAVE_PA);
do_wrmsr(SVM_MSR_VM_HSAVE_PA, host_save_pa);
/* save another copy of the host state, used to provide a more consistent
* host state at vmexit */
vmsave(host_vmcb_pa);
}
static int virtualize_processor(void* processor_number) {
struct page* host_status_page;
int order = get_order(sizeof(struct ProcessorStatus));
host_status_page = alloc_pages(GFP_KERNEL, order);
if (!host_status_page)
goto host_status_page_failed;
unsigned long flags;
get_cpu();
local_irq_save(flags);
memset(page_address(host_status_page), 0, PAGE_SIZE * (1 << order));
struct InitialContext context;
/* Capture the context to be used to initialize the guest, since it
* returns 1 but the stored state has 0 as it's return, when the guest
* starts execution at the stored state it will not enter into the if.
* */
if(capture_context(&context)) {
do_wrmsr(MSR_EFER, do_rdmsr(MSR_EFER) | EFER_SVME); /* Enable SVME */
setup_guest_state(host_status_page, &context);
struct ProcessorStatus* host_status = page_address(host_status_page);
/* Kinda ugly, effectively sets a CR3 for the host that will
* always be safe to access the hypervisor's code from
* */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0)
leave_mm();
#else
leave_mm(smp_processor_id());
#endif
run_vm(host_status);
BUG();
}
/* Only the guest should run this code */
put_cpu();
pr_info("Processor %d virtualized\n", (u32)(u64)processor_number);
return 0;
host_status_page_failed:
local_irq_restore(flags);
put_cpu();
return -ENOMEM;
}
static int check_svm_on_cpu(void*) {
return do_rdmsr(MSR_EFER) & EFER_SVME;
}
static bool check_svm_status(void) {
u32 svm_enabled = 0;
cpus_read_lock();
u32 cpu;
for_each_online_cpu(cpu) {
svm_enabled |= smp_call_on_cpu(cpu, check_svm_on_cpu, NULL, true);
}
cpus_read_unlock();
return svm_enabled;
}
static int devirtualize_processor(void* processor_number) {
int order = get_order(sizeof(struct ProcessorStatus));
unsigned long flags;
get_cpu();
local_irq_save(flags);
u64 rax = UNLOAD_HV_MAGIC;
u64 rbx, rcx, rdx;
asm volatile("cpuid":"+a"(rax),"=b"(rbx),"=c"(rcx),"=d"(rdx)::"memory");
struct ProcessorStatus* status = NULL;
if(rax != UNLOAD_HV_MAGIC) {
status = (struct ProcessorStatus*)rbx;
struct VMCB* guest_vmcb = &status->vmcb;
do_write_cr3(guest_vmcb->state_area.cr3);
do_write_cr4(guest_vmcb->state_area.cr4);
do_write_cr2(guest_vmcb->state_area.cr2);
do_write_cr0(guest_vmcb->state_area.cr0);
}
if(!shared_data.prev_svm_enabled)
do_wrmsr(MSR_EFER, do_rdmsr(MSR_EFER) & ~EFER_SVME);
local_irq_restore(flags);
put_cpu();
if(status)__free_pages(status->page_ptr, order);
pr_info("Processor %d devirtualized\n", (u32)(u64)processor_number);
return 0;
}
/* called when a Power Management (e.g. suspend) event is about to happen */
static int handle_pm_notification(struct notifier_block* nb, unsigned long action, void *data) {
if(action == PM_HIBERNATION_PREPARE || action == PM_SUSPEND_PREPARE) {
/* on suspend or hibernation svm is disabled, so
* all processors need to be devirtualized */
cpus_read_lock();
u32 cpu;
for_each_online_cpu(cpu) {
smp_call_on_cpu(cpu, devirtualize_processor, (void*)(u64)cpu, true);
}
cpus_read_unlock();
/* return the cpuid_fault bit capability to it's previous value */
if(!shared_data.prev_cpuid_fault_cap)
clear_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
}
if(action == PM_POST_HIBERNATION || action == PM_POST_SUSPEND) {
/* revirtualize all processors after suspend or hibernation */
cpus_read_lock();
u32 cpu;
for_each_online_cpu(cpu) {
smp_call_on_cpu(cpu, virtualize_processor, (void*)(u64)cpu, true);
}
cpus_read_unlock();
shared_data.prev_cpuid_fault_cap = test_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
set_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
}
return 0;
}
static struct notifier_block pm_notifier = {
.notifier_call = handle_pm_notification
};
static int __init init_emulation(void) {
if (!check_svm_support()) {
pr_info("Processor not supported\n");
return -ENODEV;
}
shared_data.prev_svm_enabled = check_svm_status();
if(shared_data.prev_svm_enabled && !ignore_svm_enabled) {
pr_info("SVM already enabled\n");
return -ENODEV;
}
shared_data.msrpm_page = alloc_pages(GFP_KERNEL, 1);
if (!shared_data.msrpm_page)
goto msrpm_failed;
shared_data.msrpm_pa = page_to_phys(shared_data.msrpm_page);
memset(page_address(shared_data.msrpm_page), 0, 0x2000);
unsigned char* section_1_start = (unsigned char*)
page_address(shared_data.msrpm_page);
unsigned char* section_3_start = (unsigned char*)
page_address(shared_data.msrpm_page) + 0x1000;
#ifdef MSR_K7_HWCR_CPUID_USER_DIS_BIT
set_bit((MSR_K7_HWCR - MSR_PM_BASE3) * 2,
(unsigned long*) section_3_start);
set_bit((MSR_K7_HWCR - MSR_PM_BASE3) * 2 + 1,
(unsigned long*) section_3_start);
#else /* on older kernels intercept the intel register instead */
set_bit((MSR_MISC_FEATURES_ENABLES - MSR_PM_BASE1) * 2,
(unsigned long*) section_1_start);
set_bit((MSR_MISC_FEATURES_ENABLES - MSR_PM_BASE1) * 2 + 1,
(unsigned long*) section_1_start);
#endif
cpus_read_lock();
u32 cpu;
for_each_online_cpu(cpu) {
smp_call_on_cpu(cpu, virtualize_processor, (void*)(u64)cpu, true);
}
cpus_read_unlock();
shared_data.prev_cpuid_fault_cap = test_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
set_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
register_pm_notifier(&pm_notifier);
return 0;
msrpm_failed:
return -ENOMEM;
}
static void __exit deinit_emulation(void) {
unregister_pm_notifier(&pm_notifier);
if(!shared_data.prev_cpuid_fault_cap)
clear_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
cpus_read_lock();
u32 cpu;
for_each_online_cpu(cpu) {
smp_call_on_cpu(cpu, devirtualize_processor, (void*)(u64)cpu, true);
}
cpus_read_unlock();
if (shared_data.msrpm_page)
__free_pages(shared_data.msrpm_page, 1);
}
module_init(init_emulation);
module_exit(deinit_emulation);
MODULE_LICENSE("GPL");
@@ -0,0 +1,121 @@
#include <linux/linkage.h>
#include <asm/asm.h>
#include <host_state.h>
#define save_to_offset_from(val, off, from) movq val, off -(HOST_STACK_OFFSET + HOST_STACK_SIZE) (from)
#define mov_from_offset_from(off, reg, from) movq off -(HOST_STACK_OFFSET + HOST_STACK_SIZE) (from), reg
#define save_to_offset(val, off) save_to_offset_from(val, off, %rsp)
#define mov_from_offset(off, reg) mov_from_offset_from(off, reg, %rsp)
#define save_to_reg_from(val, REG, from) save_to_offset_from(val, HOST_REGISTER_OFFSET + GUEST_ ## REG ## _OFFSET, from)
#define mov_from_reg_from(REG, reg, from) mov_from_offset_from(HOST_REGISTER_OFFSET + GUEST_ ## REG ## _OFFSET, reg, from)
#define save_to_reg(val, REG) save_to_reg_from(val, REG, %rsp)
#define mov_from_reg(REG, reg) mov_from_reg_from(REG, reg, %rsp)
SYM_CODE_START(run_vm)
#ifdef ENDBR
ENDBR
#endif
leaq HOST_STACK_OFFSET + HOST_STACK_SIZE(%_ASM_ARG1), %rsp
vmloop:
mov_from_offset(HOST_HOST_VMCB_PA_OFFSET, %rax)
vmsave
mov_from_offset(HOST_VMCB_PA_OFFSET, %rax)
vmload
mov_from_reg(RBX, %rbx)
mov_from_reg(RCX, %rcx)
mov_from_reg(RDX, %rdx)
mov_from_reg(RSI, %rsi)
mov_from_reg(RDI, %rdi)
mov_from_reg(RBP, %rbp)
mov_from_reg(R8, %r8)
mov_from_reg(R9, %r9)
mov_from_reg(R10, %r10)
mov_from_reg(R11, %r11)
mov_from_reg(R12, %r12)
mov_from_reg(R13, %r13)
mov_from_reg(R14, %r14)
mov_from_reg(R15, %r15)
vmrun
vmsave
mov_from_offset(HOST_HOST_VMCB_PA_OFFSET, %rax)
vmload
save_to_reg(%rbx, RBX)
save_to_reg(%rcx, RCX)
save_to_reg(%rdx, RDX)
save_to_reg(%rsi, RSI)
save_to_reg(%rdi, RDI)
save_to_reg(%rbp, RBP)
save_to_reg(%r8, R8)
save_to_reg(%r9, R9)
save_to_reg(%r10, R10)
save_to_reg(%r11, R11)
save_to_reg(%r12, R12)
save_to_reg(%r13, R13)
save_to_reg(%r14, R14)
save_to_reg(%r15, R15)
leaq -HOST_STACK_SIZE(%rsp), %_ASM_ARG1
/* Save volatile XMM registers */
sub $0x80, %rsp
movaps %xmm0, 0x20(%rsp)
movaps %xmm1, 0x30(%rsp)
movaps %xmm2, 0x40(%rsp)
movaps %xmm3, 0x50(%rsp)
movaps %xmm4, 0x60(%rsp)
movaps %xmm5, 0x70(%rsp)
sub $0x8, %rsp
call handle_vm_exit
add $0x8, %rsp
/* Restore volatile XMM registers */
movaps 0x70(%rsp), %xmm5
movaps 0x60(%rsp), %xmm4
movaps 0x50(%rsp), %xmm3
movaps 0x40(%rsp), %xmm2
movaps 0x30(%rsp), %xmm1
movaps 0x20(%rsp), %xmm0
add $0x80, %rsp
test %rax, %rax
jz vmloop
mov_from_reg(RBX, %rbx) /* now holds rflags */
pushq %rbx
popfq
leaq (%rsp), %rax;
mov_from_reg(RBX, %rbx)
mov_from_reg(RCX, %rcx)
mov_from_reg(RDX, %rdx)
mov_from_reg(RSI, %rsi)
mov_from_reg(RDI, %rdi)
mov_from_reg(RBP, %rbp)
mov_from_reg(R8, %r8)
mov_from_reg(R9, %r9)
mov_from_reg(R10, %r10)
mov_from_reg(R11, %r11)
mov_from_reg(R12, %r12)
mov_from_reg(R13, %r13)
mov_from_reg(R14, %r14)
mov_from_reg(R15, %r15)
mov_from_reg(RSP, %rsp)
mov_from_reg_from(RAX, %rax, %rax) /* now holds rip */
stgi
pushq %rax
RET
SYM_CODE_END(run_vm)