view driver/main.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

/**
 * main.c
 * SPE プロセスマネージャ spe_manager
 * spe_manager_init, spe_manager_exit
 */

#include <linux/module.h>      // カーネルモジュール全般
#include <linux/init.h>        // __init, __exit, module_init, module_exit
#include <linux/kernel.h>      // printk
#include <linux/fs.h>          // register_chrdev_region, unregister_chrdev_region
#include <linux/cdev.h>        // cdev_init, cdev_add, cdev_del
#include <linux/proc_fs.h>     // create_proc_entry, remove_proc_entry

#include <asm/io.h>            // 暫定
#include <asm/mmu-hash64.h>    // 暫定

#include "../include/hvcalls/common.h"
#include "../include/hvcalls/ppe.h"
#include "../include/kernel.h"        // Note: SPE 向け軽量カーネルの make を必ず先に行うこと!!
#include "main.h"
#include "lspe.h"
#include "cdev_handler.h"
#include "procfs/hypervisor.h"
#include "procfs/processes.h"
#include "critical.h"
#include "process_list.h"

#include "../include/spe_process.h"   // 暫定

MODULE_LICENSE("GPL");   // これがないと動かない… (^ω^#)ビキビキ


// キャラクタデバイス用変数
static struct cdev            spe_manager_cdev;
static struct file_operations spe_manager_cdev_fopts = {
	owner:   THIS_MODULE,
	open:    spe_manager_open,
	write:   spe_manager_write,
	ioctl:   spe_manager_ioctl,
	read:    spe_manager_read,
	release: spe_manager_release
};

// procfs 用変数
static struct proc_dir_entry *proc_base_dir, *proc_processes, *proc_hypervisor;


// 内部で利用する関数
static void transfer_spe_kernel(void);



// 後で別ファイルに移す?
void initialize_manager(void)
{
//	int i, lspe_count = get_lspe_count();



	initialize_critical();
//	initialize_spinlocks();
	initialize_process_lists();

	transfer_spe_kernel();


/*
	for (i = 0; i < lspe_count; i++)
	{
		lspe_data_t *lspe = get_lspe_data(i);

		*(unsigned int *)(lspe->problem + 0x4034) = SPE_KERNEL_INITIAL_PC | 0x1;
		asm volatile("eieio");
		*(unsigned int *)(lspe->problem + 0x401c) = 1;
	}
	asm volatile("eieio");   // 一応(^^;
*/
}






// 後で別ファイルに移す


//===================================================================================== set_spu_channel_data_and_count()
/**
 * set_spu_channel_data_and_count
 * SPU チャネル・データ・レジスタおよび SPU チャネル・カウント・レジスタを設定する
 *
 * @param  int            n      SPE 番号 (0..x)
 * @param  unsigned long  index  SPU チャネル・インデックス
 * @param  long           data   SPU チャネル・データの設定値 (負数の場合は設定しない)
 * @param  long           count  SPU チャネル・カウントの設定値 (負数の場合は設定しない)
 * @return void
 */
void set_spu_channel_data_and_count(const int n, const unsigned long index, const long data, const long count)
{
	lspe_data_t *lspe = get_lspe_data(n);

	// SPU チャネル・インデックス・レジスタ
	out_be64(lspe->priv2 + 0x4060, index);
	asm volatile("eieio");

	if (data >= 0)
	{
		// SPU チャネル・データ・レジスタ
		out_be64(lspe->priv2 + 0x4070, (unsigned long)data);
	}
	if (count >= 0)
	{
		// SPU チャネル・カウント・レジスタ
		out_be64(lspe->priv2 + 0x4068, (unsigned long)count);
	}
	asm volatile("eieio");
}



//================================================================================================== start_spe_process()
/**
 * start_spe_process
 * SPE プロセスを開始する
 *
 * @param  int                           spe_no        論理 SPE 番号 (0..x)
 * @param  spe_process_context_list_t *  this_process  開始するプロセスコンテキストへのポインタ
 * @return void
 */
