読者です 読者をやめる 読者になる 読者になる
スポンサーリンク

自作のC言語プログラムから,BIOS設定(CMOS)を読み書きする方法 (の調査ログ)

C言語 asm windows linux ハードウェア hack

自作のプログラムから,BIOSの設定を変更する事は可能なのか。


例えばブートデバイス設定やブートシーケンスの設定は,

ふつうはPC起動時の「BIOS設定画面」から手動で変更するわけだが,

これらの項目を,自作プログラムから書き換える事はできるのか。

  • (1)BIOSやCMOSなど関連キーワードについて
  • (2)自作プログラムからBIOS/CMOSにアクセスする方法
  • (3)具体的なサンプルコードと実現方針
  • (4)結論

(1)BIOSやCMOSなど関連キーワードについて簡単におさらい


PCの起動の流れ:

  • ユーザはPCの電源を入れる。
  • PCのマザーボードに通電する。ここで,マザーボード上には,CPU,ROMまたRAMが設置されている。
    • ROM内には,BIOSのプログラムが入っている。
      • ROMとはいえ,フラッシュメモリなので,書き換え可能である。ここの書き換えは,BIOSアップデートを意味する。
    • RAM内には,BIOSの設定内容が記憶されている。ここは内蔵電池によって記憶がもっている。
      • このRAMがCMOS RAMである。略して単にCMOSとも呼ぶ。BIOSの設定が保管されている,重要な場所。
  • マザーボード上のCPUが動作し始める。
  • CPUは,BIOSプログラムを探索して実行する。
    • 前述の通り,BIOSプログラムは実メモリ上ではなく,BIOS用のROM上に存在する。その領域は仮想メモリとして認識されており,メモリ領域全体の末尾にマッピングされている。そのため,CPUがBIOSを探索する際には,メモリの末尾にジャンプした上で命令を読み込んでゆく。
  • BIOSは,各種デバイスを起動する。
  • BIOSは,各種デバイスの中から,ブートデバイス(ブートドライブ)を探索・決定する。
  • BIOSは,ブートドライブの先頭セクタ(=MBR)から,ブートローダを読み取り,メモリ上にロードして実行する。
  • ブートローダは,OS(のカーネル)を読み取り,メモリ上にロードして実行する。

ブート ストラップ - コンピュータの起動手順(CPUの動作フローが順を追って箇条書きになっており,非常に詳しい)
http://park12.wakwak.com/~eslab/pcmem...

  • CPU が起動直後に最初に参照するメモリ位置 (0xFFFF0) をリセットベクタという
  • メモリの 0xE0000 〜 0xFFFFF の範囲には、ROM に記録されたコード (システム BIOS) がマップされている。 そのため CPU がアドレス バスに出力した 0xFFFF0 の値は、チップセット (ノースブリッジ) のアドレス デコーダにより、システム BIOS (ROM) 側へのアクセスに切り替えられる (回路が開かれる)。
  • アドレス 0xFFFF0 の位置にはシステム BIOS先頭アドレスへのジャンプ命令があり、システム BIOS はその先頭から順次 CPU へ読み込まれて処理される。


redhat ブートプロセスの詳細
http://docs.redhat.com/docs/ja-JP/Red...

  • BIOS (Basic Input/Output System) は、ブートプロセスの第 1 ステップを制御するだけでなく、周辺機器デバイスに最低レベルのインターフェースを提供するファームウェアインターフェースです。
  • BIOS を装備した x86 システムでは、プログラムは読み取り専用の固定メモリーに書き込まれるため、常に使用できます。
  • システムのブート時に、プロセッサはシステムメモリーの最後で BIOS プログラムを探して、実行します。


BIOSの基本とトレンド
http://www.dosv.jp/other/0805/index.htm

  • BIOSプログラムはフラッシュメモリに書き込まれているため書き換えが可能で、BIOSプログラムを新しく書き換えることを「BIOSアップデート」と言う


フラッシュメモリ
http://ja.wikipedia.org/wiki/%E3%83%9...

  • 書き換え可能。一般にはROMに分類されるが、ROMでもRAMでもない存在として別に分類されている場合もある


マザーボードのCMOSクリアとは
http://arigato.web.infoseek.co.jp/pcj...

  • マザーボード上のCMOS RAM(Complementary Metal Oxide Semiconductor Random Access Memory)には、BIOSの設定画面において設定した情報が保存されています
  • CMOSクリア(CMOSは「しーもす」と読みます)とは、このCMOS RAMに保存されている情報を消去する操作のこと


そういうわけで,

  • BIOSの設定を読み書きするためには,CMOSの中身を読み書きできればよい。

ということになる。


そして,そのCMOSというのが存在する領域は実メモリ領域ではなく,

