2008年12月16日

[Linux] メモリに物理アドレスでアクセスする

カーネルをいじっていて,物理アドレスでメモリにアクセスする必要がでた.

かなり強引技だけど,仮想メモリのアドレスをそのまま物理メモリアドレス変換するページテーブルを作成してみた.
# もっとスマートなやりかたないですかねー...?
#include <linux/module.h>
#include <linux/init.h>
#include <asm/page.h>
#include <linux/pagemap.h>


// 物理メモリにアクセスするためのページテーブル
void *linear_pagedir = NULL;


/*
 * 物理メモリにアクセスするためのページテーブルを作成
 * (最初に1回これを呼ぶ)
 */
void
linear_pagedir_init(void) {
	struct page *pagedir_page;
	ulong pde_template, pde;
	int i;
	ulong *pd;

	pagedir_page = alloc_page(GFP_ATOMIC | __GFP_ZERO);
	linear_pagedir = page_address(pagedir_page);
	pd = (ulong *) linear_pagedir;

	pde_template = (1 << 7) + (1 << 1) + (1 << 0);

	for (i = 0; i < 1024; i++) {
		pde = pde_template | (i << 22);
		pd[i] = pde;
	}
}

/*
 * 物理メモリを1ワード読む
 * phys_addr: 読みたい物理アドレス
 * val: 読み出す変数
 *
 * 事前にlinear_pagedir_init()を呼んでおく必要あり.
 */
asmlinkage void
read_phys_mem (ulong phys_addr, ulong *val) {
	ulong tmpcr3 = (ulong) __pa(linear_pagedir);
	asm volatile (
			"mov %%cr3, %%eax\n\t"
			"mov %1, %%cr3\n\t"
			"mov (%2), %%ebx\n\t"
			"mov %%eax, %%cr3\n\t"
			"mov %%ebx, %0"
			: "=r" (*val)
			: "r" (tmpcr3), "r" (phys_addr)
			: "%eax", "%ebx", "cc"
	);
}


/**
 * 物理メモリにアクセスするためのページテーブルを開放
 * (最後に1回これを呼ぶ)
 */
void
linear_pagedir_free(void) {
	if (linear_pagedir == NULL) {
		// no need to free
	} else {
		free_page((ulong)linear_pagedir);
		linear_pagedir = NULL;
	}
}

posted by dev-man at 15:36| Comment(0) | TrackBack(0) | Linux | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック