panda's tech note

Advent Calendar 2018: advos

Day 6: ブートローダ(電源OFF)

今日はBIOSの電源管理機能(APM)で電源を切る機能を実装します。

PICの無効化

4日目に説明した通り,割り込みはIntelではIntel 8259(A)というProgrammable Interrupt Controller (PIC)によって処理され,CPUに割り込みリクエストを送ります。8259(A)は最大で8本の割り込み線を持ちます。x86/x86-64のチップセットではこれをカスケード接続して,Interrupt Request (IRQ) 0-15までを処理しています。1つ目のPICを master,2つ目を slave と呼びます(昔に名付けられているので現代ではpolitically incorrectな名前ですね)。Master PICのIRQ 2がSlaveにつながっています。

昔はIRQの割り当てはBIOSで設定できましたが,最近のBIOSだと項目がないかもしれません。理由はマルチコア・マルチプロセッサが当たり前となり,I/O APICと呼ばれるチップにその役割が取って代わられ,さらにPCIもMessage Signaled Interrupts (MSI)と呼ばれるメッセージベースのIRQではない割り込みになったため,IRQという言葉を聞くことは少なくなりました。標準の(典型的な)IRQ設定は以下の通りですので,これを前提に進めます。

IRQ Description
0 System timer (PIC)
1 Keyboard
2 Cascade
3 Serial (COM2)
4 Serial (COM1)
5 Sound card
6 Floppy disk
7 Parallel port (1)
8 Real-time clock
9 Peripheral
10 Peripheral
11 Peripheral
12 PS/2 mouse
13 x87 floating point coprocessor
14 Primary IDE
15 Secondary IDE

Master PICのコマンド,データI/Oポートはそれぞれ,20h21hに接続されています。また,slave PICのコマンド,データI/OポートはそれぞれA0hA1hに接続されています。PICのコマンド,データI/Oポートの仕様はIntel 8259A PROGRAMMABLE INTERRUPT CONTROLLERを参照してください。

以下のコードは,PICをすべて無効にするルーチンです。それぞれのPICのデータI/Oポートに0xffを書き込むことでPICを無効にできます。ただし,PICはカスケードされているので,slave,masterの順でPICを無効化していきます。今回は電源を落とす前に割り込みを無効化するためにこの処理を行っていますが,後日説明するAPIC,I/O APICを使う場合も,PICを無効化します。

disable_pic:
    pushw	%ax
    movb	$0xff,%al
    outb	%al,$0xa1
    movb	$0xff,%al
    outb	%al,$0x21
    popw	%ax
    ret

APMによる電源OFF

以下のようにINT 15hのシステムサービスを使って電源を落とします。

    /* Disable PIC */
    call	disable_pic

    /* Power off with APM */
    movw	$0x5301,%ax	/* Connect APM interface */
    movw	$0x0,%bx	/* Specify system BIOS */
    int	$0x15		/* Return error code in %ah with CF */
    jc	1f		/* Error */

    movw	$0x530e,%ax	/* Set APM version */
    movw	$0x0,%bx	/* Specify system BIOS */
    movw	$0x102,%cx	/* Version 1.2 */
    int	$0x15		/* Return error code in %ah with CF */
    jc	1f		/* Error */

    movw	$0x5308,%ax	/* Enable power management */
    movw	$0x1,%bx	/* All devices managed by the system BIOS */
    movw	$0x1,%cx	/* Enable */
    int	$0x15		/* Ignore errors */

    movw	$0x5307,%ax	/* Set power state */
    movw	$0x1,%bx	/* All devices managed by the system BIOS */
    movw	$0x3,%cx	/* Off */
    int	$0x15
1:
    ret			/* Return on error */

まず最初にPICを無効化します。

    movw	$0x5301,%ax	/* Connect APM interface */
    movw	$0x0,%bx	/* Specify system BIOS */
    int	$0x15		/* Return error code in %ah with CF */
    jc	1f		/* Error */

次に,INT 15h, AX=5301hでAPMインターフェイスに接続します。詳細はこのページの後半のBIOSサービス呼び出しを参照してください。

    movw	$0x530e,%ax	/* Set APM version */
    movw	$0x0,%bx	/* Specify system BIOS */
    movw	$0x102,%cx	/* Version 1.2 */
    int	$0x15		/* Return error code in %ah with CF */
    jc	1f		/* Error */

INT 15h, AX=530EhでAPMのバージョンを1.2に設定します。

    movw	$0x5308,%ax	/* Enable power management */
    movw	$0x1,%bx	/* All devices managed by the system BIOS */
    movw	$0x1,%cx	/* Enable */
    int	$0x15		/* Ignore errors */

次にINT 15h, AX=5308hでAPMによる電源管理機能を有効にします。これでAPMでの電源管理ができるようになるので,あとは電源を切るだけです。

    movw	$0x5307,%ax	/* Set power state */
    movw	$0x1,%bx	/* All devices managed by the system BIOS */
    movw	$0x3,%cx	/* Off */
    int	$0x15

最後にINT 15h, AX=5307hでPower StateをOff(CX=3)にします。これで電源が切れるはずですが,APMが無効に成っていると電源が切れませんので注意してください。

ただ,リアルモードでは上記のようにAPMを使って電源を管理しますが,32/64ビットモードでカーネルを動かすようになったらAPMは使わず,ACPIで電源を落とすようにするので,この機能は実装しなくても問題無いと思います。

Appendix: BIOSサービスの呼び出し

電源OFFで使ったINT 15hサービスをまとめます。詳細はEmbedded BIOSTM 4.1を参照してください。

