view driver/lspe.c @ 0:42f240cc4bc6

From: 太田 篤志 <atoc@namikilab.tuat.ac.jp>
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Tue, 08 Sep 2009 13:44:18 +0900
parents
children
line wrap: on
line source

/**
 * lspe.c
 * SPE プロセスマネージャ spe_manager
 * 論理 SPE に関する処理
 */

#include <linux/module.h>      // カーネルモジュール全般
#include <linux/kernel.h>      // printk
#include <linux/slab.h>        // kmalloc, kfree
#include <linux/interrupt.h>   // request_irq, free_irq
#include <asm/ps3.h>           // ps3_irq_plug_setup, ps3_irq_plug_destroy
#include <asm/io.h>            // ioremap, iounmap

#include <asm/pgtable-ppc64.h> // 暫定

#include "../include/hvcalls/common.h"
#include "../include/hvcalls/spe.h"
#include "../include/hvcalls/repository.h"
#include "lspe.h"
#include "interrupt.h"
#include "main.h"


// 割り込みマスク値定数
const HVC_u64 spe_int_mask[SPE_VIRQ_CLASS_MAX+1] = {
	(HVC_u64)0x07,   // SPE クラス 0 割り込み (すべての要因をイネーブル)
	(HVC_u64)0x0F,   // SPE クラス 1 割り込み (すべての要因をイネーブル)
	(HVC_u64)0x0F    // SPE クラス 2 割り込み (INT_Mask_class2[B] はイネーブルにするとハング)
};


// 論理 SPE 用変数
static int         lspe_count = 0;
static lspe_data_t lspe_data[SPE_COUNT_MAX];


// 内部で利用する関数
static int initialize_logical_spe(const HVC_u64, const HVC_u64, const int, const HVC_u64, irq_handler_t);
static void finalize_logical_spe(const int);



//============================================================================================ initialize_logical_spes()
/**
 * initialize_logical_spes
 * 論理 SPE の確保と初期化
 *
 * @param  HVC_u64  lpar_id  論理パーティション識別子
 * @param  HVC_u64  vas_id   仮想アドレス識別子
 * @return int
 */
int initialize_logical_spes(const HVC_u64 lpar_id, const HVC_u64 vas_id)
{
	for (
		lspe_count = 0;
		(SPE_COUNT_REQ && lspe_count < SPE_COUNT_REQ && lspe_count < SPE_COUNT_MAX) || (!SPE_COUNT_REQ && lspe_count < SPE_COUNT_MAX);
		lspe_count++
	)
	{
		int ret = initialize_logical_spe(lpar_id, vas_id, lspe_count, (HVC_u64)lspe_count, interrupt_handler);
		if (ret)
		{
			printk (
				KERN_NOTICE "[%s] Notice: spe%d.initialize_logical_spe() returned %d. (%s:%u)\n",
				SPE_MANAGER_MODULE_NAME, lspe_count, ret, __FILE__, __LINE__
			);
			break;
		}
	}
	return lspe_count;
}


//============================================================================================= initialize_logical_spe()
/**
 * initialize_logical_spe
 * 論理 SPE の確保と初期化
 *
 * @param  HVC_u64        lpar_id          論理パーティション識別子
 * @param  HVC_u64        vas_id           仮想アドレス空間識別子
 * @param  int            n                SPE 番号 (0..x)
 * @param  HVC_u64        spe_reserve_key  論理 SPE 予約キー (0..5)
 * @param  irq_handler_t  irq_handler      論理 SPE 割り込みハンドラ
 * @return int
 * @static
 */
