My Cloud PR4100 / PR2100 Firmware Analysis


#87

Finding the actual hardware has definitely been a problem, but I’m learning a lot as I continue trying. The fan is just the first step, where I hope to eventually have control of the LED display, etc.

Indeed, having the source code would change everything. At the very least, it would save my sanity… too many hours staring at a hex editor.


#88

DSDT.txt (321.9 KB)
Here is the decoded DSDT table.
Now we just need to be able to talk to the I2C devices.


#89

Is that table from the EX4100? If so, my PR4100 table is likely different, depending on the chips on each motherboard. Later, I will decode my table and see what it can reveal. It will also be interesting to do a diff between them.

I see a lot of serialization in your table, which confirms what I have been seeing. Everything seems to point to the serial bus, but I still can’t pin it down.


#90

DSDT-PR4100.txt (352.5 KB)

Here is the decoded DSDT table from the PR4100. I performed a quick diff and they are in fact different. However, significant portions of the tables are the same.

Decompiling ACPI Tables:

https://blog.fpmurphy.com/2014/12/decompiling-acpi-tables.html

iASL compiler and Windows ACPI tools:

https://acpica.org/downloads/binary-tools

Writing an ACPI driver:

https://lwn.net/Articles/367630/


#91

I don’t get data from most of the I2S busses but /dev/i2s-8 is a bit different.

addr data
0x30 0xFF
0x31 0xFF
0x4c 0x20
0x50 0x55
0x51 0x55

To get this data, I used python with pip install smbus2

import smbus2

b = smbus2.SMBus(8)

for d in range(128):
    try:
        data = bus.read_byte(d)
        print hex(d), hex(data)
    except:
        pass

The last 2 addresses are changing occasionally.


#92

Two shared library files seem to be linked to fan control.

libthermal_management.so.1
libthermal_service.so.1

So far, I have only located two executables which seem to have any effect on the fan.

/opt/wd/bin/wdtms
/usr/sbin/fan_control

Given the fact that both are related to thermal control, I suspect that they access the same hardware, using the same control method.

Unfortunately, reverse engineering compiled binary files is not my forte.


#93

Readings with SMBus on i2c-8 on address 0x4c:

for j in range(16): print ' '.join('%02x' % bus.read_byte_data(0x4c, j*16+k) for k in range(16))

1f 21 80 00 06 55 00 55 00 00 06 55 00 55 00 00
60 00 00 00 00 55 00 00 00 55 55 00 00 00 00 00
55 0a 70 23 a0 0f 0f 12 12 c0 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
06 07 07 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 63 a0 00 00 00 00 00 00 2b 92 26 50
20 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 21 5d 03

This confirms the suspicion from sensors-detect: this is the SMSC 1073, based on the last 3 bytes.
http://ww1.microchip.com/downloads/en/DeviceDoc/1073_1074.pdf

LM-sensors doesn’t have a driver yet, but that seems a nice project for this weekend :slight_smile:

Reading the internal chip temperature

bus.read_byte_data(0x4c, 0x00)
31

Reading the external diodes temperature

bus.read_byte_data(0x4c, 0x01)
33
bus.read_byte_data(0x4c, 0x23)
35
bus.read_byte_data(0x4c, 0x2a)
0      (no sensor connected)

Cool beans.


#94

But, does the PR4100 share the same SMSC EMC1073 thermal sensor chip? By the way, I found a proposed patch for sensors-detect that would have added detection support.

name => "SMSC EMC1073",
+		driver => "to-be-written",
+		i2c_addrs => [0x1c, 0x3c, 0x4c, 0x5c, 0x6c, 0x7c],
+		i2c_detect => sub { emc1403_detect(@_, 9); }

However, the proposed patch was rejected with the following explanation.

These changes add I2C addresses 0x10, 0x3c, 0x3d, 0x6c and 0x7c to the
list of addresses being probed (check with sensors-detect --stat).
Adding new probed addresses is potentially dangerous, as we have had
(regular and sometimes dramatic) reports of issues related to probing
specific I2C addresses in the past. In general, I strongly suggest to
_not_ add new addresses without a good reason.