マザーボード上に存在するRAMなのである。


つまりC言語などのプログラミングで言うと,

普通のファイル読み書きやメモリ操作のAPIではとても手が届かない場所にある。

どうやってアクセスしたらよいのか?


(2)自作プログラムからBIOS/CMOSにアクセスする方法

かなり詳しい技術資料が。

CMOS Memory Map
http://www-ivs.cs.uni-magdeburg.de/~z...


全訳はめんどいので,かいつまんで消化する。

Originally, the IBM PC/AT only made use of a small portion of CMOS memory
and was defined in the IBM PC/AT Technical Reference Manual, specifically
bytes 10h, 12h, 14h-18h, 2Eh-33h. The balance was left undefined but was
quickly appropriated by various BIOS manufacturers for such user-selectable
options such as wait states, clock speeds, initial boot drive selection, and
password storage.

現在,PCのアーキテクチャの世界標準は,IBMの「PC/AT互換機」である。(Macを除く)

したがって,BIOS周りの作り込みについて知りたい場合,この規格について調査すればよい。


PC/AT互換機のCMOSメモリの仕様は,もともと小さい仕様だったのだが,

時代を経るうちにIBMのコントロール下に留まれなくなってきて,

今ではクロックスピードやブートドライブ情報などの様々な情報を格納するようになった。

PC/AT互換機
http://ja.wikipedia.org/wiki/PC/AT%E4...


CMOSの具体的なアドレス仕様も掲載されている。

Accessing the CMOS

The CMOS memory exists outside of the normal address space and cannot
contain directly executable code. It is reachable through IN and OUT
commands at port number 70h (112d) and 71h (113d). To read a CMOS byte,
an OUT to port 70h is executed with the address of the byte to be read and
an IN from port 71h will then retrieve the requested information. The
following BASIC fragment will read 128 CMOS bytes and print them to the
screen in 8 rows of 16 values.

1Dh - (Zenith Z-200 monitor) Boot Drive Selection
Bits 6-5 (0xx0 0000)
00 - MFM Monitor
01 - First floppy drive (A:)
10 - First fixed disk (C:)
11 - First floppy drive (A:). If not there then First fixed disk (C:)
(this is the default).
・・・


CMOS/BIOSは,CMOSメモリと称されるけれども,通常のメモリ空間には属さない。

CPUの70番・71番のポートを介してアクセスする。


ここで,CPUにポートがあるというのは耳慣れないかもしれない。


ちょうどTCP/IP上の通信におけるポート番号と同じように,

CPUにも入出力のためのポートが複数存在し,各種デバイスと情報をやり取りするのである。

PC内部でのそういった通信ないし情報伝達の事を,ポートマップドI/Oと呼ぶ。

I/Oポート
http://e-words.jp/w/I2FOE3839DE383BCE...

  • コンピュータ内部で、CPUが周辺機器にデータを送受信するために使う窓口。「入出力ポート」とも


メモリマップドI/O
http://ja.wikipedia.org/wiki/%E3%83%A...

  • ポートマップドI/Oでは、入出力用の特別なCPU命令を使用する。例えば、インテルのx86には入出力専用の IN 命令と OUT 命令があり、入出力機器の1つのバイトの読み書きを行う。入出力機器は主記憶装置とは分離したアドレス空間


I/O空間
http://ja.wikipedia.org/wiki/%E5%85%A...

  • Z80やIntelx86などのCPUやPCIバスなどでは、メインメモリとは別のアドレス空間としてI/O空間があり、ペリフェラルのレジスタを接続するために用意されている。 x86ではI/O空間はメモリ空間より狭く、アクセスできる命令も限定されている。 I/O空間のアドレスはI/OアドレスやI/Oポートアドレスと呼ばれる。

(3)具体的なサンプルコードと実現方針

上記の資料には,CMOS設定へのアクセスを実現するためのBASICのサンプルコードまで丁寧に掲載されている。

10 CLS
20 FOR i = 0 TO &H7F
30 OUT &H70, i
40 PRINT USING "\   \"; HEX$(INP(&H71));
50 NEXT i
60 PRINT " "

このコードが一体どういう環境で動作するんだ,という突っ込みは置いといて,

  • 70番や71番のレジスタへの入出力をしている
  • 機械語のレベルで割り込み命令(INT, INP, interrupt)を出している

というイメージはとりあえず掴めるだろう。



なお,通常のWindows/Win32API経由では無理。

OS起動後に,CPUが保護モードに入るため。

ROM BIOS Call in VC++ 6.0
http://www.programmersheaven.com/mb/C...

I am asking is about ROM BIOS Calls in VC++ 6.0 Envirment

Windows does not support direct access like that. You must use standard Win32 API function calls.

