SEV ゲスト API 完全ドキュメント
1. 概要
SEV API は、ゲストまたはハイパーバイザが SEV 仮想マシンの特定の側面を取得 または設定するために使う ioctl の集合である。ioctl は次のクラスに分かれる。
ハイパーバイザ ioctl: SEV ファームウェア全体に影響するグローバル属性を 問い合わせて設定する。これらの ioctl はプラットフォームのプロビジョニング ツールが使う。
ゲスト ioctl: SEV 仮想マシンの属性を問い合わせて設定する。
2. API の説明
この節では、SEV ファームウェアから SEV ゲストレポートを問い合わせるために 使う ioctl を説明する。各 ioctl について、説明とあわせて次の情報を示す。
- Technology:
この ioctl を提供する SEV 技術。SEV、SEV-ES、SEV-SNP のいずれか、 またはすべて。
- Type:
ハイパーバイザまたはゲスト。この ioctl はゲスト内でもハイパーバイザ内 でも使える。
- Parameters:
ioctl が受け付けるパラメータ。
- Returns:
返される値。一般的なエラー番号(-ENOMEM、-EINVAL)は詳述しないが、 固有の意味を持つエラーは記す。
ゲスト ioctl は /dev/sev-guest デバイスのファイルディスクリプタに対して 発行する。ioctl は struct snp_guest_request_ioctl を受け取る。入力構造体と 出力構造体は、それぞれ req_data フィールドと resp_data フィールドで指定する。 ファームウェアエラーで ioctl の実行に失敗した場合は fw_error コードが設定 され、それ以外の場合は fw_error に -1 が設定される。
ファームウェアは、メッセージシーケンスカウンタがゲストのメッセージ シーケンスカウンタより 1 大きいことを確認する。ゲストドライバがメッセージ カウンタをインクリメントできない場合(例: カウンタのオーバーフロー)、 -EIO が返される。
struct snp_guest_request_ioctl {
/* Message version number */
__u32 msg_version;
/* Request and response structure address */
__u64 req_data;
__u64 resp_data;
/* bits[63:32]: VMM error code, bits[31:0] firmware error code (see psp-sev.h) */
union {
__u64 exitinfo2;
struct {
__u32 fw_error;
__u32 vmm_error;
};
};
};
ホスト ioctl は /dev/sev デバイスのファイルディスクリプタに対して発行する。 ioctl は以下に示すコマンド ID と入力構造体を受け取る。
struct sev_issue_cmd {
/* Command ID */
__u32 cmd;
/* Command request structure */
__u64 data;
/* Firmware error code on failure (see psp-sev.h) */
__u32 error;
};
2.1 SNP_GET_REPORT
- Technology:
sev-snp
- Type:
ゲスト ioctl
- Parameters (in):
struct snp_report_req
- Returns (out):
成功時は struct snp_report_resp、エラー時は負の値
SNP_GET_REPORT ioctl は、SEV-SNP ファームウェアからアテステーションレポート を問い合わせるために使える。この ioctl は、SEV-SNP ファームウェアが提供する SNP_GUEST_REQUEST(MSG_REPORT_REQ)コマンドを使ってアテステーションレポート を問い合わせる。
成功すると、snp_report_resp.data にレポートが入る。レポートの形式は SEV-SNP 仕様に記述されている。詳細は SEV-SNP 仕様を参照。
2.2 SNP_GET_DERIVED_KEY
- Technology:
sev-snp
- Type:
ゲスト ioctl
- Parameters (in):
struct snp_derived_key_req
- Returns (out):
成功時は struct snp_derived_key_resp、エラー時は負の値
SNP_GET_DERIVED_KEY ioctl は、ルート鍵から派生した鍵を取得するために使える。 派生鍵は、鍵のシーリングや外部との通信など、ゲストが任意の目的で使える。
この ioctl は、SEV-SNP ファームウェアが提供する SNP_GUEST_REQUEST (MSG_KEY_REQ)コマンドを使って鍵を派生する。鍵派生要求で渡す各種フィールド の詳細は SEV-SNP 仕様を参照。
成功すると、snp_derived_key_resp.data に派生鍵の値が入る。詳細は SEV-SNP 仕様を参照。
2.3 SNP_GET_EXT_REPORT
- Technology:
sev-snp
- Type:
ゲスト ioctl
- Parameters (in/out):
struct snp_ext_report_req
- Returns (out):
成功時は struct snp_report_resp、エラー時は負の値
SNP_GET_EXT_REPORT ioctl は SNP_GET_REPORT に似ている。違いは、レポートと 一緒に返される追加の証明書データにある。返される証明書データは、ハイパー バイザが SNP_SET_EXT_CONFIG を通じて提供する。
この ioctl は、SEV-SNP ファームウェアが提供する SNP_GUEST_REQUEST (MSG_REPORT_REQ)コマンドを使ってアテステーションレポートを取得する。
成功すると、snp_ext_report_resp.data にアテステーションレポートが入り、 snp_ext_report_req.certs_address に証明書ブロブが入る。ブロブの長さが想定 より小さい場合は、snp_ext_report_req.certs_len が想定値で更新される。
証明書ブロブの解析方法の詳細は GHCB 仕様を参照。
2.4 SNP_PLATFORM_STATUS
- Technology:
sev-snp
- Type:
ハイパーバイザ ioctl コマンド
- Parameters (out):
struct sev_user_data_snp_status
- Returns (out):
成功時は 0、エラー時は負の値
SNP_PLATFORM_STATUS コマンドは、SNP プラットフォームのステータスを問い合わせ るために使う。ステータスには API のメジャーバージョン、マイナーバージョン などが含まれる。詳細は SEV-SNP 仕様を参照。
2.5 SNP_COMMIT
- Technology:
sev-snp
- Type:
ハイパーバイザ ioctl コマンド
- Returns (out):
成功時は 0、エラー時は負の値
SNP_COMMIT は、SEV-SNP ファームウェアの SNP_COMMIT コマンドを使って、現在 インストールされているファームウェアをコミットするために使う。これにより、 以前にコミットしたファームウェアバージョンへのロールバックを防ぐ。また、 報告される TCB を現在インストールされているファームウェアのものに合わせて 更新する。
2.6 SNP_SET_CONFIG
- Technology:
sev-snp
- Type:
ハイパーバイザ ioctl コマンド
- Parameters (in):
struct sev_user_data_snp_config
- Returns (out):
成功時は 0、エラー時は負の値
SNP_SET_CONFIG は、アテステーションレポートで報告される TCB バージョンなど、 システム全体の設定を行うために使う。このコマンドは SEV-SNP 仕様で定義されて いる SNP_CONFIG コマンドに似ている。このコマンドの影響を受けるファームウェア パラメータの現在値は、SNP_PLATFORM_STATUS で問い合わせられる。
2.7 SNP_VLEK_LOAD
- Technology:
sev-snp
- Type:
ハイパーバイザ ioctl コマンド
- Parameters (in):
struct sev_user_data_snp_vlek_load
- Returns (out):
成功時は 0、エラー時は負の値
アテステーションレポートを要求するとき、ゲストは SNP ファームウェアにどちら の鍵でレポートに署名させるかを指定できる。チップ固有の秘密から派生する Versioned Chip Endorsement Key(VCEK)か、AMD Key Derivation Service(KDS) から取得し、登録済みクラウドサービスプロバイダに割り当てられたシードから 派生する Versioned Loaded Endorsement Key(VLEK)である。
VLEK 鍵の場合は、KDS から取得した後にシステムへロードするために SNP_VLEK_LOAD SNP コマンドを使う。これは SEV-SNP 仕様で規定されている SNP_VLEK_LOAD ファームウェアコマンドにほぼ対応する。
3. SEV-SNP の CPUID 強制
SEV-SNP ゲストは、SNP_LAUNCH_UPDATE ファームウェアコマンドの一部として PSP が 検証した CPUID 値の表を含む特別なページにアクセスできる。このページは、 CPUID 値の正当性について次のことを保証する。
そのアドレスはブートローダ/ファームウェア(CC blob 経由)から取得され、 それらのバイナリは SEV-SNP アテステーションレポートの一部として測定 される。
その初期状態は暗号化済みかつ pvalidate 済みになる。そのため実行中に変更 しようとすると、不正なデータが書き込まれるか、ハイパーバイザがバッキング ページをスワップしようとすると検証状態の変化により #VC 例外が発生する。
ハイパーバイザが通常のページや CPUID 用でない暗号化ページを使って PSP の 検査を回避しようとすると、SEV-SNP アテステーションレポートが提供する測定値 が変わる。
CPUID ページの内容は測定され*ない*が、ゲストの初期化の一環として CPUID ページの想定内容を変更しようとすると、SNP_LAUNCH_UPDATE 中にそのページに 対して行われる PSP の CPUID 強制ポリシー検査で阻まれ、ゲスト所有者が CPUID 値の独自チェックを実装していれば後から気付ける。
最後の保証は、カーネルがブートのすべての段階で SEV-SNP CPUID を使うよう配慮 している場合にのみ役立つことに注意することが重要である。そうでなければ、 ゲスト所有者のアテステーションは、ブート中のどこかの時点でカーネルに誤った 値が渡されなかったことを保証しない。
4. SEV ゲストドライバの通信用鍵
SEV ゲストと AMD Secure Processor(ASP、別名 PSP)内の SEV ファームウェアとの 通信は、VM Platform Communication Key(VMPCK)で保護される。既定では、 sev-guest ドライバはゲストが動作している VM Privilege Level(VMPL)に対応 する VMPCK を使う。この鍵が sev-guest ドライバによって消去された場合 (VMPCK が消去される理由はドライバを参照)、sev-guest ドライバを再ロードし、 vmpck_id モジュールパラメータで目的の鍵を指定すれば、別の鍵を使える。
参考文献
SEV-SNP および GHCB 仕様: developer.amd.com/sev
このドライバは SEV-SNP ファームウェア仕様 0.9 と GHCB 仕様バージョン 2.0 に 基づく。