2014/11/26(水)[ARM][Linux]copy_{from|to}_user()
copy_to_user()
kernel空間からusesr空間への安全なメモリコピー、と見てもらえれば良いでしょう。単純なメモリコピーは、memcpy()を使いますが、これはユーザ空間のアドレスを
指定できることに意義があるもの。
ユーザ空間のメモリは必ずしも物理メモリに割りつけられているとは限らない。
まぁこれだけではないので、swap未使用であったりメモリが多くても使いましょう。
NAME copy_to_user - Copy a block of data into user space. SYNOPSIS unsigned long copy_to_user(void __user * to, const void * from, unsigned long n); ARGUMENTS to Destination address, in user space. from Source address, in kernel space. n Number of bytes to copy. CONTEXT User context only. This function may sleep. DESCRIPTION Copy data from kernel space to user space. Returns number of bytes that could not be copied. On success, this will be zero.
実装を追う
実際にコードを追った順序と記載順序が異なるので、変な感じです。関数の実態は以下にあります。
アライメントを考慮した最適な転送を行うためのテンプレでコードが書かれており、
そこで使用するデータ転送の実態をcopy_to_userとcopy_from_userでマクロ定義し、
同一templateでコードを生成させています。
FILE: arch/arm/lib/copy_to_user.S
FILE: arch/arm/lib/copy_template.S
で、ここからが本題。strusrマクロでは、abort引数がついています。
これが何しているのか、何となく想像はつきますが、理解できなかった。
バイナリを逆アセンブルすると普通のstr命令が並ぶだけで、memcpyとの差異が判らず。
実コードと併せて定数テーブルを作っていることに気がつき、
このセクション名で検索したところ仕組みがわかったという流れでした。
FILE: copy_to_user.S
.macro str1w ptr reg abort strusr \reg, \ptr, 4, abort=\abort .endmFILE: archinc/asm/assembler.h
.macro strusr, reg, ptr, inc, cond=al, rept=1, abort=9001f usracc str, \reg, \ptr, \inc, \cond, \rept, \abort .endm .macro usracc, instr, reg, ptr, inc, cond, rept, abort, t=TUSER() .rept \rept 9999: .if \inc == 1 \instr\cond\()b\()\t \reg, [\ptr], #\inc .elseif \inc == 4 \instr\cond\()\t \reg, [\ptr], #\inc .else .error "Unsupported inc macro argument" .endif .pushsection __ex_table,"a" .align 3 .long 9999b, \abort .popsection .endr .endm usracc str, \reg, \ptr, \inc(=4), \cond(=none,al), \rept(=none,1), \abort(=pop) stral reg, [ptr], #inc .pushsection __ex_table,"a" .align 3 .long 9999b, \abort
セクション "__ex_table"に、{exceptionが発生するアドレス、返りアドレス}を設定する。
ldscriptにて、同名のセクションを定義、その前後のアドレスをを拾えるようにしてある。
FILE: /kernel/linux-stable/arch/arm/kernel/vmlinux.lds.S
. = ALIGN(4); __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { __start___ex_table = .; #ifdef CONFIG_MMU *(__ex_table) #endif __stop___ex_table = .;
テーブルの先頭が変数名として判ったので、コレを探す。
これを参照しているのは、以下の関数。
/* Given an address, look for it in the exception tables. */ const struct exception_table_entry *search_exception_tables(unsigned long addr)さらにこれを使っているところを探すと、アーキ依存部のpage fault処理部分が見つかる。
FILE: kernel/linux-stable/arch/arm/mm/extable.c
int fixup_exception(struct pt_regs *regs) { const struct exception_table_entry *fixup; fixup = search_exception_tables(instruction_pointer(regs)); if (fixup) { regs->ARM_pc = fixup->fixup; #ifdef CONFIG_THUMB2_KERNEL /* Clear the IT state to avoid nasty surprises in the fixup */ regs->ARM_cpsr &= ~PSR_IT_MASK; #endif } return fixup != NULL; }さらにさらに、これを使っているところを探すと、
アーキ依存部のpage fault処理部分が見つかる。
FILE: kernel/linux-stable/arch/arm/mm/fault.c
/* * Oops. The kernel tried to access some page that wasn't present. */ static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, struct pt_regs *regs) { /* * Are we prepared to handle this kernel fault? */ if (fixup_exception(regs)) return;ここに来れば faultが発生しても、Oopsを吐かずに指定されたアドレスへ
帰って行くという仕組みになっている。
copy_to_user()に関しては、エラーハンドリングは特に行っていない。
失敗すればコピーを中断して返る、という実装になっている。
そのため、返値はコピーバイト数の残り、ということにしたのであろう。
で、この__do_kernel_fault()はどこから来たのかなぁと見ていくと、
fault handlerのほうへと流れ着くことになる。
ここらへんまできて、ちょっと深みにはまりそうだな、と感じたので
別記事にすることにします。
FILE: kernel/linux-stable/arch/arm/mm/fault.c
static int __kprobes do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
2014/04/04(金)ioremapで死ぬ件
当該チェックは次のコミットで追加されたコードで行われている。
kernel parameterで mem=...を指定した際に、属性が設定されてしまったことが要因だったと思う。
ずいぶん前の事象なので、記憶から欠損している・・・ので、ゴミですね。
とりあえず、後日自分で引っかかるかもしれないので残しておく・・・。
たぶん、コレにひっかかっていたのだと思う・・・。たぶん。
void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn, /* * Don't allow RAM to be mapped - this causes problems with ARMv6+ */ if (WARN_ON(pfn_valid(pfn))) return NULL;
linux kernelコミットログを漁る
commit 576d2f2525612ecb5af029a76f21f22a3b82563d Author: Nicolas Pitre <nicolas.pitre@linaro.org> Date: Fri Sep 16 01:14:23 2011 -0400 ARM: add generic ioremap optimization by reusing static mappings Now that we have all the static mappings from iotable_init() located in the vmalloc area, it is trivial to optimize ioremap by reusing those static mappings when the requested physical area fits in one of them, and so in a generic way for all platforms. Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org> Tested-by: Stephen Warren <swarren@nvidia.com> Tested-by: Kevin Hilman <khilman@ti.com> Tested-by: Jamie Iles <jamie@jamieiles.com>この手前で、vmlistの領域に引っかかっていれば、そのアドレスを返している(ioremapした空間だと思われ)。
上記コミットは最適化の結果なので、これより前にRussel氏がARMv6以降のmemory領域を
remapすることはNGだというコミットを出している(下記).
論理-物理mapのaliaseを設けるときに設定が異なるとマズイと言っている..のだな.
commit 309caa9cc6ff39d261264ec4ff10e29489afc8f8 Author: Russell King <rmk+kernel@arm.linux.org.uk> Date: Mon Jun 21 21:03:18 2010 +0100 ARM: Prohibit ioremap() on kernel managed RAM ARMv6 and above have a restriction whereby aliasing virtual:physical mappings must not have differing memory type and sharability attributes. Strictly, this covers the memory type (strongly ordered, device, memory), cache attributes (uncached, write combine, write through, write back read alloc, write back write alloc) and the shared bit. However, using ioremap() and its variants on system RAM results in mappings which differ in these attributes from the main system RAM mapping. Other architectures which similar restrictions approch this problem in the same way - they do not permit ioremap on main system RAM. Make ARM behave in the same way, with a WARN_ON() such that users can be traced and an alternative approach found. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>FILE: arch/arm/mm/ioremap.c
static mapping属性を追加@mm.h して,...
総論
ざっとarch/armのあたりを中心に探ってみた。ioremapでそのページ(セクションもありえる)の属性(PTE)を設定しているので、二度目の設定がなされると、上記の問題に当たる。
Memory領域は(おそらく)、kernelが好きなようにいじってしまうので、
ioremapで任意の設定にすることが嫌われるのだと思う。
(だとすると、ioremapでマップしたメモリのcache設定とかは..どうなるのか)
mem=xxx@xxxで領域を指定した場合、kernelが論理物理マップをとるのがその範囲になる(はずな)ので、
MMUに対して属性の設定がされていない状態となる(少なくともそのCPUから見て)。
したがって、ioremap()が成功している時点で、そのCPUからみたMMU経由の設定に不一致は存在しない。