1#!/usr/bin/env python3

2# Copyright 2020 Canonical Ltd.

3# All rights reserved.

4#

5# Written by:

6# Nara Huang <nara.huang@canonical.com>

7#

8# Script to get system ethernet device MAC address, and from ACPI DSDT table,

9# grep "AUXMAC" to get pass-through MAC address,

10# then compare them to check if MAC address pass-through function is working

11 import subprocess

12 import glob

13 import os

14 import shutil

15

16 MAC_PASS_SMBIOS_TOKEN = "0x49b"

17

18

19 def get_system_mac():

20 """Get mac addresses from ethernet devices

21 Returns:

22 List: Mac addresses of ethernet devices

23 """

24 mac_addresses = []

25 for interface in glob.glob("/sys/class/net/e*/address"):

26 try:

27 with open(interface, "r") as iface:

28 mac = iface.read()

29 mac = mac.strip()

30 mac = mac.replace(":", "")

31 mac_addresses.append(mac)

32 except Exception as err:

33 raise SystemExit(err)

34 return mac_addresses

35 # Since system could have multiple ethernet interfaces,

36 # the return value is a list.

37

38

39 def get_pass_through_mac():

40 """Get pass-through mac address from BIOS ACPI DSDT table

41 iasl command is included in acpica-tools package

42 Returns:

43 String: Pass-through MAC address from DSDT table

44 """

45 tmp_path = os.environ.get("SNAP_USER_DATA", "/tmp")

46 aml_path = os.path.join(tmp_path, "dsdt.aml")

47 dsl_path = os.path.join(tmp_path, "dsdt.dsl")

48 try:

49 shutil.copy("/sys/firmware/acpi/tables/DSDT", aml_path)

50 subprocess.call(

51 ["iasl", "-d", aml_path],

52 stdout=subprocess.DEVNULL,

53 stderr=subprocess.STDOUT,

54 )

55 with open(dsl_path, encoding="utf-8", errors="ignore") as dsl:

56 dsdt = dsl.readlines()

57 for line in dsdt:

58 if "AUXMAC" in line:

59 bios_mac = line.split("#")[1]

60 print(

61 "Pass-through MAC address from DSDT table: "

62 + bios_mac.lower()

63 )

64 return bios_mac.lower()

65 raise SystemExit(

66 "No AUXMAC is found in DSDT table, "

67 "MAC address pass-through is not working."

68 )

69 except Exception as err:

70 raise SystemExit(err)

71

72

73 def check_mac_passthrough(mac, bios_mac):

74 """Compare pass-through MAC address from BIOS DSDT table

75 and system MAC address, to check if the pass-through function works.

76 Args:

77 mac: A list contains MAC addresses from system

78 bios_mac: A string of MAC address from BIOS DSDT table

79 """

80 # Check duplicate item in mac list first

81 if len(mac) != len(set(mac)):

82 print(

83 "AUXMAC in DSDT table is identical to onboard NIC MAC, which "

84 "means in BIOS setting, it is set to "

85 '"Integrated NIC 1 MAC Address".'

86 )

87 return 0

88 if bios_mac in mac:

89 print(

90 "AUXMAC in DSDT table is passed to dock, MAC address "

91 "pass-through is working."

92 )

93 return 0

94 else:

95 raise SystemExit(

96 "MAC address pass-through is not working, "

97 "maybe the dock is not connected?"

98 )

99 # If a system enables mac pass-through function,

100 # but not connected with a dock, it goes here.

101

102

103 def check_smbios_token(index):

104 """Check SMBIOS for BIOS MAC address pass-through setting.

105 If it shows "false", then the function is enabled in

106 BIOS setting.

107 This function requires command "smbios-token-ctl",

108 which comes with package "smbios-utils"

109 Args:

110 index: A string for SMBIOS token index

111 """

112 smbios_token_cmd = ["smbios-token-ctl", "--token-id="]

113 smbios_token_cmd[1] += index

114 out = subprocess.run(smbios_token_cmd, check=False, capture_output=True)

115 for line in out.stdout.decode("utf-8").splitlines():

116 if "value: bool" in line:

117 if "true" in line:

118 raise SystemExit(

119 "MAC address pass-through " "is disabled in BIOS setting."

120 )

121 else:

122 print("MAC address pass-through is enabled in BIOS setting.")

123

124

125 def main():

126 check_smbios_token(MAC_PASS_SMBIOS_TOKEN)

127 pasthrough_mac_addresses = get_pass_through_mac()

128 os_mac_addresses = get_system_mac()

129 check_mac_passthrough(os_mac_addresses, pasthrough_mac_addresses)

130

131

132 if __name__ == "__main__":

133 raise SystemExit(main())