So we have to think about the usage of the EMC devices in question. Do
you know if any of these are being, or will be, used on PC boards?
Remember that the purpose of sensors-detect is not to be a universal
hardware monitoring device detector. It's primarily aimed at PC users.
Users of embedded devices or exotic hardware should know what hardware
monitoring devices they have, and these device should be instantiated by
the kernel directly, rather than being detected from user-space.

https://www.spinics.net/lists/lm-sensors/msg31122.html
https://www.spinics.net/lists/lm-sensors/msg31128.html


#95

It looks like the EMC1073 uses the EMC1403 lookup code, but the driver is to-be-written.
See here

However the datasheets are almost completely the same (when comparing the registers), except for 1 extra feature in the EMC1403: it has beta compensation.
All registers are the same, except for 0x25 and 0x26 is reserved for the beta compensation params in the EMC1403.

page 27 - http://ww1.microchip.com/downloads/en/DeviceDoc/1073_1074.pdf
page 29 - http://www.mouser.com/ds/2/268/20005272A-468064.pdf

The EMC1403 has a driver - emc1403 and it doesn’t use the beta compensation

So you can just use this driver for the temperature chip, this can be done in the lm-sensors config file.
Next step is the fan.


#96

The only way to solve the problem is to know exactly what chips are on the PR4100 motherboard. The fan needed cleaning, so I took the opportunity to go a step further and see exactly what chips are being used.

MXIC BIOS Chip (Serial I/O):
MX25U6435FM21-10G

Micron 4GB MMC Flash Memory:
MTFC4GMCDM-1M (JW963)

STMicroelectronics 32-bit ARM Cortex MCU:
STM32F030R8T6

Asmedia SATA III Controller:
ASM1061

Asmedia SATA III Controller:
ASM1061

RealTek Gigabit Ethernet Controller (PCI Express):
RTL8111H

RealTek Gigabit Ethernet Controller (PCI Express):
RTL8111H

As it turns out, the PR4100 does not have a SMSC EMC1073 chip. The STMicroelectronics 32-bit ARM Cortex MCU (STM32F030R8T6) chip is responsible for most of the I/O operations, including fan control.

The STM32F030x4/x6/x8/xC microcontrollers incorporate the high-performance ARM®Cortex®-M0 32-bit RISC core operating at a 48 MHz frequency, high-speed embedded memories (up to 256 Kbytes of Flash memory and up to 32 Kbytes of SRAM), and an extensive range of enhanced peripherals and I/Os. All devices offer standard communication interfaces (up to two I2Cs, up to two SPIs and up to six USARTs), one 12-bit ADC, seven general-purpose 16-bit timers and an advanced-control PWM timer.

http://www.st.com/en/microcontrollers/stm32f030r8.html


#97

EMC1073 and EMC1403 have the exact same supplier code and model number, but there’s 4 years between the datasheets and SMSC was bought by MicroChip.

Sensors-detect stops on the first match, which results in the detection of EMC1073… but I’m quite sure it’s the EMC1403.
I’m certain that there is an I2C device connected on the bus (the ST micro), it measures the temperature and it’s one of these 2 devices (as all the registers match).

The results are shown in WD’s fan_control program (it shows 4 + 4 + 1 temperature measurements), but debian’s sensors doesn’t show the disk temperatures due the missing driver.
This is fixed by adding emc1403 to /etc/modules.

The fan is controlled by the BIOS. Here is the best overview I could find. I don’t know how to get access to the RPM value.

# cat /sys/class/thermal/cooling_device0/type
Fan

# cat /sys/class/thermal/cooling_device0/device/uevent
DRIVER=acpi-fan
MODALIAS=acpi:PNP0C0B:

#98

The following commands should show a relatively complete list of ACPI devices. Path directly correlates to the DSDT.

Path only:
for f in $(find /sys -name path -type f); do echo "$f" && echo -e -n "\t" && cat $f; done

Hid only:
for f in $(find /sys -name hid -type f); do echo "$f" && echo -e -n "\t" && cat $f; done