void start_spe_process(const int spe_no, spe_process_context_list_t *const this_process)
{


	unsigned long vsid_context, vsid_program;
	struct task_struct *task;





	lspe_data_t *lspe = get_lspe_data(spe_no);




#if 0
	// MFC コマンド・キュー・オペレーションを中断する
	out_be64(lspe->priv2 + 0x03000 /*MFC_Cntl*/, (0UL<<4 /*[Sm]*/ || 1UL<<0 /*[Sc]*/));
	asm volatile ("eieio");
	do
	{
		unsigned long in_cntl = in_be64(lspe->priv2 + 0x03000 /*MFC_Cntl*/);
		if ((in_cntl & 3UL<<8 /*[Ss]*/) == 3UL<<8 /*11*/) break;
	}
	while(1);
#endif

	// MFC マルチソース同期レジスタをチェックする
	out_be32(lspe->problem + 0x00000 /*MFC_MSSync*/, 1);
	do
	{
		unsigned int in_mssync = in_be32(lspe->problem + 0x00000 /*MFC_MSSync*/);
		if ((in_mssync & 1<<0 /*[S]*/) == 0) break;
	}
	while(1);

#if 0
	// MFC コマンドをパージ (除去) する
	out_be64(lspe->priv2 + 0x03000 /*MFC_Cntl*/, (1UL<<15 /*[Pc]*/ || 1UL<<4 /*[Sm]*/));
	asm volatile ("eieio");
	do
	{
		unsigned long in_cntl = in_be64(lspe->priv2 + 0x03000 /*MFC_Cntl*/);
		if ((in_cntl & 3UL<<24 /*[Ps]*/) == 3UL<<24 /*11*/) break;
	}
	while(1);
#endif

	// SPU 特権制御レジスタを初期化 (SPU_PrivCntl[Le,A,S] = 0)
	out_be64(lspe->priv2 + 0x04040 /*SPU_PrivCntl*/, 0UL);
	asm volatile ("eieio");

	// MFC ステート・レジスタ 1 (MFC_SR1) を初期化 (MFC_SR1[S,R,T,D] = 1)
	lv1_set_spe_privilege_state_area_1_register(lspe->spe_id, 0x0000UL /*MFC_SR1*/, 0x33UL);

	// SPE SLB エントリを全て無効化
	out_be64(lspe->priv2 + 0x01128 /*SLB_Invalidate_All*/, 0UL);

	// SPU チャネル・データ・レジスタおよびチャネル・カウント・レジスタの初期化
	set_spu_channel_data_and_count(spe_no,  0,  0,  0);
	set_spu_channel_data_and_count(spe_no,  1,  0, -1);
	set_spu_channel_data_and_count(spe_no,  3,  0,  0);
	set_spu_channel_data_and_count(spe_no,  4,  0,  0);
	set_spu_channel_data_and_count(spe_no, 21, -1, 16);
	set_spu_channel_data_and_count(spe_no, 23, -1,  1);
	set_spu_channel_data_and_count(spe_no, 24,  0,  0);
	set_spu_channel_data_and_count(spe_no, 25,  0,  0);
	set_spu_channel_data_and_count(spe_no, 27,  0,  0);
	set_spu_channel_data_and_count(spe_no, 28, -1,  1);
	set_spu_channel_data_and_count(spe_no, 29, -1,  0);
	set_spu_channel_data_and_count(spe_no, 30, -1,  1);

	// 特権 1 レジスタ MFC_TClass_ID の初期化
	lv1_set_spe_privilege_state_area_1_register(lspe->spe_id, 0x0820UL /*MFC_TClass_ID*/, 0x10000000UL);
	asm volatile ("eieio");

	// MFC コマンド・キュー・オペレーションを再開
	out_be64(lspe->priv2 + 0x03000 /*MFC_Cntl*/, (0UL<<4 /*[Sm]*/ || 0UL<<0 /*[Sc]*/));








//	this_process->context.read.ret = 0xDEADBEEFDEADBEEF;
//	asm volatile("sync");




/*
	unsigned long       vsid_process, vsid_program;//, vsid_data;
	uint64_t            esid_data;
	unsigned long       i;
	HVC_u64 ret;
*/



	// SPE プロセスコンテキストのある実効アドレスに対応する仮想セグメント ID (vsid) を取得
	vsid_context = get_kernel_vsid((unsigned long)(&(this_process->context)), 0);
//	vsid_context = get_kernel_vsid((unsigned long)(&(this_process->context)), sizeof(spe_process_context_t));
//	printk(KERN_INFO "[%s] ADDR(this_process->context) = 0x%016lx\n", SPE_MANAGER_MODULE_NAME, (unsigned long)(&(this_process->context)));
//	printk(KERN_INFO "[%s] vsid_context                = 0x%016lx\n", SPE_MANAGER_MODULE_NAME, vsid_context);

/*
	printk(KERN_INFO "%016lx %016lx\n",  *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x00), *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x08));
	printk(KERN_INFO "%016lx %016lx\n",  *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x10), *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x18));
	printk(KERN_INFO "%016lx %016lx\n",  *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x20), *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x28));
	printk(KERN_INFO "%016lx %016lx\n",  *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x30), *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x38));
	printk(KERN_INFO "%016lx %016lx\n",  *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x40), *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x48));
	printk(KERN_INFO "%016lx %016lx\n",  *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x50), *(unsigned long *)((unsigned long)(&(this_process->context)) + 0x58));
*/



	// 登録側プロセスにある SPE プログラムの実効アドレスに対応する仮想セグメント ID (vsid) を取得
	vsid_program = get_vsid(current->mm->context.id, this_process->context.write.pgm_start, 0);
//	printk(KERN_INFO "[%s] pid                         = %d\n",       SPE_MANAGER_MODULE_NAME, current->pid);
//	printk(KERN_INFO "[%s] context.write.pgm_start     = 0x%016lx\n", SPE_MANAGER_MODULE_NAME, this_process->context.write.pgm_start);
//	printk(KERN_INFO "[%s] vsid_program                = 0x%016lx\n", SPE_MANAGER_MODULE_NAME, vsid_program);
/*
	for_each_process(task)
	{
		if (task->pid == this_process->context.write.pid)
		{
			vsid_program = get_vsid(task->mm->context.id, (unsigned long)(this_process->context.write.pgm_start), 0);
//			vsid_program = get_vsid(task->mm->context.id, (unsigned long)(process->write.program_start), (size_t)(process->write.program_size));
			printk(KERN_INFO "[%s] pid                         = %d\n",       SPE_MANAGER_MODULE_NAME, task->pid);
			printk(KERN_INFO "[%s] current->pid                = %d\n",       SPE_MANAGER_MODULE_NAME, current->pid);
			printk(KERN_INFO "[%s] vsid_program                = 0x%016lx\n", SPE_MANAGER_MODULE_NAME, vsid_program);
			break;
		}
	}
*/



	// SPE SLB エントリを全て無効化
//	out_be64(lspe->priv2 + 0x01128 /*SLB_Invalidate_All*/, 0UL);

	// vsid_context を SLB エントリに登録
	out_be64(lspe->priv2 + 0x01108 /*SLB_Index*/, 0UL);
	out_be64(lspe->priv2 + 0x01118 /*SLB_VSID*/, vsid_context << 12 | 0x400 /*Kp*/ | 0x000 /*L|LP*/);
	out_be64(lspe->priv2 + 0x01110 /*SLB_ESID*/, (unsigned long)(&(this_process->context)) & 0xFFFFFFFFF0000000UL | 0x8000000UL /*V*/);
//	out_be64(lspe->priv2 + 0x01110 /*SLB_ESID*/, 0xFFFFFFFFF0000000UL | 0x8000000UL /*V*/);

	// vsid_program を SLB エントリに登録
	out_be64(lspe->priv2 + 0x01108 /*SLB_Index*/, 1UL);
	out_be64(lspe->priv2 + 0x01118 /*SLB_VSID*/, vsid_program << 12 | 0xc00 /*Ks|Kp*/ | 0x80 /*C*/ | 0x000 /*L|LP*/);
	out_be64(lspe->priv2 + 0x01110 /*SLB_ESID*/, this_process->context.write.pgm_start & 0xFFFFFFFFF0000000UL | 0x8000000UL /*V*/);
//	out_be64(lspe->priv2 + 0x01110 /*SLB_ESID*/, 0xFFFFFFFFE0000000UL | 0x8000000UL /*V*/);

	// Note: カーネル空間の vsid には Kp ビット、ユーザ空間の vsid には Ks, Kp, C ビットを付ける。
	//       (cf. arch/powerpc/platforms/cell/spu_base.c の __spu_trap_data_seg 関数)



	lspe->mm        = current->mm;
	lspe->slb_index = 2;


/*
	// vsid_data 登録
	esid_data = process->write.data_start;
	for (i = 2UL; i < 16UL; i++, esid_data += 0x10000000UL)
	{
		vsid_data = get_vsid(task->mm->context.id, (unsigned long)esid_data);
		if (vsid_data == vsid_program) continue;

		*(unsigned long *)(spe_priv2[spe_no] + 0x01108UL) = i;                                // SLB_Index
		*(unsigned long *)(spe_priv2[spe_no] + 0x01118UL) = vsid_data << 12;                  // SLB_VSID
		*(unsigned long *)(spe_priv2[spe_no] + 0x01110UL) = esid_data & 0xfffffffff0000000UL | 0x8000000UL;
	}
*/



	// SPE プロセスコンテキストのある実効アドレスは LS の所定の位置に格納
	out_be64(lspe->ls + SPE_KERNEL_PROC_CONTEXT_OFFSET, (unsigned long)(&(this_process->context)));
//	out_be32(lspe->ls + SPE_KERNEL_PROC_CONTEXT_OFFSET, (unsigned int)((unsigned long)(&(this_process->context)) & 0xFFFFFFFUL));

/*
	// 登録側プロセスにある SPE プログラムの実効アドレスの下位 28bit は LS の所定の位置に格納
	out_be32(lspe->ls + SPE_KERNEL_PROGRAM_OFFSET, (unsigned int)((unsigned long)(this_process->context.write.pgm_start) & 0xFFFFFFFUL));
*/

	asm volatile ("eieio");


	out_be32(lspe->problem + 0x0000 /*MFC_MSSync*/, 1);
	do
	{
		unsigned int r = in_be32(lspe->problem + 0x0000 /*MFC_MSSync*/);
		if (r == 0) break;
	}
	while(1);

	// SPE の実行を開始
	out_be32(lspe->problem + 0x4034 /*SPU_NPC*/, SPE_KERNEL_INITIAL_PC | 0x1);
	out_be32(lspe->problem + 0x401c /*SPU_RunCntl*/, 1);
}