INT 15h: General System Services

ここでは,INT 15hのうちAPM関連のサービスをまとめます。

INT 15h, AX=5300h: APM Installation Check
  • 入力パラメータ
    • BX: 0000h (System BIOS)
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: 成功の場合メジャーバージョン番号(BCD),失敗の場合86h
    • AL: マイナーバージョン番号(BCD)
    • BH: 'P'
    • BL: 'M'
    • CX: Capabilityフラグ
      • bit 0: 16-bit protected mode interfaceがサポートされている場合セット
      • bit 1: 32-bit protected mode interfaceがサポートされている場合セット
      • bit 2: CPU Idle呼び出しがクロックスピードを提示する場合セット
      • bit 3: BIOS Power Managementが無効の場合セット
INT 15h, AX=5301h: APM Interface Connect
  • 入力パラメータ
    • BX: 0000h (System BIOS)
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード
      • 02h: Interface Connectionが既に有効になっている
      • 09h: 認識できないデバイスID
      • 86h: APMがサポートされていない
INT 15h, AX=5302h: APM Protected Mode 16-Bit Interface Connect
  • 入力パラメータ
    • BX: 0000h (System BIOS)
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード
      • 02h: Interface Connectionが既に有効になっている
      • 05h: 16-bit protected mode interfaceが既に確立済み
      • 06h: 16-bit protected mode interfaceが未対応
      • 09h: 認識できないデバイスID
      • 86h: APMがサポートされていない
    • AX:BX: 成功の場合,APMリクエストをサポートする16:16 protected modeのエントリーポイント
    • CX: 成功の場合,APMのエントリポイントで使われる16-bitデータセレクタ
INT 15h, AX=5303h: APM Protected Mode 32-Bit Interface Connect
  • 入力パラメータ
    • BX: 0000h (System BIOS)
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード
      • 02h: Interface Connectionが既に有効になっている
      • 07h: 32-bit protected mode interfaceが既に確立済み
      • 08h: 32-bit protected mode interfaceが未対応
      • 09h: 認識できないデバイスID
      • 86h: APMがサポートされていない
    • AX:EBX: 成功の場合,APMリクエストをサポートする16:32 protected modeのエントリーポイント
    • CX: 成功の場合,APMのエントリポイントで使われる16-bitコードセグメントセレクタ
    • DX: 成功の場合,APMのエントリポイントで使われる16-bitデータセレクタ
INT 15h, AX=5304h: APM Interface Disconnect
  • 入力パラメータ
    • BX: 0000h (System BIOS)
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード
      • 03h: Interfaceが未接続
      • 09h: 認識できないデバイスID
      • 86h: APMがサポートされていない
INT 15h, AX=5305h: APM CPU Idle
  • 入力パラメータ
    • BX: 0000h (System BIOS)
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード
      • 86h: APMがサポートされていない
INT 15h, AX=5306h: APM CPU Busy
  • 入力パラメータ
    • BX: 0000h (System BIOS)
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード
      • 86h: APMがサポートされていない
INT 15h, AX=5307h: APM Set Power State
  • 入力パラメータ
    • BX: 0001h (System BIOS)
    • CX: System State ID
      • 0000h: Ready
      • 0001h: Standby
      • 0002h: Suspend
      • 0003h: Off
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード
      • 01h: Power management機能が無効
      • 09h: 認識できないデバイスID
      • 0Ah: CXが範囲外
      • 60h: リクエストした状態に遷移できない
      • 86h: APMがサポートされていない
INT 15h, AX=5308h: APM Enable/Disable APM Functionality
  • 入力パラメータ
    • BX: FFFFh (enable/disable all power management)
    • CX: 0=無効, 1=有効
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード
      • 01h: Power management機能が無効
      • 09h: 認識できないデバイスID
      • 0Ah: CXが範囲外
      • 86h: APMがサポートされていない
INT 15h, AX=5309h: APM Restore APM Power-On Defaults
  • 入力パラメータ
    • BX: FFFFh (all power management)
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード
      • 09h: 認識できないデバイスID
      • 86h: APMがサポートされていない
INT 15h, AX=530Ah: APM Get Power Status
  • 入力パラメータ
    • BX: 0001h
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード
      • 09h: 認識できないデバイスID
      • 86h: APMがサポートされていない
    • BH: 成功の場合,A/Cラインステータス
      • 00h: オフライン
      • 01h: オンライン
      • FFh: 不明
    • BL: 成功の場合,バッテリーステータス
      • 00h: High
      • 01h: Low
      • 02h: Critical
      • FFh: 不明
    • CL: 成功の場合,残存バッテリーライフ
      • 0-100: パーセンテージ
      • FFh: 不明
INT 15h, AX=530Bh: APM Get APM Event
  • 入力パラメータ
    • なし
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード
      • 03h: Interfaceが未接続
      • 86h: APMがサポートされていない
    • BX: 成功の場合,PMイベントコード
      • 01h: System standby request notification
      • 02h: System suspend request notification
      • 03h: Normal resume system notification
      • 04h: Critical resume system notification
      • 05h: Battery low notification
INT 15h, AX=530Eh: APM Set APM Version
  • 入力パラメータ
    • BX: 0000h
    • CH: メジャーバージョン
    • CL: マイナーバージョン
  • 返り値パラメータ
    • CF: 成功ならクリア,失敗ならセット
    • AH: エラーコード

まとめと明日の予定

今日はAPMでPCの電源を落としました。明日はいよいよ16ビットの世界を離れて32/64ビットの世界に入ろうと思います。