Path and hid:
for f in $(find /sys -name hid -type f -or -name path -type f); do echo "$f" && echo -e -n "\t" && cat $f; done


#99

I had a look at GPL/kernel/linux-4.1.13/drivers/acpi/fan.c provided by WD. It has the commands to get the fan speed.
I’m a python guy, so I’d borrow some idea’s here and use that to talk to /var/run/acpid.socket


#100

I believe those are standard generic ACPI drivers that come with the kernel source code. The custom WD stuff is in different folders. Unfortunately, everything required to control the hardware is stored in compiled binary files.

WDMyCloud_PR4100_GPL_v2.30.165_20170321\firmware\module\crfs\opt\wd\bin
WDMyCloud_PR4100_GPL_v2.30.165_20170321\firmware\module\crfs\opt\wd\lib
WDMyCloud_PR4100_GPL_v2.30.165_20170321\firmware\module\crfs\usrsbin

#102

Cooling Devices:

# cat /sys/devices/virtual/thermal/cooling_device0/type
	Fan
# cat /sys/devices/virtual/thermal/cooling_device1/type
	Processor
# cat /sys/devices/virtual/thermal/cooling_device2/type
	Processor
# cat /sys/devices/virtual/thermal/cooling_device3/type
	Processor
# cat /sys/devices/virtual/thermal/cooling_device4/type
	Processor
# cat /sys/devices/virtual/thermal/cooling_device5/type
	LCD

CPU Core Temperature:

# cat /sys/class/hwmon/hwmon1/name
	coretemp
# cat /sys/class/hwmon/hwmon1/temp2_label
	Core 0
# cat /sys/class/hwmon/hwmon1/temp2_crit
	90000
# cat /sys/class/hwmon/hwmon1/temp2_crit_alarm
	0
# cat /sys/class/hwmon/hwmon1/temp2_input
	40000
# cat /sys/class/hwmon/hwmon1/temp2_max
	90000
# cat /sys/class/hwmon/hwmon1/temp3_label
	Core 1
# cat /sys/class/hwmon/hwmon1/temp3_crit
	90000
# cat /sys/class/hwmon/hwmon1/temp3_crit_alarm
	0
# cat /sys/class/hwmon/hwmon1/temp3_input
	40000
# cat /sys/class/hwmon/hwmon1/temp3_max
	90000
# cat /sys/class/hwmon/hwmon1/temp4_label
	Core 2
# cat /sys/class/hwmon/hwmon1/temp4_crit
	90000
# cat /sys/class/hwmon/hwmon1/temp4_crit_alarm
	0
# cat /sys/class/hwmon/hwmon1/temp4_input
	39000
# cat /sys/class/hwmon/hwmon1/temp4_max
	90000
# cat /sys/class/hwmon/hwmon1/temp5_label
	Core 3
# cat /sys/class/hwmon/hwmon1/temp5_crit
	90000
# cat /sys/class/hwmon/hwmon1/temp5_crit_alarm
	0
# cat /sys/class/hwmon/hwmon1/temp5_input
	40000
# cat /sys/class/hwmon/hwmon1/temp5_max
	90000

i2c Adapter Devices:

# cat /sys/class/i2c-adapter/i2c-0/name
	i915 gmbus ssc
# cat /sys/class/i2c-adapter/i2c-1/name
	i915 gmbus vga
# cat /sys/class/i2c-adapter/i2c-2/name
	i915 gmbus panel
# cat /sys/class/i2c-adapter/i2c-3/name
	i915 gmbus dpc
# cat /sys/class/i2c-adapter/i2c-4/name
	i915 gmbus dpb
# cat /sys/class/i2c-adapter/i2c-5/name
	i915 gmbus dpd
# cat /sys/class/i2c-adapter/i2c-6/name
	DPDDC-B
# cat /sys/class/i2c-adapter/i2c-7/name
	DPDDC-D
# cat /sys/class/i2c-adapter/i2c-8/name
	SMBus I801 adapter at f040

i2c Devices:

# cat /sys/class/i2c-dev/i2c-0/name
	i915 gmbus ssc