//=================================================================================================== spe_manager_init()
/**
 * spe_manager_init
 * デバイスの初期化
 *
 * Note: 以下に示す外部関数参照がある。
 *         procfs_handler_processes_read (procfs_handler.c)
 *         hvc_get_lpar_id
 *         hvc_get_ppe_id
 *         hvc_get_vas_id
 *         initialize_logical_spes (lspe.c)
 *         initialize_manager
 *
 * @return int
 * @static
 */
static int __init spe_manager_init(void)
{
	HVC_u64 lpar_id, ppe_id, vas_id;
	int     spes;


	//-------------------- キャラクタデバイスの登録
	if (register_chrdev_region(SPE_MANAGER_DEVICE_ID, 1, SPE_MANAGER_DEVICE_NAME))
	{
		printk(KERN_ERR "[%s] Error: register_chrdev_region() (%s:%u)\n", SPE_MANAGER_MODULE_NAME, __FILE__, __LINE__);
		// デバイス番号の割り当てができない
		return -ENODEV;
	}
	cdev_init(&spe_manager_cdev, &spe_manager_cdev_fopts);
	spe_manager_cdev.owner = THIS_MODULE;
	if (cdev_add(&spe_manager_cdev, SPE_MANAGER_DEVICE_ID, 1))
	{
		printk(KERN_ERR "[%s] Error: cdev_add() (%s:%u)\n", SPE_MANAGER_MODULE_NAME, __FILE__, __LINE__);
		unregister_chrdev_region(SPE_MANAGER_DEVICE_ID, 1);
		// キャラクタデバイスを追加できない
		return -ENODEV;
	}


	//-------------------- procfs エントリの生成 (デバッグ用途)
	if (!(proc_base_dir = proc_mkdir(SPE_MANAGER_PROCFS_BASE_DIR_NAME, NULL)))
	{
		printk(KERN_ERR "[%s] Error: proc_mkdir() (%s:%u)\n", SPE_MANAGER_MODULE_NAME, __FILE__, __LINE__);
		cdev_del(&spe_manager_cdev);
		unregister_chrdev_region(SPE_MANAGER_DEVICE_ID, 1);
		// procfs ディレクトリを生成できない
		return -ENODEV;
	}

	if (!(proc_hypervisor = create_proc_entry(SPE_MANAGER_PROCFS_HYPERVISOR_ENTRY_NAME, 0444, proc_base_dir)))
	{
		printk(KERN_ERR "[%s] Error: create_proc_entry() (%s:%u)\n", SPE_MANAGER_MODULE_NAME, __FILE__, __LINE__);
		remove_proc_entry(SPE_MANAGER_PROCFS_BASE_DIR_NAME, NULL);
		cdev_del(&spe_manager_cdev);
		unregister_chrdev_region(SPE_MANAGER_DEVICE_ID, 1);
		// procfs エントリを生成できない
		return -ENODEV;
	}
	proc_hypervisor->owner      = THIS_MODULE;
	proc_hypervisor->read_proc  = procfs_hypervisor_read;
	proc_hypervisor->write_proc = NULL;

	if (!(proc_processes = create_proc_entry(SPE_MANAGER_PROCFS_PROCESSES_ENTRY_NAME, 0444, proc_base_dir)))
	{
		printk(KERN_ERR "[%s] Error: create_proc_entry() (%s:%u)\n", SPE_MANAGER_MODULE_NAME, __FILE__, __LINE__);
		remove_proc_entry(SPE_MANAGER_PROCFS_HYPERVISOR_ENTRY_NAME, proc_base_dir);
		remove_proc_entry(SPE_MANAGER_PROCFS_BASE_DIR_NAME, NULL);
		cdev_del(&spe_manager_cdev);
		unregister_chrdev_region(SPE_MANAGER_DEVICE_ID, 1);
		// procfs エントリを生成できない
		return -ENODEV;
	}
	proc_processes->owner      = THIS_MODULE;
	proc_processes->read_proc  = procfs_processes_read;
	proc_processes->write_proc = NULL;



	// 論理パーティション識別子, 論理 PPE 識別子, 仮想アドレス識別子 の取得
	hvc_get_lpar_id(&lpar_id);
	hvc_get_ppe_id(&ppe_id);
	if (hvc_get_vas_id(ppe_id, &vas_id))
	{
		printk(KERN_ERR "[%s] Error: hvc_get_vas_id() (%s:%u)\n", SPE_MANAGER_MODULE_NAME, __FILE__, __LINE__);
		remove_proc_entry(SPE_MANAGER_PROCFS_HYPERVISOR_ENTRY_NAME, proc_base_dir);
		remove_proc_entry(SPE_MANAGER_PROCFS_PROCESSES_ENTRY_NAME, proc_base_dir);
		remove_proc_entry(SPE_MANAGER_PROCFS_BASE_DIR_NAME, NULL);
		cdev_del(&spe_manager_cdev);
		unregister_chrdev_region(SPE_MANAGER_DEVICE_ID, 1);
		// 仮想アドレス識別子を取得できない
		return -ENODEV;
	}

	// 論理 SPE の確保と初期化
	if ((spes = initialize_logical_spes(lpar_id, vas_id)) < 1)
	{
		printk(KERN_ERR "[%s] Error: initialize_logical_spes() (%s:%u)\n", SPE_MANAGER_MODULE_NAME, __FILE__, __LINE__);
//		finalize_logical_spes();   // 1 つも確保できていないから不要
		remove_proc_entry(SPE_MANAGER_PROCFS_HYPERVISOR_ENTRY_NAME, proc_base_dir);
		remove_proc_entry(SPE_MANAGER_PROCFS_PROCESSES_ENTRY_NAME, proc_base_dir);
		remove_proc_entry(SPE_MANAGER_PROCFS_BASE_DIR_NAME, NULL);
		cdev_del(&spe_manager_cdev);
		unregister_chrdev_region(SPE_MANAGER_DEVICE_ID, 1);
		// 使用できる論理 SPE がない
		return -ENOSPC;
	}

	// SPE プロセスマネージャの初期化
	initialize_manager();


	printk(KERN_INFO "[%s] The module is loaded.\n", SPE_MANAGER_MODULE_NAME);
	return 0;
}


