GeodeNX用にpowernow-k7をハックする

私が使っているLinux箱は、ABIT の VA-20 というマザーボードに、AMDのGeode NX 1500というマニアックなCPUを載せて動かしています。

この Geode NX 1500 は中身は殆ど Athron XP-M と同一のもので、なんといってもTDP 9Wという低消費電力に魅力を感じ、常時起動しっぱなしになるLinuxサーバーのCPUとして使用しています。

このCPUは、実はPowerNow!にも対応しているため、ソフトウェアにより駆動周波数を制御することが可能です。ところが、これは普通は動作しません。

一般のマザーボードではPowerNow!は動作できません

モバイルCPUは、AMDの省電力機構であるAMD PowerNow!の動作もサポートしています。PowerNow!はOSの起動プロセス中にCPUの定格倍率を検出し、動作周波数および動作電圧を動的に調整する機能です。しかしPowerNow!の動作は、「搭載CPUの倍率、電圧テーブルがBIOS内に登録されていることが必須」ですから、PowerNow!のソフトウエア(ドライバー)単体では動作できず、対応BIOSが必要です。一般のマザーボードでは、BIOSにGeode NXの倍率や電圧のデータが用意されていることは、まずありませんからPowerNow!は動きません。

Geode NXプロセッサの詳細 倍率を変え動作クロックを変更する - Fab51

残念ながら、BIOSに「倍率・電圧テーブル」が無いと動作しないわけですね。

Windowsなら諦めてCrystalCPUIDなんかのソフトで我慢するしか無いわけですが、Linuxなら対応策があります。所詮BIOS内の表を読むのもOS(Windows/Linux)なのだから、OSを改造すれば自由自在です。

というわけで、今日は、私が使っている改造版PowerNowモジュールの紹介です。

ハックのポイント

Athron XP-M 用の cpufreq のモジュールは powernow-k7 で、ソースは linux/arch/i386/kernel/cpu/cpufreq/powernow-k7.c にあります。 この中で、BIOS中に入っているテーブルを参照しているコードもここにあります。

やることは単純で、BIOSからテーブルを読むコードをコメントアウトして、かわりに嘘のテーブルが読めたことにしてやるわけです。

というわけで、編集箇所は以下の通りです。

--- powernow-k7.c	2006-11-12 00:12:21.000000000 +0900
+++ powernow-k7-mobilehack.c	2008-06-04 01:31:16.000000000 +0900
@@ -32,7 +32,7 @@
 #include <acpi/processor.h>
 #endif
 
-#include "powernow-k7.h"
+#include "powernow-k7-mobilehack.h"
 
 #define PFX "powernow: "
 
@@ -103,6 +103,7 @@ static char have_a0;
 
 #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "powernow-k7", msg)
 