# cat /sys/class/i2c-dev/i2c-1/name
	i915 gmbus vga
# cat /sys/class/i2c-dev/i2c-2/name
	i915 gmbus panel
# cat /sys/class/i2c-dev/i2c-3/name
	i915 gmbus dpc
# cat /sys/class/i2c-dev/i2c-4/name
	i915 gmbus dpb
# cat /sys/class/i2c-dev/i2c-5/name
	i915 gmbus dpd
# cat /sys/class/i2c-dev/i2c-5/name
	i915 gmbus dpd
# cat /sys/class/i2c-dev/i2c-6/name
	DPDDC-B
# cat /sys/class/i2c-dev/i2c-7/name
	DPDDC-D
# cat /sys/class/i2c-dev/i2c-8/name
	SMBus I801 adapter at f040

ACPI Devices:

# cat /sys/devices/LNXSYSTM:00/hid
	LNXSYSTM
# cat /sys/devices/LNXSYSTM:00/hid
	\
# cat /sys/devices/LNXSYSTM:00/LNXCPU:00/hid
	LNXCPU
# cat /sys/devices/LNXSYSTM:00/LNXCPU:00/path
	\_PR_.CPU0
# cat /sys/devices/LNXSYSTM:00/LNXCPU:01/hid
	LNXCPU
# cat /sys/devices/LNXSYSTM:00/LNXCPU:01/path
	\_PR_.CPU1
# cat /sys/devices/LNXSYSTM:00/LNXCPU:02/hid
	LNXCPU
# cat /sys/devices/LNXSYSTM:00/LNXCPU:02/path
	\_PR_.CPU2
# cat /sys/devices/LNXSYSTM:00/LNXCPU:03/hid
	LNXCPU
# cat /sys/devices/LNXSYSTM:00/LNXCPU:03/path
	\_PR_.CPU3
# cat /sys/devices/LNXSYSTM:00/LNXPWRBN:00/hid
	LNXPWRBN
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/hid
	LNXSYBUS
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/path
	\_SB_
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT0002:00/hid
	INT0002
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT0002:00/path
	\_SB_.GPED
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT33BD:00/hid
	INT33BD
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT33BD:00/path
	\_SB_.MBID
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT33FF:00/hid
	INT33FF
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT33FF:00/path
	\_SB_.GPO0
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT33FF:01/hid
	INT33FF
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT33FF:01/path
	\_SB_.GPO1
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT33FF:02/hid
	INT33FF
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT33FF:02/path
	\_SB_.GPO2
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT33FF:03/hid
	INT33FF
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT33FF:03/path
	\_SB_.GPO3
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT3497:00/hid
	INT3497
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INT3497:00/path
	\_SB_.PIND
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INTCFD9:00/hid
	INTCFD9
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/INTCFD9:00/path
	\_SB_.TBAD
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/LNXPOWER:01/hid
	LNXPOWER
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/LNXPOWER:01/path
	\_SB_.USBC
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/hid
	PNP0A08
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/path
	\_SB_.PCI0
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0B00:00/hid
	PNP0B00
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0B00:00/path
	\_SB_.RTC0
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0C:00/hid
	PNP0C0C
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0C:00/path
	\_SB_.PWRB
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00/hid
	PNP0C0D
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00/path
	\_SB_.LID0
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0E:00/hid
	PNP0C0E
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0E:00/path
	\_SB_.SLPB
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:00/hid
	PNP0C0F
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:00/path
	\_SB_.LNKA
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:01/hid
	PNP0C0F
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:01/path
	\_SB_.LNKB
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:02/hid
	PNP0C0F
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:02/path
	\_SB_.LNKC
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:03/hid
	PNP0C0F
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:03/path
	\_SB_.LNKD
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:04/hid
	PNP0C0F
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:04/path
	\_SB_.LNKE
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:05/hid
	PNP0C0F
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:05/path
	\_SB_.LNKF
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:06/hid
	PNP0C0F
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:06/path
	\_SB_.LNKG
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:07/hid
	PNP0C0F
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0F:07/path
	\_SB_.LNKH
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C31:00/hid
	PNP0C31
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C31:00/path
	\_SB_.TPM_
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:01/hid
	LNXSYBUS
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:01/path
	\_TZ_
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:01/LNXPOWER:05/hid
	LNXPOWER
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:01/LNXPOWER:05/path
	\_TZ_.FN00
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:01/LNXTHERM:00/hid
	LNXTHERM
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:01/LNXTHERM:00/path
	\_TZ_.TZ01
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:01/PNP0C0B:00/hid
	PNP0C0B
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:01/PNP0C0B:00/path
	\_TZ_.FAN0