//=================================================================================================== spe_manager_exit()
/**
 * spe_manager_exit
 * キャラクタデバイス解放
 *
 * Note: 以下に示す外部関数参照がある。
 *         finalize_logical_spes (lspe.c)
 *
 * @return void
 * @static
 */
static void __exit spe_manager_exit(void)
{
	// 論理 SPE の後始末
	finalize_logical_spes();
	// procfs エントリとディレクトリの解放
	remove_proc_entry(SPE_MANAGER_PROCFS_HYPERVISOR_ENTRY_NAME, proc_base_dir);
	remove_proc_entry(SPE_MANAGER_PROCFS_PROCESSES_ENTRY_NAME, proc_base_dir);
	remove_proc_entry(SPE_MANAGER_PROCFS_BASE_DIR_NAME, NULL);
	// キャラクタデバイスの削除
	cdev_del(&spe_manager_cdev);
	unregister_chrdev_region(SPE_MANAGER_DEVICE_ID, 1);


	printk(KERN_INFO "[%s] The module is unloaded.\n", SPE_MANAGER_MODULE_NAME);
}


module_init(spe_manager_init);
module_exit(spe_manager_exit);



//================================================================================================ transfer_spe_kernel()
/**
 * transfer_spe_kernel
 * SPE 向け軽量カーネルを論理 SPE へ転送する
 *
 * @return void
 * @static
 */
static void transfer_spe_kernel(void)
{
	int i, lspe_count = get_lspe_count();
	for (i = 0; i < lspe_count; i++)
	{
		lspe_data_t *lspe = get_lspe_data(i);
		memcpy(lspe->ls, spe_kernel_raw, SPE_KERNEL_SIZE);
	}
	asm volatile("eieio");   // 一応(^^;
}