static int initialize_logical_spe (
	const HVC_u64       lpar_id,
	const HVC_u64       vas_id,
	const int           n,
	const HVC_u64       spe_reserve_key,
	      irq_handler_t irq_handler
)
{
	HVC_u64       TMP_spe_id, lpar_ls, lpar_problem, lpar_priv2, lpar_shadow;
	HVC_u64       value1, spe_rsv;
	HVC_u64       outlet[SPE_VIRQ_CLASS_MAX+1];
	unsigned int  TMP_virq[SPE_VIRQ_CLASS_MAX+1];
	char         *TMP_virq_dev_name[SPE_VIRQ_CLASS_MAX+1];
	int           i, j;


	if (n < 0 || n >= SPE_COUNT_MAX) return -1;

	// 論理 SPE を生成
	if (hvc_create_logical_spe(vas_id, &TMP_spe_id, &lpar_ls, &lpar_problem, &lpar_priv2, &lpar_shadow))
	{
		return -2;
	}

	// 論理 SPE 予約識別子を取得する
	if (hvc_get_repository_node (
		lpar_id,
		hvc_make_repository_first_key("bi", 0), hvc_make_repository_key("spursv", 0), spe_reserve_key, 0,
		&value1, &spe_rsv ))
	{
		hvc_destroy_logical_spe(TMP_spe_id);
		return -3;
	}

	// 論理 SPE を利用可能状態にする
	if (hvc_enable_logical_spe(TMP_spe_id, spe_rsv))
	{
		hvc_destroy_logical_spe(TMP_spe_id);
		return -4;
	}

	// MFC ステート・レジスタ 1 (MFC_SR1) を設定
//	lv1_set_spe_privilege_state_area_1_register(TMP_spe_id, 0UL, 0x33UL);   // S, R, T, D
//	lv1_set_spe_privilege_state_area_1_register(TMP_spe_id, 0UL, 0x7bUL);   // TL, S, R, PR, T, D

	for (i = 0; i <= SPE_VIRQ_CLASS_MAX; i++)
	{
		// SPE IRQ アウトレットを生成
		if (hvc_create_spe_irq_outlet(TMP_spe_id, (HVC_u64)i, &outlet[i]))
		{
			for (j = 0; j < i; j++)
			{
				free_irq(TMP_virq[j], NULL);
				kfree(TMP_virq_dev_name[j]);
				ps3_irq_plug_destroy(TMP_virq[j]);
				// Note: SPE IRQ アウトレットは解放できない?
				//       (解放するためのハイパーバイザコールがない→論理 SPE を解放すればおk?)
			}
			hvc_disable_logical_spe(TMP_spe_id);
			hvc_destroy_logical_spe(TMP_spe_id);
			return -5;
		}

		// 仮想割り込み番号を生成 (カーネルの手助けを要する)
		if (ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, outlet[i], &TMP_virq[i]))
		{
			for (j = 0; j < i; j++)
			{
				free_irq(TMP_virq[j], NULL);
				kfree(TMP_virq_dev_name[j]);
				ps3_irq_plug_destroy(TMP_virq[j]);
				// Note: SPE IRQ アウトレットは解放できない?
			}
			hvc_disable_logical_spe(TMP_spe_id);
			hvc_destroy_logical_spe(TMP_spe_id);
			return -6;
		}

		// 割り込みハンドラの dev_name の準備
		if ((TMP_virq_dev_name[i] = (char *)kmalloc(sizeof(char) * 32, GFP_KERNEL)) == NULL)
		{
			printk (
				KERN_NOTICE "[%s] Notice: spe%d.kmalloc() returned NULL. (%s:%u)\n",
				SPE_MANAGER_MODULE_NAME, n, __FILE__, __LINE__
			);
		}
		else
		{
			sprintf(TMP_virq_dev_name[i], SPE_MANAGER_DEVICE_NAME " (spe%d_class%d)", n, i);
		}

		// 割り込みハンドラを登録
		if (request_irq(TMP_virq[i], irq_handler, IRQF_DISABLED, TMP_virq_dev_name[i], NULL))
		{
			for (j = 0; j < i; j++)
			{
				free_irq(TMP_virq[j], NULL);
				kfree(TMP_virq_dev_name[j]);
				ps3_irq_plug_destroy(TMP_virq[j]);
				// Note: SPE IRQ アウトレットは解放できない?
			}
			kfree(TMP_virq_dev_name[i]);
			ps3_irq_plug_destroy(TMP_virq[i]);
			// Note: SPE IRQ アウトレットは解放できない?
			hvc_disable_logical_spe(TMP_spe_id);
			hvc_destroy_logical_spe(TMP_spe_id);
			return -7;
		}

		// SPE の割り込みマスクを設定
		hvc_set_spe_int_mask(TMP_spe_id, (HVC_u64)i, spe_int_mask[i]);
	}


	// lspe_data の各フィールドに格納
	lspe_data[n].spe_id = TMP_spe_id;
	for (i = 0; i <= SPE_VIRQ_CLASS_MAX; i++)
	{
		lspe_data[n].virq[i]          = TMP_virq[i];
		lspe_data[n].virq_dev_name[i] = TMP_virq_dev_name[i];
	}
	lspe_data[n].ls      = ioremap_flags(lpar_ls, lpar_problem - lpar_ls, _PAGE_NO_CACHE);
	lspe_data[n].problem = ioremap(lpar_problem, lpar_priv2 - lpar_problem);
	lspe_data[n].priv2   = ioremap(lpar_priv2, SPE_PRIV2_AREA_SIZE);
	{
		// Note: シャドウレジスタエリアはこのようにマップしないとレジスタの読み込み時に OS ごと落ちる
		unsigned long shadow_flags = _PAGE_NO_CACHE | 3;
		lspe_data[n].shadow = __ioremap(lpar_shadow, SPE_SHADOW_AREA_SIZE, shadow_flags);
	}

	// LS エリアを 0 クリア
	memset(lspe_data[n].ls, 0, SPE_LS_AREA_SIZE);
	asm volatile("eieio");


	return 0;
}