+#if 0
 static int check_fsb(unsigned int fsbspeed)
 {
 	int delta;
@@ -111,6 +112,7 @@ static int check_fsb(unsigned int fsbspe
 	delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
 	return (delta < 5);
 }
+#endif
 
 static int check_powernow(void)
 {
@@ -409,6 +411,7 @@ static int powernow_acpi_init(void)
 
 static int powernow_decode_bios (int maxfid, int startvid)
 {
+#if 0
 	struct psb_s *psb;
 	struct pst_s *pst;
 	unsigned int i, j;
@@ -480,6 +483,21 @@ static int powernow_decode_bios (int max
 	}
 
 	return -ENODEV;
+#endif
+	char tbl[] = {
+		0x09, 0x2b, /* 7.5x 1.000V(1.175V) */
+		0x08, 0x2b, /* 7.0x 1.000V(1.175V) */
+		0x07, 0x2b, /* 6.5x 1.000V(1.175V) */
+		0x06, 0x2b, /* 6.0x 1.000V(1.175V) */
+		0x05, 0x2b, /* 5.5x 1.000V(1.175V) */
+		0x04, 0x2b, /* 5.0x 1.000V(1.175V) */
+	};
+
+	printk(KERN_WARNING PFX "powernow-k7-mobilehack: it supports Geode NX 1500 only!\n");
+
+	latency = 100;
+	number_scales = sizeof(tbl) / (sizeof(char) * 2);
+	return get_ranges(tbl);
 }
 
 
@@ -653,7 +671,7 @@ static struct cpufreq_driver powernow_dr
 	.get	= powernow_get,
 	.init	= powernow_cpu_init,
 	.exit	= powernow_cpu_exit,
-	.name	= "powernow-k7",
+	.name	= "powernow-hack",
 	.owner	= THIS_MODULE,
 	.attr	= powernow_table_attr,
 };

(追記@2012-03-10: 疑似テーブルを周波数が高い順に並ぶように修正しました。一部の設定スクリプトがこの表の先頭の物が最高速度だという前提で書かれていたのでそれに対する対応です。)

勘の良い人は気付くかもしれませんが、上記のpatchには、モジュール名の変更やソース/ヘッダファイル名の変更も含まれています。

これは、本来のカーネルソースから作られたモジュールとバッティングしないようにする変更です。このパッチを当てたソースを、カーネルソースとは別にとっておいて、カーネルモジュールビルド用のmakefileを書くことで、カーネルソース無しでも make install 一発で安全にインストールできるようになります。

これで、ディストリビューションカーネルがバージョンアップした際に、追従が億劫にならずに済むので、意外と重要なテクニックです。

モジュールのロードと設定

モジュールを通常の /lib/modules/`uname -r`/ にインストールしているなら、 modprobe でロードできます。が、どうせ cpuspeed サービスで動作させるので、そちらに設定してしまいましょう。

設定するのは /etc/sysconfig/cpuspeed で、DRIVER に、読み込むモジュール名を、GOVERNOR には ondemand を指定します。

--- cpuspeed	2008/06/23 14:51:22	1.1
+++ cpuspeed	2008/06/23 14:52:20
@@ -9,7 +9,7 @@
 # so its usually best not to specify one. The most commonly-needed driver
 # module these days is 'p4-clockmod'.
 # default value: empty (auto-detect/use built-in)
-DRIVER=
+DRIVER=powernow-k7-mobilehack
 
 ### GOVERNOR ###
 # Which scaling governor to use
@@ -23,7 +23,7 @@ DRIVER=
 # - Using the 'userspace' governor will trigger the cpuspeed daemon to run,
 #   which provides said user-space frequency scaling.
 # default value: empty (defaults to ondemand)
-GOVERNOR=
+GOVERNOR=ondemand
 
 ### FREQUENCIES ###
 # NOTE: valid max/min frequencies for your cpu(s) can be found in

コメントには、「ondemand は powernow-k8 とかじゃないとダメだよ」とか書いてあるんですが、試しに使ってみたところ、なぜかちゃんと動いているように見えるので ondemand にしておきました。

cpuspeed サービスは CentOS 5.1 ではデフォルトでOnになっているので、次回起動時からはビルドしたモジュールが有効になっているはずです。

ちなみに、ondemand governor に設定すると、速度の変更がカーネル内部で完結するため、ユーザーランドのデーモンが必要無くなります。そのため、たとえ cpuspeed サービスを起動するように設定していても、デーモンプロセスは起動しなくなります。 それでも cpuspeed サービスを有効にするのは、モジュールのロードと設定を代行してくれるからです。

動作確認

再起動後に、 ondemand governor で動作していることと、

$ /sbin/service cpuspeed status
Frequency scaling enabled using ondemand governor

適当な負荷をかけて、 /proc/cpuinfo の周波数表示が上下していることが観測できればOKです。

何なら、あえて可動周波数帯を最大onlyや最小onlyに設定して、実際の計算速度が変わることを確認してみても良いかもしれません。(私がこのモジュールを作った時には確認しました。)