ACPI Thermal Zone:

# cat /sys/class/hwmon/hwmon0/name
	acpitz
# cat /sys/class/hwmon/hwmon0/temp1_crit
	95000
# cat /sys/class/hwmon/hwmon0/temp1_input
	26800
# cat /sys/devices/virtual/hwmon/hwmon0/name
	acpitz
# cat /sys/devices/virtual/hwmon/hwmon0/temp1_crit
	95000
# cat /sys/devices/virtual/hwmon/hwmon0/temp1_input
	26800
# cat /sys/devices/virtual/thermal/thermal_zone0/type
	acpitz
# cat /sys/devices/virtual/thermal/thermal_zone0/temp
	26800
# cat /sys/devices/virtual/thermal/thermal_zone0/policy
	step_wise
# cat /sys/devices/virtual/thermal/thermal_zone0/mode
	enabled
# cat /sys/devices/virtual/thermal/thermal_zone0/trip_point_0_temp
	95000
# cat /sys/devices/virtual/thermal/thermal_zone0/trip_point_0_type
	critical
# cat /sys/devices/virtual/thermal/thermal_zone0/trip_point_1_temp
	90000
# cat /sys/devices/virtual/thermal/thermal_zone0/trip_point_1_type
	hot
# cat /sys/devices/virtual/thermal/thermal_zone0/trip_point_2_temp
	85000
# cat /sys/devices/virtual/thermal/thermal_zone0/trip_point_2_type
	passive
# cat /sys/devices/virtual/thermal/thermal_zone0/trip_point_3_temp
	110000
# cat /sys/devices/virtual/thermal/thermal_zone0/trip_point_3_type
	active

#103

Just in case, I decided to rule out the possibility that GPIO ports were somehow involved with fan control, the theory being that the GPIO ports were being exported, then unexported after they were used. However, the following command line script returned a string of “Invalid Argument” errors. In other words, no GPIO ports are available, or at least none within the numerical range that was probed, which means that ACPI is almost certainly being used for fan control

# for i in $(seq 1 1000); do echo $i > /sys/class/gpio/export; done

This is far from an isolated problem. In fact, simple research has been greatly hindered by an endless flood of people with the same complaint about their fan not working properly with Linux. Invariably, requests for help are followed by the same obsolete answers about trying lm-sensors, sensors-detect, pwmconfig, fancontrol, etc. Had any of these so-called internet “experts” done done a little research, they would know that the problem most often affects laptop computers, and the tools they say to use often don’t work on laptops.

As it turns out, the My Cloud PR4100 is basically a laptop computer with a bunch of hard drives inside, except that it doesn’t have a screen. It even uses a standard 19 volt laptop power brick.

Frankly, I’m running out of ideas, but I can’t shake this nagging feeling that I’m close… very close to finding the answer.


#104

strace on wdhws

nanosleep({1, 0}, 0x7ffc21ad6a10)       = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0

no info yet on the other services.


#105

While trying to solve the fan control problem, I stumbled on something VERY interesting. Ever wonder why RAID rebuilds take so long? Turns out, WD is deliberately slowing them down, but I don’t know if there’s a good reason why they’re doing it.

From: /usr/local/modules/script/system_init

# slow down rebuild speed
md_sync_speed.sh min

From: /usr/local/modules/script/md_sync_speed.sh

#!/bin/sh

[ ! -e /tmp/speed_limit_min ] && cp -f /proc/sys/dev/raid/speed_limit_min /tmp/speed_limit_min
[ ! -e /tmp/speed_limit_max ] && cp -f /proc/sys/dev/raid/speed_limit_max /tmp/speed_limit_max

