Target System: MacBook Pro Retina 15" (Late 2013, Model 11,3)
OS: Linux (Mint/Ubuntu/Debian based)
Objective: Completely power down the dedicated NVIDIA GT 750M to save battery and reduce heat, relying solely on the Integrated Intel Iris Pro graphics.
The MacBook Pro firmware decides which hardware to expose based on what OS it thinks it is booting.
If it thinks it's Windows/Linux: It often forces the NVIDIA card ON (for compatibility) and hides the Integrated GPU or the GMUX controls.
If it thinks it's macOS: It exposes the GMUX controller and leaves the power management logic intact.
sudo apt install refindDuring installation, it will ask: "Do you want to install rEFInd to the ESP?"
Select YES.
sudo nano /boot/efi/EFI/refind/refind.confCtrl+W to search for spoof_osx_version.
Remove the # to uncomment it.
spoof_osx_version 10.9
Save and Exit.
Critical: Tell the system to use the iris chip instead of nvidia. Injects a variable into the Mac's firmware to reset GPU power preferences. If you ever reset your PRAM (Command+Option+P+R), you must run efi-inject.sh again.
Use this to check if the firmware variable is currently set.
#!/bin/bash
# VERIFY EFI GPU PREFERENCES
# Checks if the gpu-power-prefs variable exists in NVRAM.
EFI_VAR="/sys/firmware/efi/efivars/gpu-power-prefs-fa4ce28d-b62f-4c99-9cc3-0c68ec427f89"
echo "Checking EFI Firmware..."
if [ -f "$EFI_VAR" ]; then
echo "STATUS: FOUND"
echo "The GPU Power Preference variable is present."
# Optional: Read the value (Hex dump)
# Valid/Safe values are usually 01 00 00 00 or similar minimal headers.
hexdump -C "$EFI_VAR"
else
echo "STATUS: MISSING"
echo "WARNING: The variable is not set. If you reset PRAM, the system may freeze on boot."
echo "Run ./efi-inject.sh immediately."
fiUse this to inject the variable. Run this as sudo.
#!/bin/bash
# RESTORE EFI GPU PREFERENCES
# Injects the 'gpu-power-prefs' variable to prevent boot hang on MBP 11,3.
# 1. The Target Variable (UUID: fa4ce28d-b62f-4c99-9cc3-0c68ec427f89)
EFI_VAR="/sys/firmware/efi/efivars/gpu-power-prefs-fa4ce28d-b62f-4c99-9cc3-0c68ec427f89"
# 2. Check if efivarfs is mounted (Required to write)
if ! mount | grep -q "efivarfs"; then
echo "Mounting efivarfs..."
mount -t efivarfs efivarfs /sys/firmware/efi/efivars
fi
# 3. Unlock the variable (if it exists, it's often write-protected)
if [ -f "$EFI_VAR" ]; then
echo "Variable exists. Unlocking..."
chattr -i "$EFI_VAR"
fi
# 4. Inject the Safe Value
# The header (0x07 0x00 0x00 0x00) + Clean Data (0x00 0x00 0x00 0x00)
# This clears any "Force Discrete" flags set by macOS.
echo "Injecting safe GPU preferences..."
printf "\x07\x00\x00\x00\x00\x00\x00\x00" > "$EFI_VAR"
# 5. Verify write
if [ $? -eq 0 ]; then
echo "SUCCESS: Variable written."
# Lock it again to prevent accidental changes
chattr +i "$EFI_VAR"
else
echo "ERROR: Failed to write to EFI. Are you running as root?"
fiWe need to load the standard open-source driver (nouveau) so the system recognizes the card and creates the "switch" file. We also force Active State Power Management (ASPM) to allow the hardware to sleep.
-
Edit GRUB Config:
sudo nano /etc/default/grub
-
Modify the Line: Find
GRUB_CMDLINE_LINUX_DEFAULT. Change it to match this exactly:GRUB_CMDLINE_LINUX_DEFAULT="quiet splash pcie_aspm=force"(Note: Do NOT add
nouveau.modeset=0. We need the driver to load.) -
Update GRUB:
sudo update-grub
-
Reboot: Restart your computer.
The OS will boot with the card ON. We need a script to flip the hardware switch to OFF automatically after logging in.
-
Create/Edit rc.local:
sudo nano /etc/rc.local
(If the file is new, make sure it starts with
#!/bin/bash). -
Paste the Kill Logic:
#!/bin/bash # IRIS KEEPER: NVIDIA POWER CUT # Description: Turns off discrete GPU on MBP 11,3 via vgaswitcheroo # 1. Wait for desktop and drivers to fully load sleep 10 # 2. Force the switch to OFF if [ -f /sys/kernel/debug/vgaswitcheroo/switch ]; then echo OFF > /sys/kernel/debug/vgaswitcheroo/switch fi # 3. Optional: Cut the Audio too (if not already handled) if [ -d /sys/bus/pci/devices/0000:01:00.1 ]; then echo auto > /sys/bus/pci/devices/0000:01:00.1/power/control fi exit 0
-
Make Executable:
sudo chmod +x /etc/rc.local
-
Enable the Service (Systemd systems): On newer versions of Linux (like Ubuntu 20.04+ and Mint). Systemd considers rc.local "legacy," so while the service exists, it isn't configured to start automatically by default.
We need to manually add the "Install" instruction to the service file so systemctl enable works.
- Edit the Service Unit: Open the systemd service file for rc-local:
sudo nano /lib/systemd/system/rc-local.service
- Add the Install Section: Scroll to the very bottom of the file. If you don't see an [Install] section, paste this block at the end:
[Install] WantedBy=multi-user.target
Note: If the file is completely empty (unlikely), paste this standard configuration:
[Unit] Description=/etc/rc.local Compatibility ConditionFileIsExecutable=/etc/rc.local [Service] Type=forking ExecStart=/etc/rc.local start TimeoutSec=0 RemainAfterExit=yes GuessMainPID=no [Install] WantedBy=multi-user.target
- Reload and Enable: Now that we've told systemd when to run it (multi-user.target), we can enable it.
Reload systemd manager configuration:
sudo systemctl daemon-reload
Enable the service:
sudo systemctl enable rc-local(This should now output Created symlink ... without error).
Start it immediately (to test):
sudo systemctl start rc-local
Check status:
sudo systemctl status rc-local
Target: You want to see Active: active (exited).
Note: If it says failed, check that your /etc/rc.local file is executable (sudo chmod +x /etc/rc.local) and has #!/bin/bash at the top.
- Ensure rc-local is active: (some modern distros disable it by default):
sudo systemctl enable rc-local sudo systemctl start rc-local
Use these commands to confirm the fix is working.
Confirm the software switch is in the "OFF" position.
sudo cat /sys/kernel/debug/vgaswitcheroo/switch- Success:
0:DIS: :Off:0000:01:00.0 - Failure:
0:DIS: :Pwr:0000:01:00.0
Confirm the hardware is cold (Sensor should be missing or N/A).
sensors- Success: No
nouveauadapter listed, or temp isN/A. - Failure:
nouveau-pci-0100 ... temp1: +45.0°C
Since the HDMI port is wired to the NVIDIA card, you need a quick way to turn the card back ON if you want to use an external monitor.
Use this if you need to plug in an HDMI cable or if the system isn't detecting screens correctly.
#!/bin/bash
# RESTORE NVIDIA GPU (For HDMI/DP Output)
echo "Turning NVIDIA Card ON..."
if [ -f /sys/kernel/debug/vgaswitcheroo/switch ]; then
echo ON | sudo tee /sys/kernel/debug/vgaswitcheroo/switch
echo "Success. NVIDIA card is powered up."
echo "You may need to log out/in for xrandr to detect the new ports."
else
echo "Error: Switcheroo file not found."
fiA simplified version of the monitor script to quickly answer: "Is it on or off?"
#!/bin/bash
# CHECK GPU POWER STATE
echo "--- SWITCH STATUS ---"
if [ -f /sys/kernel/debug/vgaswitcheroo/switch ]; then
grep "DIS" /sys/kernel/debug/vgaswitcheroo/switch
else
echo "vgaswitcheroo: NOT FOUND"
fi
echo "--- POWER SENSORS ---"
# Check if nouveau is reporting temp (Alive) or if it's gone (Dead)
if sensors | grep -q "nouveau"; then
TEMP=$(sensors | grep -A 2 "nouveau" | grep "temp1" | awk '{print $2}')
echo "NVIDIA Sensor: ALIVE ($TEMP)"
else
echo "NVIDIA Sensor: DEAD (No data = Good for battery)"
fiCheck to see if iris is actually being used by the system.
#!/bin/bash
# =================================================================
# IRIS KEEPER: SYSTEM AUDIT TOOL (V3 - ROBUST)
# =================================================================
# Checks the health of the MacBook Pro 11,3 graphics fix.
# =================================================================
echo "--- STARTING SYSTEM AUDIT ---"
echo "Date: $(date)"
echo "-----------------------------------------------------------"
# 1. CHECK FIRMWARE (EFI)
echo -n "[1/5] Checking EFI Firmware Fix... "
# Method: Find the variable by name, ignore path strictness
# We search for 'gpu-power-prefs' inside the efivars folder
if [ ! -d "/sys/firmware/efi/efivars" ]; then
# Try to mount if missing
mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
fi
EFI_FOUND=$(find /sys/firmware/efi/efivars -name "*gpu-power-prefs-*" 2>/dev/null)
if [ -n "$EFI_FOUND" ]; then
echo "PASS"
else
echo "FAIL (Variable not found)"
echo " -> The system is working, but the tool can't see the file."
echo " -> If you reboot and get a black screen, run restore-screen.sh."
fi
# 2. CHECK BOOT PARAMETERS (GRUB)
echo -n "[2/5] Checking Boot Parameters... "
if grep -q 'acpi_osi=Darwin' /proc/cmdline; then
echo "PASS"
else
echo "FAIL (Missing acpi_osi=Darwin)"
echo " -> Edit /etc/default/grub -> GRUB_CMDLINE_LINUX_DEFAULT."
fi
# 3. CHECK ACTIVE DISPLAY (Hardware ID Check)
echo -n "[3/5] Checking Active Display... "
# Find the connector that is "connected" (Internal Screen)
STATUS_FILE=$(grep -l "^connected$" /sys/class/drm/*/status 2>/dev/null | grep -E "eDP|LVDS|DP" | head -n 1)
if [ -n "$STATUS_FILE" ]; then
# Resolve the full physical path of the connector directory
CONN_DIR=$(dirname "$STATUS_FILE")
PHYS_PATH=$(readlink -f "$CONN_DIR")
# Go up directories until we find the PCI ID (vendor file)
# Structure: .../pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/
# We look for the folder containing the file 'vendor'
PCI_DIR="$PHYS_PATH"
while [ "$PCI_DIR" != "/" ]; do
if [ -f "$PCI_DIR/vendor" ]; then
break
fi
PCI_DIR=$(dirname "$PCI_DIR")
done
if [ -f "$PCI_DIR/vendor" ]; then
VENDOR_ID=$(cat "$PCI_DIR/vendor")
# Intel is 0x8086. NVIDIA is 0x10de.
if [[ "$VENDOR_ID" == "0x8086" ]]; then
echo "PASS (Intel Chip Active)"
elif [[ "$VENDOR_ID" == "0x10de" ]]; then
echo "FAIL (NVIDIA Chip Active)"
else
echo "WARN (Unknown Vendor: $VENDOR_ID)"
fi
else
echo "FAIL (Could not trace hardware path)"
fi
else
echo "FAIL (No connected screen found?)"
fi
# 4. CHECK BACKLIGHT
echo -n "[4/5] Checking Brightness Keys... "
if lsmod | grep -q "apple_gmux"; then
echo "PASS (apple_gmux loaded)"
else
echo "FAIL (apple_gmux missing)"
fi
# 5. CHECK 3D ACCELERATION
echo -n "[5/5] Checking 3D Acceleration... "
if command -v glxinfo >/dev/null; then
if glxinfo -B 2>/dev/null | grep -iE "Intel|Iris" >/dev/null; then
echo "PASS"
else
RENDERER=$(glxinfo -B 2>/dev/null | grep "Device:")
echo "FAIL (Renderer: $RENDERER)"
fi
else
echo "SKIP (glxinfo missing)"
fi
echo "-----------------------------------------------------------"
echo "AUDIT COMPLETE"Create this file to watch your battery draw in real-time.
#!/bin/bash
# IRIS KEEPER: Power Monitor (Extended)
# Dependency: bc (sudo apt install bc)
while true; do
clear
echo "=== IRIS POWER MONITOR ==="
echo "Time: $(date +%T)"
echo "--------------------------"
# 1. Check Software Switch (vgaswitcheroo)
if [ -f /sys/kernel/debug/vgaswitcheroo/switch ]; then
SWITCH_STATE=$(grep "DIS" /sys/kernel/debug/vgaswitcheroo/switch)
echo "Switcheroo: FOUND"
echo "State: $SWITCH_STATE"
else
echo "Switcheroo: NOT FOUND"
fi
echo "--------------------------"
# 2. Check Hardware Link (PCIe Bridge)
if command -v lspci &> /dev/null; then
# Check bridge 00:01.0 (The gateway to the GPU)
LINK_INFO=$(sudo lspci -vv -s 00:01.0 2>/dev/null | grep -E "LnkSta:|Power")
SPEED=$(echo "$LINK_INFO" | grep -o "Speed [^,]*")
WIDTH=$(echo "$LINK_INFO" | grep -o "Width [^,]*")
if [[ -z "$SPEED" ]]; then
echo "PCIe Link: [Reading Error]"
else
echo "PCIe Link: $SPEED, $WIDTH"
fi
fi
echo "--------------------------"
# 3. Check Battery Metrics
if [ -d /sys/class/power_supply/BAT0 ]; then
STATUS=$(cat /sys/class/power_supply/BAT0/status)
# Raw values are usually in micro-units (uV, uA)
VOLTAGE_RAW=$(cat /sys/class/power_supply/BAT0/voltage_now)
CURRENT_RAW=$(cat /sys/class/power_supply/BAT0/current_now)
# Convert to human readable (Volts, Amps)
VOLTS=$(echo "scale=2; $VOLTAGE_RAW / 1000000" | bc)
AMPS=$(echo "scale=3; $CURRENT_RAW / 1000000" | bc)
# Calculate Watts: (uV * uA) / 10^12
WATTS=$(echo "scale=2; ($VOLTAGE_RAW * $CURRENT_RAW) / 1000000000000" | bc)
echo "Battery: $STATUS"
echo "Voltage: $VOLTS V"
echo "Current: $AMPS A"
echo "Power Draw: $WATTS W"
else
echo "Battery: NOT DETECTED"
fi
echo "=========================="
echo "Press [CTRL+C] to exit"
sleep 2
doneTarget Power Draw:
- Idle (Desktop): ~10W - 15W
- Load: > 20W