It's not Microsoft's fault that you can't do it. The reason is that the hardware CPU and computer is booted into protected mode when Windows starts.



Change BIOS settings using the C language
http://stackoverflow.com/questions/17...
in general how do I contact the BIOS settings?

You can access the BIOS settings via I/O port 70h and 71h. (Some chipsets also have an extended CMOS at 72/73 or similar.)
If you are running Windows, you won't be able to do port I/O directly from an application, you'll have to write a kernel mode driver. Under Linux, you can use /dev/nvram to get at the CMOS settings.

Maybe your motherboard vendor can provide you with a CMOS map.
Try dumping the CMOS settings with various memory sizes and figure out which bits change.

Once you change the setting, it won't take effect to the next reboot



Accessing BIOS using C language
http://www.programmersheaven.com/mb/C...
to access BIOS, you might want to use interrupts, e.g. int 21h and int 10h,.



Change Bios Using C/C++?
http://thedailyreviewer.com/dotnet/vi...
Is it possible to change BIOS using a C/C++ programm? For example I must
activate Hyperthreading in BIOS for all machine.

With sufficient work (which would probably entail reverse-engineering parts
of the BIOS), I'm sure it could be done, but the solution would almost
certainly be hardware and BIOS specific.
There's simply no standard for how
BIOS settings are stored or manipulated.

I assume that
some of the bigger vendors (IBM, DELL etc) have created interfaces/BIOS API for
their own computer systems, so you might want to ask your vendor.

どうしてもWindows上で作りたければ,カーネルモードで動作するようなデバイスドライバを作成する必要がある。

そこまで手を出せる猛者がいれば,下記のエントリなどを参考に作ってみて頂きたい。

ウィンドウをきっかけに Windows の内部の仕組みを探る (後半)システムコールからカーネルデバイスドライバまで
http://language-and-engineering.hatenablog.jp/entry/20081111/1226413103


その際,アセンブラでBIOSコールするための割り込み命令の書き方を知っておけば役に立つ。

BIOS interrupt call
http://en.wikipedia.org/wiki/BIOS_int...
mov ah, 0x0e
mov al, '!'
int 0x10



Hello Worldコレクション
21 コンピューティングの基礎を学びたい方へ - BIOSコールのアセンブラ編
http://journal.mycom.co.jp/column/hel...
DOS上の*.com形式の実行ファイルの中からBIOSコールを行うようなアセンブラプログラムを作成

BIOSコールのためのソフトウェア割り込みには、複数の種類がありますが、画面出力関係には「int $0x10」を使います。alレジスタには出力文字を、ahにはBIOSコール番号を代入します

Linuxでコーディングする方法を調査しても,結局は,カーネルモジュールの作成方法に行き着く。

Windowsの場合とあまり事情は変わらない。

The Linux Kernel Module Programming Guide
http://tldp.org/LDP/lkmpg/2.6/html/lk...
2.1. Hello, World (part 1): The Simplest Module


カーネルモジュールプログラミング超入門 #1(仮)
http://www.slideshare.net/tsukubalinu...


Linux でアセンブリプログラミング
http://www.mztn.org/lxasm/asm00.html

  • linuxでNASMでアセンブラ


BIOS設定を変更するような,サードパーティ製のソフトウェアも確かに存在する。

だが,設定変更操作は動作保証外だったりする。

DellのサーバマシンのBIOS設定をLinux上から行う方法
http://jfut.integ.jp/2009/11/12/dell-...

Dellのサーバマシンに用意されているBIOSの設定項目すべてを設定できるのだと思います。ただし、BIOSの設定を変える activateCmosToken というコマンドは Unsupported Binaries に分類されています。従って、この記事を読んで設定したらBIOSが壊れてマシンが起動しなくなることもあるかもしれません。そうなっても自分で責任とれる方以外は絶対に行わない方が良いでしょう。

設定を行うのはactivateCmosToken コマンドを使います。基本構文は次のとおりです。

# /usr/sbin/activateCmosToken 0x"Token Value"


カーネルに手を出したり,デバイスドライバに手を出したりするのが

どれほど手間を食う作業になるかについては,知る人ぞ知る。

ブルースクリーンが日常茶飯事で,OS再起動不可もよくある世界,との事だ。

実現コストとリスクが大きすぎる。


(4)結論

そういうわけで,「自作プログラムによるBIOS設定の書き換え」を実現するために必要な技術要素と,その仕組み・動作原理はわかった。

カーネルモードでの操作になるので,プログラム作成には極めて大きなコストとリスクが伴う,という事も。


それだけわかっていれば,ソフトウェア開発者としては十分だろう。

万が一にも,ソフトウェアの機能仕様として「BIOS書き換え」を提案された時,即答で何と答えればよいか,わかった事になるのだから・・・。