if [ -n "$1" ] ; then
	SPEED_VALUE=$1
	if [ "${SPEED_VALUE}" = "min" ] ; then
		SPEED_VALUE=`cat /tmp/speed_limit_min`
	elif [ "${SPEED_VALUE}" = "max" ] ; then
		SPEED_VALUE=`cat /tmp/speed_limit_max`
	fi
	#echo "${SPEED_VALUE}" > /proc/sys/dev/raid/speed_limit_max 
	sysctl -w dev.raid.speed_limit_max=${SPEED_VALUE}
else
	sysctl dev.raid.speed_limit_max
fi
#echo "speed_limit_max=$(cat /proc/sys/dev/raid/speed_limit_max)" > /dev/console

The default minimum setting is 1000 and the default maximum setting is 100000.

(1000 / 100000) * 100 = 1% of the maximum possible speed.

In other words, RAID rebuild times are 99% longer than they could be. To be fair, lots of variables are in play, so 99% may not be a completely accurate number, but still.


#106

This post has some valid reasons to slow down RAID rebuild: https://superuser.com/questions/625722/reducing-linux-software-raid-rebuild-speed


#108

Having recently discovered that the WD merge program, included with their GPL firmware releases, produces partially corrupted firmware files, I decided to investigate further.

Part of the investigation process is analyzing hexdumps of the merge program and the firmware file it creates. In this case, I begun by performing a series of diff (difference) checks between the original firmware file and a custom firmware file.

Original Firmware Header:

Original Firmware

Custom Firmware Header:

Custom Firmware

It became immediately apparent that the first 128 bytes of the firmware file are the header, and the first byte (0x80) seem to be the header length, but the rest is not so clear.

The firmware version is clearly visible, and the string “BlackCy” was found hardcoded in the merge program. The last two bytes (0xB6, 0x06) could mark the end of the header, or they could be part of the sequence (0x7A, 0x2C, 0x77, 0xDE, 0x2C, 0x94) appearing immediately before them. There also appears to be some padding, as denoted by a series of nulls (0x00).

Hexidecimal can be interpreted in many different ways. For example: The hexidecimal values 0x70 and 0x67 could be interpreted as follows.

0x70 = 112 decimal
0x67 = 103 decimal
0x7067 = 28775 decimal

I suspect that much of the differing information may be CRC checksums or file lengths, but I remain uncertain. For comparison, here is the output of the merge program, resulting from the custom firmware header shown above.

kernel name = uImage
firmware name = WD-NAS-firmware
size of structure = 128
default path=../module/crfs/default/config.xml
Product     ID = 0
Custom      ID = 14
Model       ID = 7
Hardware    ID = 1
Sub         ID = 1
CH off 1 = 128
CH len 1 = 5800432
CH off 2 = 5800560
CH len 2 = 3861088
CH off 3 = 9661648
CH len 3 = 102950912
CH off 4 = 112612560
CH len 4 = 16220
alignment = 3 , file name:uRamdisk
alignment = 1 , file name:default.tar.gz
CH checksum 1 = 10CE050C
CH checksum 2 = 16CDAEB
CH checksum 3 = 622E000
CH checksum 4 = 4F316EC6
Next_offset = 0x06B6942C
#@#@#@#@ header_checksum = 0xde772c7a #@#@#@#@
Directory = /tmp, Filename = grub.tgz
alignment = 3 , file name:grub.tgz
==========================================================
File Name: /tmp/grub.tgz
EX offset   = 0x06b6948c
EX len      = 182872
EX checksum = 0x3adae984
Next_offset = 0x00000000
len = 51800
fw ver : 2.30.165.0321.2017

It’s funny, but just after creating this post and checking it for errors, some of the values suddenly leaped out at me. The hexadecimal value 0x7A2C77DE is the “header checksum” 0xDE772C7A, and the hexidecimal value 0x2C94B606 is the “next offset” 0x06B6942C. Etc, etc, etc. Some hexidecimal values are reversed.