RX480 써멀 재도포 이후 팬을 컨트롤하는 놈이 궁금해졌다.
어떠한 원리로 컨트롤 하는지 알고 싶기 떄문에 RX480을 분석하기로 한다.
<Fig1. Fan Pinout>
RX480의 쿨러는 4PIN을 사용하고 있다. 그림 1과 같이
검은색 = GND
노란색 = +12V
녹색 = Senser
파란색 = Control
으로 구성되어 있다.
<Fig2. signal>
로직 애널라이저로 Sensor pin을 확인하면 Pulse-width modulation (PWM)로 구성되어 있는 것을 확인할 수 있다.
참고 자료 : https://en.wikipedia.org/wiki/Pulse-width_modulation
<Fig3. BIOS Version>
TechPowerUP 도구를 사용하여 RX480의 바이오스를 추출할 수 있다. 물론 플래싱 까지 가능하다.
<Fig4. BIOS Extract>
해당 바이오스 펌웨어는 당연히 Binwalk에서도 아무것도 할 수 없다.
삽질을 통해 분석한 결과...
<Fig5. BIOS Analysis>
해당 구조로 구성되어 있는 것을 확인할 수 있다.
조금 설명하자면 Little Endian으로 기록되어 있으며, 처음에는 AMD Magic signature로 추정되는 0x55AA가 기록되어 있으며 (linux kernel AMD gpu driver 참고함) 현재의 총 블럭 개수가 기록되어 있다. 이후 식별된 데이터는 Block의 크기가 기록되어 있어 해당 영역은 0x73 * 0x200 = 0xE600 만큼 크기인것을 확인할 수 있다. 이후 확인한 CheckSum이였으며, 계산 원리는 간단하다. 0번지 offset부터 사이즈 만큼 모든 byte를 더한 값이 CheckSum 데이터이다.
또한 온도 기록방식도 특이하였다. 최대 온도를 예를 들어 0x2134 이며 계산 식은 0x2134 / 0x64 = 0x55 (85) 인것을 확인할 수 있다.
RX480의 바이오스는 무결성 확인이 없기 때문에 아무나 수정해서 올릴 수 있다. 따라서 최대 온도와 RPM 온도 등의 설정정보를 범위 이상으로 놓으면 (?).....
온도 관련된 정보들은 어느정도 찾았으니 나머지는 여러분이 분석해보길 바란다.
<Fig6. cool!>
바이오스 수정해서 올리지 마세요. 벽돌 될 수 있습니다. 전 못고치며 모든 책임은 당신에게 있습니다.
import struct
rom_checksum = 0x21
venderid = 0x238
subid = 0x23A
deviceid = 0x24A
subvendorid = 0x238
min_temp = 0x9ED8
med_temp = 0x9EDA
high_temp = 0x9EDC
max_temp = 0x9EE4
max_RPM = 0x9EEB
max_gpufreq = 0x9C59
max_memory_freq = 0x9C5D
power_control_limit = 0x9C61
shutdown_temp = 0x9F1D
max_temp_power = 0x9F17
print "################################"
print "# RX480 Bios Analysis v0.01 #"
print "# #"
print "# hacked by koha #"
print "################################\n"
f=open("bios.rom","rb")
databuffer = f.read()
f.close()
checksum = struct.unpack("B",databuffer[rom_checksum])[0]
size = struct.unpack("B",databuffer[0x02])[0] * 0x200
check=0
for i in range(size):
check += struct.unpack("B",databuffer[i])[0]
check = checksum - (check % 0x100)
if checksum == check:
print "Checksum ok\n"
print "vendor ID : " + databuffer[venderid:venderid+2].encode('hex')
print "Sub ID : " + databuffer[subid:subid+2].encode('hex')
print "device ID : " + databuffer[deviceid:deviceid+2].encode('hex')
print "subvendor ID : " + databuffer[subvendorid:subvendorid+2].encode('hex')+"\n"