//===================================================================================================== get_lspe_count()
/**
 * get_lspe_count
 * 確保・初期化された論理 SPE の数を取得する
 *
 * @return int  論理 SPE 数
 */
int get_lspe_count(void)
{
	return lspe_count;
}


//====================================================================================================== get_lspe_data()
/**
 * get_lspe_data
 * 確保・初期化された論理 SPE データを取得する
 *
 * Note: 引数 n に負数や確保した論理 SPE 以上のインデックスを指定した場合は NULL が返る。
 *
 * @param  int  n         SPE 番号 (0..x)
 * @return lspe_data_t *  論理 SPE データへのポインタ
 */
lspe_data_t *get_lspe_data(const int n)
{
	return (n >= 0 && n < lspe_count) ? lspe_data + n : NULL;
}


//============================================================================================== finalize_logical_spes()
/**
 * finalize_logical_spes
 * 論理 SPE の解放
 *
 * @return void
 */
void finalize_logical_spes(void)
{
	for (--lspe_count; lspe_count >= 0; lspe_count--)
	{
		finalize_logical_spe(lspe_count);
	}
}


//=============================================================================================== finalize_logical_spe()
/**
 * finalize_logical_spe
 * 論理 SPE の解放
 *
 * @param  int  n  SPE 番号 (0..x)
 * @return void
 * @static
 */
static void finalize_logical_spe(const int n)
{
	int i;


	iounmap(lspe_data[n].ls);
	iounmap(lspe_data[n].problem);
	iounmap(lspe_data[n].priv2);

	for (i = 0; i <= SPE_VIRQ_CLASS_MAX; i++)
	{
		free_irq(lspe_data[n].virq[i], NULL);
		kfree(lspe_data[n].virq_dev_name[i]);
		ps3_irq_plug_destroy(lspe_data[n].virq[i]);
		// Note: SPE IRQ アウトレットは解放できない?
	}

	hvc_disable_logical_spe(lspe_data[n].spe_id);
	hvc_destroy_logical_spe(lspe_data[n].spe_id);
}