NAND FLASH ECC校验原理与实现

  NAND FLASH ECC校验原理与实现

2008-01-25 14:57:27
 
ECC简介
  由于NAND Flash的工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此,在NAND的生产中及使用过程中会产生坏块。为了检测数据的可靠性,在应用NAND Flash的系统中一般都会采用一定的坏区管理策略,而管理坏区的前提是能比较可靠的进行坏区检测。
  如果操作时序和电路稳定性不存在问题的话,NAND Flash出错的时候一般不会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出错。
  对数据的校验常用的有奇偶校验、CRC校验等,而在NAND Flash处理中,一般使用一种比较专用的校验——ECC。ECC能纠正单比特错误和检测双比特错误,而且计算速度很快,但对1比特以上的错误无法纠正,对2比特以上的错误不保证能检测。
ECC原理
  ECC一般每256字节原始数据生成3字节ECC校验数据,这三字节共24比特分成两部分:6比特的列校验和16比特的行校验,多余的两个比特置1,如下图所示:
 
  
  ECC的列校验和生成规则如下图所示:

  用数学表达式表示为:
    P4=D7(+)D6(+)D5(+)D4  P4`=D3(+)D2(+)D1(+)D0
    P2=D7(+)D6(+)D3(+)D2  P2`=D5(+)D4(+)D1(+)D0
    P1=D7(+)D5(+)D3(+)D1  P1`=D6(+)D4(+)D2(+)D0
  这里(+)表示“位异或”操作
  
  ECC的行校验和生成规则如下图所示:
  用数学表达式表示为:
    P8 = bit7(+)bit6(+)bit5(+)bit4(+)bit3(+)bit2(+)bit1(+)bit0(+)P8
    ……………………………………………………………………………………
  这里(+)同样表示“位异或”操作
 
  当往NAND Flash的page中写入数据的时候,每256字节我们生成一个ECC校验和,称之为原ECC校验和,保存到PAGE的OOB(out-of-band)数据区中。
  当从NAND Flash中读取数据的时候,每256字节我们生成一个ECC校验和,称之为新ECC校验和。
  校验的时候,根据上述ECC生成原理不难推断:将从OOB区中读出的原ECC校验和新ECC校验和按位异或,若结果为0,则表示不存在错(或是出现了ECC无法检测的错误);若3个字节异或结果中存在11个比特位为1,表示存在一个比特错误,且可纠正;若3个字节异或结果中只存在1个比特位为1,表示OOB区出错;其他情况均表示出现了无法纠正的错误。
ECC算法的实现
  static const u_char nand_ecc_precalc_table[] =
  {
    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
  };
  // Creates non-inverted ECC code from line parity
  static void nand_trans_result(u_char reg2, u_char reg3,u_char *ecc_code)
  {
    u_char a, b, i, tmp1, tmp2;
    /* Initialize variables */
    a = b = 0x80;
    tmp1 = tmp2 = 0;
    /* Calculate first ECC byte */
    for (i = 0; i < 4; i++)
    {
      if (reg3 & a)    /* LP15,13,11,9 --> ecc_code[0] */
        tmp1 |= b;
      b >>= 1;
      if (reg2 & a)    /* LP14,12,10,8 --> ecc_code[0] */
        tmp1 |= b;
      b >>= 1;
      a >>= 1;
    }
    /* Calculate second ECC byte */
    b = 0x80;
    for (i = 0; i < 4; i++)
    {
      if (reg3 & a)    /* LP7,5,3,1 --> ecc_code[1] */
        tmp2 |= b;
      b >>= 1;
      if (reg2 & a)    /* LP6,4,2,0 --> ecc_code[1] */
        tmp2 |= b;
      b >>= 1;
      a >>= 1;
    }
    /* Store two of the ECC bytes */
    ecc_code[0] = tmp1;
    ecc_code[1] = tmp2;
  }
  // Calculate 3 byte ECC code for 256 byte block
  void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
  {
    u_char idx, reg1, reg2, reg3;
    int j;
    /* Initialize variables */
    reg1 = reg2 = reg3 = 0;
    ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
    /* Build up column parity */
    for(j = 0; j < 256; j++)
    {
      /* Get CP0 - CP5 from table */
      idx = nand_ecc_precalc_table[dat[j]];
      reg1 ^= (idx & 0x3f);
      /* All bit XOR = 1 ? */
      if (idx & 0x40) {
        reg3 ^= (u_char) j;
        reg2 ^= ~((u_char) j);
      }
    }
    /* Create non-inverted ECC code from line parity */
    nand_trans_result(reg2, reg3, ecc_code);
    /* Calculate final ECC code */
    ecc_code[0] = ~ecc_code[0];
    ecc_code[1] = ~ecc_code[1];
    ecc_code[2] = ((~reg1) << 2) | 0x03;
  }
  // Detect and correct a 1 bit error for 256 byte block
  int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
  {
    u_char a, b, c, d1, d2, d3, add, bit, i;
    /* Do error detection */
    d1 = calc_ecc[0] ^ read_ecc[0];
    d2 = calc_ecc[1] ^ read_ecc[1];
    d3 = calc_ecc[2] ^ read_ecc[2];
    if ((d1 | d2 | d3) == 0)
    {
      /* No errors */
      return 0;
    }
    else
    {
      a = (d1 ^ (d1 >> 1)) & 0x55;
      b = (d2 ^ (d2 >> 1)) & 0x55;
      c = (d3 ^ (d3 >> 1)) & 0x54;
      /* Found and will correct single bit error in the data */
      if ((a == 0x55) && (b == 0x55) && (c == 0x54))
      {
        c = 0x80;
        add = 0;
        a = 0x80;
        for (i=0; i<4; i++)
        {
          if (d1 & c)
            add |= a;
          c >>= 2;
          a >>= 1;
        }
        c = 0x80;
        for (i=0; i<4; i++)
        {
          if (d2 & c)
            add |= a;
          c >>= 2;
          a >>= 1;
        }
        bit = 0;
        b = 0x04;
        c = 0x80;
        for (i=0; i<3; i++)
        {
          if (d3 & c)
            bit |= b;
          c >>= 2;
          b >>= 1;
        }
        b = 0x01;
        a = dat[add];
        a ^= (b << bit);
        dat[add] = a;
        return 1;
      }
      else
      {
        i = 0;
        while (d1)
        {
          if (d1 & 0x01)
            ++i;
          d1 >>= 1;
        }
        while (d2)
        {
          if (d2 & 0x01)
            ++i;
          d2 >>= 1;
        }
        while (d3)
        {
          if (d3 & 0x01)
            ++i;
          d3 >>= 1;
        }
        if (i == 1)
        {
          /* ECC Code Error Correction */
          read_ecc[0] = calc_ecc[0];
          read_ecc[1] = calc_ecc[1];
          read_ecc[2] = calc_ecc[2];
          return 2;
        }
        else
        {
          /* Uncorrectable Error */
          return -1;
        }
      }
    }
    /* Should never happen */
    return -1;
  } 

 ECC算法实践      2007-07-17 15:05:00

版权声明:原创作品,如需转载,请与作者联系。否则将追究法律责任。
关于椭圆曲线加密算法 (ECC) ,前几天因为想算序列号,所以研究了一下,把知道的东西写一下,怕以后忘记。
关于 ECC 算法本身,请 Google 一篇文件《 ECC 加密算法入门介绍》。在此,只介绍几个概念。
所谓椭圆曲线,是指类似于  y^2+a1xy+a3y = x^3+a2x^2+a4x+a6  这个表达式的曲线。
为了简便起见,有的算法采用 y^2 = x^3+ax^2+b 的形式,而有的采用  y^2 + xy = x^3 + ax^2 + b  的形式。
椭圆曲线为了用于密码学,需要把以前的连续曲线变为有限域上的点。所谓有限域,就是由有限个元素组成的域,比如说有限域 Fp ,就是由 P 个元素组成的域。
在引入有限域后,原来的加减乘除法则都发生了变化,例如:
加法是 a+b≡c (mod p) ,即 (a+b)÷p 的余数  c÷p 的余数相同。
乘法是 a×b≡c (mod p) ,即 (a×b)÷p 的余数  c÷p 的余数相同。
b 的倒数 b^-1 的算法是用 b×b^-1≡1 (mod p) 来算的。
举例来说假定有限域是 20 ,那么 11+13≡43 的倒数是 7
有限域的算法对 ECC 来说非常关键,由此可以类推的是 b 的平方根 c 应该是 c×c≡b (mod p) ,所以相当难算,到现在我也想不出算法是什么。
还有一个很重要的概念就是阶,对于椭圆曲线上的某个点 G ,存在一个最小的数 n ,使得 nG=O∞ ,那么 n 就是 G 点的阶。
ECC 可以用户数据加密,签名等,也可以用户生成软件序列号,微软的软件序列号就是用的 ECC
ECC 生成软件序列号的方式是:
   1 、选择一条椭圆曲线 Ep(a,b) ,和基点 G 
   2
、选择私有密钥 kk<nnG 的阶),利用基点 G 计算公开密钥 K=kG 
   3
、产生一个随机整数 rr<n ),计算点 R=rG 
   4
、将用户名和点 R 的坐标值 x,y 作为参数,计算 SHASecure Hash Algorithm )值,即 Hash=SHA(username,x,y) 
   5
、计算 sn≡r - Hash * k (mod n) 
   6
、将 snHash 作为   用户名 username 的序列号
验证的算法为:
1 、从用户输入的序列号中,提取 sn 以及 Hash 
2
、计算点 R≡sn*G+Hash*K ( mod p ) ,如果 snHash 正确,其值等于软件作者签名过程中点 R(x,y) 的坐标,因为  
      sn≡r-Hash*k 
mod n 
      
所以  
       sn*G + Hash*K 
      =(r-Hash*k)*G+Hash*K 
      =rG-Hash*kG+Hash*K 
      =rG- Hash*K+ Hash*K 
      =rG=R 
 
 3
、将用户名和点 R 的坐标值 x,y 作为参数,计算 H=SHA(username,x,y) 
 4
、如果 H=Hash  则注册成功。如果 H≠Hash  ,则注册失败。
实现该算法时,可以采用一些 Open SourceECC 库,例如 borZoi
borZoi 选定有限域用的是这样的方法:
// Field polynomial: p(t) = t^163 + t^7 + t^6 + t^3 + 1
inline  void use_NIST_B_163 () { F2X pt=Pentanomial (163, 7, 6, 3, 0); setModulus (pt); };
可想而知,这个有限域的值是很大的,但是这还只是borZoi中最小的有限域,可以选择强度更大的有限域。其中的F2X是封装了大数加减乘除等运算的类。
接下来定义曲线:
// Degree 163 Binary Field from fips186-2
// Pseudorandom curve E: y^2 + xy = x^3 + x^2 + b,
// b = 2 0a601907 b8c953ca 1481eb10 512f7874 4a3205fd
// Base point order:
// r = 5846006549323611672814742442876390689256843201587
// Base point G:
// Gx = 3 f0eba162 86a2d57e a0991168 d4994637 e8343e36
// Gy = 0 d51fbc6c 71a0094f a2cdd545 b11c5c0c 797324f1
// Cofactor f = 2
#define  NIST_B_163 EC_Domain_Parameters (163, 3, 7, 6, 3, Curve ("1", "20a601907b8c953ca1481eb10512f78744a3205fd"), decto_BigInt ("5846006549323611672814742442876390689256843201587"), Point ("3f0eba16286a2d57ea0991168d4994637e8343e36", "0d51fbc6c71a0094fa2cdd545b11c5c0c797324f1"), decto_BigInt ("2"));
可以看到,borZoi用的曲线方程是y^2 + xy = x^3 + x^2 + ba1,但是b选择了一个大数:
b = 2 0a601907 b8c953ca 1481eb10 512f7874 4a3205fd
基点G的座标也是很大的数,rG点阶,可以验证rG=0
如果试图自己找到另外一个G点,是非常困难的一件事,我曾经试图自己找到一个,但是最终还是放弃了。虽然从道理上来说很简单,只要满足方程式就可以了。
我们先找到一对私钥和公钥。
use_NIST_B_163 ();
EC_Domain_Parameters dp = NIST_B_163;
ECPrivKey sk (dp);
ECPubKey pk (sk);
构造私钥的代码其实就是:
ECPrivKey::ECPrivKey (const EC_Domain_Parameters& ecdp) {
         dp = ecdp;
         s = GenRandom (dp.m);
         s %= dp.r;
}
实际上就是选择了一个小于阶r的一个整数。
根据私钥生成公钥的过程是:
ECPubKey::ECPubKey (const ECPrivKey& sk) {
         dp = sk.dp;
         Curve E (dp.a, dp.b);
         W = E.mul (sk.s, dp.G);
}
实际上就是按照算法要求的计算K=sGborZoi提供了曲线计算的很多方法,例如,此处计算sG,用的是Curve类的mul方法。
然后,我们根据算法要求,生成一个随机数rr<n,并且计算R=rG
BigInt r = GenRandom (pk.dp.m);
r %= pk.dp.r;
Curve C(pk.dp.a, pk.dp.b);
Point R=C.mul (r, pk.dp.G);
然后我们定义一个字符串作为用户名
OCTETSTR data(3);
data[0] = 'a'; data[1] = 'b'; data[2] = 'c';
将用户名和R点的座标计算SHA
OCTETSTR x_buf = FE2OSP (R.x);
OCTETSTR y_buf = FE2OSP (R.y);
BigInt Hash = OS2IP (SHA1 (data || x_buf || y_buf));
OCTETSTR 的定义实际就是:
typedef  unsigned char OCTET;
typedef  std::vector<OCTET> OCTETSTR;
然后计算 sn≡r - Hash * k (mod n)
BigInt sn = sk.s * Hash;
sn %= pk.dp.r;  // pk.dp.r 就是G点的阶
sn = r - sn;
if (sn < BigInt(0))
         sn += pk.dp.r;
snHash就是 data 的序列号了。
下面验证,根据算法要求, 计算点 R≡sn*G+Hash*K ( mod p )
Point R2 = C.mul(sn,pk.dp.G);
R2 = C.add(R2, C.mul(Hash,pk.W));
OCTETSTR x2_buf = FE2OSP (R2.x);
OCTETSTR y2_buf = FE2OSP (R2.y);
然后, H=SHA(username,x,y)
BigInt Hash2 = OS2IP (SHA1 (data || x2_buf || y2_buf));
判断两次的Hash值是否是相等的就可以了。
以上就是生成和验证序列号的方法,遗憾的是,borZoi的曲线和G点选择都太大了,生成的序列号太长了。
微软的序列号只有25位,这25位是根据114位的数据用base24编码生成的,这24个字符一般是不容易混淆的字符: BCDFGHJKMPQRTVWXY2346789
根据文章《Microsoft25CDKey里有什么》记载,这114位的序列号包括:
31位的Data部分,对应于我们例子中的data部分;28位的Hash55位的签名。
根据算法的原理可知,Hash数据取多少位无所谓,如果需要28位,直接从前往后取28位就可以,不影响算法的准确性。
但是签名数据如果取55位的话,就比较麻烦,因为根据算法,我们知道如果签名是55位的话,就要求G点的阶r不能多于56位,否则不能保证签名一定会少于55位。因为我们需要找到一个G点,使他的阶r小于56位。我曾经试图找过,但是因为算法较笨,电脑跑了半天也没有找到,所以最后还是放弃了。

 CRC

2006-11-26 20:44:58
对通信的可靠性检查就需要‘校验’,校验是从数据本身进行检查,它依靠某种数学上约定的形式进行检查,校验的结果是可靠或不可靠,如果可靠就对数据进行处理,如果不可靠,就丢弃重发或者进行修复。 

CRC码是由两部分组成,前部分是信息码,就是需要校验的信息,后部分是校验码,如果CRC码共长n个bit,信息码长k个bit,就称为(n,k)码。 它的编码规则是: 
1、首先将原信息码(kbit)左移r位(k+r=n) 
2、运用一个生成多项式g(x)(也可看成二进制数)用模2除上面的式子,得到的余数就是校验码。 

非常简单,要说明的:模2除就是在除的过程中用模2加,模2加实际上就是我们熟悉的异或运算,就是加法不考虑进位,公式是: 
0+0=1+1=0,1+0=0+1=1 
即‘异’则真,‘非异’则假。 
由此得到定理:a+b+b=a 也就是‘模2减’和‘模2加’直值表完全相同。 

有了加减法就可以用来定义模2除法,于是就可以用生成多项式g(x)生成CRC校验码。 

例如: g(x)=x4+x3+x2+1,(7,3)码,信息码110产生的CRC码就是: 
101 
11101 | 110,0000 
111 01 
1 0100 
1 1101 
1001 
余数是1001,所以CRC码是110,1001 

标准的CRC码是,CRC-CCITT和CRC-16,它们的生成多项式是: 
CRC-CCITT=x16+x12+x5+1 
CRC-16=x16+x15+x2+1
 
 
  循环冗余码(CRC)
 1.CRC的工作方法
  在发送端产生一个循环冗余码,附加在信息位后面一起发送到接收端,接收端收到的信息按发送端形成循环冗余码同样的算法进行校验,若有错,需重发。
 2.循环冗余码的产生与码字正确性检验例子。
 例1.已知:信息码:110011 信息多项式:K(X)=X5+X4+X+1
      生成码:11001    生成多项式:G(X)=X4+X3+1(r=4)
    求:循环冗余码和码字。
  解:1)(X5+X4+X+1)*X4的积是 X9+X8+X5+X4 对应的码是1100110000。
    2)积/G(X)(按模二算法)。
    由计算结果知冗余码是1001,码字就是1100111001。
 
               1 0 0 0 0 1←Q(X)
  G(x)→1 1 0 0 1 )1 1 0 0 1 1 0 0 0 0←F(X)*Xr 
           1 1 0 0 1     ,
                1 0 0 0 0
                1 1 0 0 1
                 1 0 0 1←R(X)(冗余码)
 
 例2.已知:接收码字:1100111001  多项式:T(X)=X9+X8+X5+X4+X3+1
      生成码 :  
11001     生成多项式:G(X)=X4+X3+1(r=4)
    求:码字的正确性。若正确,则指出冗余码和信息码。
  解:1)用字码除以生成码,余
数为0,所以码字正确。
 
              1 0 0 0 0 1←Q(X)
 G(x)→1 1 0 0 1 )1 1 0 0 1 1 1 0 0 1←F(X)*Xr+R(x) 
          1 1 0 0 1     ,
               1 1 0 0 1
               1 1 0 0 1
                   0←S(X)(余数)
 
     2)r=4 ,所以冗余码是: 11001 ,信息码是 :110011
  3. 循环冗余码的工作原理
  循环冗余码 CRC 在发送端编码和接收端校验时,都可以利用事先约定的生成多项式 G(X) 来得到, K 位要发送的信息位可对应于一个 (k-1) 次多项式 K(X),r 位冗余位则对应于一个 (r-1) 次多项式 R(X) ,由 r 位冗余位组成的 n=k+r 位码字则对应于一个 (n-1) 次多项式 T(X)=Xr*K(X)+R(X)
  4. 循环冗余校验码的特点
   1) 可检测出所有奇数位错;
   2) 可检测出所有双比特的错;
   3) 可检测出所有小于、等于校验位长度的突发错。

 
  ECC加密算法入门介绍 


作者  :ZMWorm[CCG] 
E-Mail:zmworm@sohu.com 
主页  :Http://ZMWorm.Yeah.Net/ 


前言 

   同RSA(Ron Rivest,Adi Shamir,Len Adleman三位天才的名字)一样,ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学)也属于公开密钥算法。目前,国内详细介绍ECC的公开文献并不多(反正我没有找到)。有一些简介,也是泛泛而谈,看完后依然理解不了ECC的实质(可能我理解力太差)。前些天我从国外网站找到些材料,看完后对ECC似乎懵懂了。于是我想把我对ECC的认识整理一下,与大家分享。当然ECC博大精深,我的认识还很肤浅,文章中错误一定不少,欢迎各路高手批评指正,小弟我洗耳恭听,并及时改正。文章将采用连载的方式,我写好一点就贴出来一点。本文主要侧重理论,代码实现暂不涉及。这就要求你要有一点数学功底。最好你能理解RSA算法,对公开密钥算法有一个了解。《近世代数基础》《初等数论》之类的书,最好您先翻一下,这对您理解本文是有帮助的。别怕,我尽量会把语言通俗些,希望本文能成为学习ECC的敲门砖。 

一、从平行线谈起。 

   平行线,永不相交。没有人怀疑把:)不过到了近代这个结论遭到了质疑。平行线会不会在很远很远的地方相交了?事实上没有人见到过。所以“平行线,永不相交”只是假设(大家想想初中学习的平行公理,是没有证明的)。既然可以假设平行线永不相交,也可以假设平行线在很远很远的地方相交了。即平行线相交于无穷远点P∞(请大家闭上眼睛,想象一下那个无穷远点P∞,P∞是不是很虚幻,其实与其说数学锻炼人的抽象能力,还不如说是锻炼人的想象力)。给个图帮助理解一下: 

   

   直线上出现P∞点,所带来的好处是所有的直线都相交了,且只有一个交点。这就把直线的平行与相交统一了。为与无穷远点相区别把原来平面上的点叫做平常点。 

   以下是无穷远点的几个性质。 

▲直线L上的无穷远点只能有一个。 
(从定义可直接得出) 
▲平面上一组相互平行的直线有公共的无穷远点。 
(从定义可直接得出) 
▲ 平面上任何相交的两直线L1,L2有不同的无穷远点。 
(否则L1和L2有公共的无穷远点P ,则L1和L2有两个交点A、P,故假设错误。) 
▲平面上全体无穷远点构成一条无穷远直线。(自己想象一下这条直线吧) 
▲平面上全体无穷远点与全体平常点构成射影平面。 




二、射影平面坐标系 

   射影平面坐标系是对普通平面直角坐标系(就是我们初中学到的那个笛卡儿平面直角坐标系)的扩展。我们知道普通平面直角坐标系没有为无穷远点设计坐标,不能表示无穷远点。为了表示无穷远点,产生了射影平面坐标系,当然射影平面坐标系同样能很好的表示旧有的平常点(数学也是“向下兼容”的)。 

 

   我们对普通平面直角坐标系上的点A的坐标(x,y)做如下改造: 
   令x=X/Z ,y=Y/Z(Z≠0);则A点可以表示为(X:Y:Z)。 
   变成了有三个参量的坐标点,这就对平面上的点建立了一个新的坐标体系。 

   例2.1:求点(1,2)在新的坐标体系下的坐标。 
   解:∵X/Z=1 ,Y/Z=2(Z≠0)∴X=Z,Y=2Z ∴坐标为(Z:2Z:Z),Z≠0。即(1:2:1)(2:4:2)(1.2:2.4:1.2)等形如(Z:2Z:Z),Z≠0的坐标,都是(1,2)在新的坐标体系下的坐标。
 

   我们也可以得到直线的方程aX+bY+cZ=0(想想为什么?提示:普通平面直角坐标系下直线一般方程是ax+by+c=0)。新的坐标体系能够表示无穷远点么?那要让我们先想想无穷远点在哪里。根据上一节的知识,我们知道无穷远点是两条平行直线的交点。那么,如何求两条直线的交点坐标?这是初中的知识,就是将两条直线对应的方程联立求解。平行直线的方程是: 
aX+bY+c1Z =0; aX+bY+c2Z =0  (c1≠c2); 
(为什么?提示:可以从斜率考虑,因为平行线斜率相同); 

   将二方程联立,求解。有c2Z= c1Z= -(aX+bY),∵c1≠c2 ∴Z=0  ∴aX+bY=0; 
所以无穷远点就是这种形式(X:Y:0)表示。注意,平常点Z≠0,无穷远点Z=0,因此无穷远直线对应的方程是Z=0。 

   例2.2:求平行线L1:X+2Y+3Z=0 与L2:X+2Y+Z=0 相交的无穷远点。 
   解:因为L1∥L2 所以有Z=0, X+2Y=0;所以坐标为(-2Y:Y:0),Y≠0。即(-2:1:0)(-4:2:0)(-2.4:1.2:0)等形如(-2Y:Y:0),Y≠0的坐标,都表示这个无穷远点。
 

   看来这个新的坐标体系能够表示射影平面上所有的点,我们就把这个能够表示射影平面上所有点的坐标体系叫做射影平面坐标系。 


练习: 
      1、求点A(2,4) 在射影平面坐标系下的坐标。 
      2、求射影平面坐标系下点(4.5:3:0.5),在普通平面直角坐标系下的坐标。 
      3、求直线X+Y+Z=0上无穷远点的坐标。 
      4、判断:直线aX+bY+cZ=0上的无穷远点 和 无穷远直线与直线aX+bY=0的交点,是否是同一个点? 


三、椭圆曲线 

   上一节,我们建立了射影平面坐标系,这一节我们将在这个坐标系下建立椭圆曲线方程。因为我们知道,坐标中的曲线是可以用方程来表示的(比如:单位圆方程是x2+y2=1)。椭圆曲线是曲线,自然椭圆曲线也有方程。 

   椭圆曲线的定义: 
   一条椭圆曲线是在射影平面上满足方程 
   Y2Z+a1XYZ+a3YZ2=X3+a2X2Z+a4XZ2+a6Z3
   ----------------[3-1] 
   的所有点的集合,且曲线上的每个点都是非奇异(或光滑)的。 

定义详解: 

   ▲ Y2Z+a1XYZ+a3YZ2 = X3+a2X2Z+a4XZ2+a6Z3是Weierstrass方程(维尔斯特拉斯,Karl Theodor Wilhelm Weierstrass,1815-1897),是一个齐次方程。 

   ▲ 椭圆曲线的形状,并不是椭圆的。只是因为椭圆曲线的描述方程,类似于计算一个椭圆周长的方程(计算椭圆周长的方程,我没有见过,而对椭圆线积分(设密度为1)是求不出来的。谁知道这个方程,请告诉我呀^_^),故得名。 

   我们来看看椭圆曲线是什么样的。 

 
 
  

   ▲ 所谓“非奇异”或“光滑”的,在数学中是指曲线上任意一点的偏导数Fx(x,y,z),Fy(x,y,z),Fz(x,y,z)不能同时为0。如果你没有学过高等数学,可以这样理解这个词,即满足方程的任意一点都存在切线。 

   下面两个方程都不是椭圆曲线,尽管他们是方程[3-1]的形式。 

 
 

   因为他们在(0:0:1)点处(即原点)没有切线。 

   ▲椭圆曲线上有一个无穷远点O∞(0:1:0),因为这个点满足方程[3-1]。 

   知道了椭圆曲线上的无穷远点。我们就可以把椭圆曲线放到普通平面直角坐标系上了。因为普通平面直角坐标系只比射影平面坐标系少无穷远点。我们在普通平面直角坐标系上,求出椭圆曲线上所有平常点组成的曲线方程,再加上无穷远点O∞(0:1:0),不就构成椭圆曲线了么? 

   我们设x=X/Z ,y=Y/Z代入方程[3-1]得到: 
   y2+a1xy+a3y = x3+a2x2+a4x+a6 -------------------------[3-2] 

   也就是说满足方程[3-2]的光滑曲线加上一个无穷远点O∞,组成了椭圆曲线。为了方便运算,表述,以及理解,今后论述椭圆曲线将主要使用[3-2]的形式。 

   本节的最后,我们谈一下求椭圆曲线一点的切线斜率问题。 
   由椭圆曲线的定义可以知道,椭圆曲线是光滑的,所以椭圆曲线上的平常点都有切线。而切线最重要的一个参数就是斜率k。 

   例3.1:求椭圆曲线方程y2+a1xy+a3y=x3+a2x2+a4x+a6上,平常点A(x,y)的切线的斜率k。 
   解:令F(x,y)= y2+a1xy+a3y-x3-a2x2-a4x-a6 
   求偏导数 
   Fx(x,y)= a1y-3x2-2a2x-a4 
   Fy(x,y)= 2y+a1x +a3 
   则导数为:f'(x)=- Fx(x,y)/ Fy(x,y)=-( a1y-3x2-2a2x-a4)/(2y+a1x +a3
                   = (3x2+2a2x+a4-a1y) /(2y+a1x +a3
   所以k=(3x2+2a2x+a4-a1y) /(2y+a1x +a3)  ------------------------[3-3] 

   看不懂解题过程没有关系,记住结论[3-3]就可以了。 


练习: 
      1、将给出图例的椭圆曲线方程Y2Z=X3-XZ2 和Y2Z=X3+XZ2+Z3转换成普通平面直角坐标系上的方程。 


四、椭圆曲线上的加法 

   上一节,我们已经看到了椭圆曲线的图象,但点与点之间好象没有什么联系。我们能不能建立一个类似于在实数轴上加法的运算法则呢?天才的数学家找到了这一运算法则 

   ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆ 
   自从近世纪代数学引入了群、环、域的概念,使得代数运算达到了高度的统一。比如数学家总结了普通加法的主要特征,提出了加群(也叫交换群,或Abel(阿贝尔)群),在加群的眼中。实数的加法和椭圆曲线的上的加法没有什么区别。这也许就是数学抽象把:)。关于群以及加群的具体概念请参考近世代数方面的数学书。 
   ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆ 

   运算法则:任意取椭圆曲线上两点P、Q (若P、Q两点重合,则做P点的切线)做直线交于椭圆曲线的另一点R’,过R’做y轴的平行线交于R。我们规定P+Q=R。(如图) 

  


法则详解: 
   ▲这里的+不是实数中普通的加法,而是从普通加法中抽象出来的加法,他具备普通加法的一些性质,但具体的运算法则显然与普通加法不同。 

   ▲根据这个法则,可以知道椭圆曲线无穷远点O∞与椭圆曲线上一点P的连线交于P’,过P’作y轴的平行线交于P,所以有 无穷远点 O∞+ P = P 。这样,无穷远点 O∞的作用与普通加法中零的作用相当(0+2=2),我们把无穷远点 O∞ 称为 零元。同时我们把P’称为P的负元(简称,负P;记作,-P)。(参见下图) 

   

   ▲根据这个法则,可以得到如下结论 :如果椭圆曲线上的三个点A、B、C,处于同一条直线上,那么他们的和等于零元,即A+B+C= O∞ 

   ▲k个相同的点P相加,我们记作kP。如下图:P+P+P = 2P+P = 3P。

 

   下面,我们利用P、Q点的坐标(x1,y1),(x2,y2),求出R=P+Q的坐标(x4,y4)。 

   例4.1:求椭圆曲线方程y2+a1xy+a3y=x3+a2x2+a4x+a6上,平常点P(x1,y1),Q(x2,y2)的和R(x4,y4)的坐标。 
   解:(1)先求点-R(x3,y3
   因为P,Q,-R三点共线,故设共线方程为y=kx+b,其中 
   若P≠Q(P,Q两点不重合) 则 
       直线斜率k=(y1-y2)/(x1-x2
   若P=Q(P,Q两点重合) 则直线为椭圆曲线的切线,故由例3.1可知: 
       k=(3x2+2a2x+a4 -a1y) /(2y+a1x+a3

   因此P,Q,-R三点的坐标值就是方程组: 
       y2+a1xy+a3y=x3+a2x2+a4x+a6    -----------------[1] 
       y=(kx+b)                     -----------------[2] 
   的解。 

   将[2],代入[1] 有 
      (kx+b)2+a1x(kx+b)+a3(kx+b) =x3+a2x2+a4x+a6    --------[3] 
   对[3]化为一般方程,根据三次方程根与系数关系(当三次项系数为1时;-x1x2x3 等于常数项系数, x1x2+x2x3+x3x1等于一次项系数,-(x1+x2+x3)等于二次项系数。) 
   所以-(x1+x2+x3)=a2-ka1-k2 
   x3=k2+ka1+a2+x1+x2;---------------------求出点-R的横坐标 
   因为k=(y1-y3)/(x1-x3) 故 
   y3=y1-k(x1-x3);-------------------------------求出点-R的纵坐标 

  (2)利用-R求R 
   显然有 x4=x3= k2+ka1+a2+x1+x2; ------------求出点R的横坐标 
   而y3 y4 为 x=x4时 方程y2+a1xy+a3y=x3+a2x2+a4x+a6的解 
   化为一般方程y2+(a1x+a3)y-(x3+a2x2+a4x+a6)=0 , 根据二次方程根与系数关系得: 
       -(a1x+a3)=y3+y4 
   故y4=-y3-(a1x+a3)=k(x1-x4)-y1-(a1x4+a3); ---------------求出点R的纵坐标 
   即: 
       x4=k2+ka1+a2+x1+x2
       y4=k(x1-x4)-y1-a1x4-a3

   本节的最后,提醒大家注意一点,以前提供的图像可能会给大家产生一种错觉,即椭圆曲线是关于x轴对称的。事实上,椭圆曲线并不一定关于x轴对称。如下图的y2-xy=x3+1 
 

五、密码学中的椭圆曲线 

   我们现在基本上对椭圆曲线有了初步的认识,这是值得高兴的。但请大家注意,前面学到的椭圆曲线是连续的,并不适合用于加密;所以,我们必须把椭圆曲线变成离散的点。 
   让我们想一想,为什么椭圆曲线为什么连续?是因为椭圆曲线上点的坐标,是实数的(也就是说前面讲到的椭圆曲线是定义在实数域上的),实数是连续的,导致了曲线的连续。因此,我们要把椭圆曲线定义在有限域上(顾名思义,有限域是一种只有由有限个元素组成的域)。 

☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆ 
   域的概念是从我们的有理数,实数的运算中抽象出来的,严格的定义请参考近世代数方面的书。简单的说,域中的元素同有理数一样,有自己得的加法、乘法、除法、单位元(1),零元(0),并满足交换率、分配率。 
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆ 

   下面,我们给出一个有限域Fp,这个域只有有限个元素。 
   
   Fp中只有p(p为素数)个元素0,1,2 …… p-2,p-1; 
   Fp 的加法(a+b)法则是 a+b≡c (mod p);即,(a+c)÷p的余数 和c÷p的余数相同。 
   Fp 的乘法(a×b)法则是  a×b≡c (mod p); 
   Fp 的除法(a÷b)法则是  a/b≡c (mod p);即 a×b-1≡c  (mod p);(b-1也是一个0到p-1之间的整数,但满足b×b-1≡1 (mod p);具体求法可以参考初等数论,或
我的另一篇文章)。 
   Fp 的单位元是1,零元是 0。 

   同时,并不是所有的椭圆曲线都适合加密。y2=x3+ax+b是一类可以用来加密的椭圆曲线,也是最为简单的一类。下面我们就把y2=x3+ax+b 这条曲线定义在Fp上: 

   选择两个满足下列条件的小于p(p为素数)的非负整数a、b 
      4a3+27b2≠0 (mod p) 
   则满足下列方程的所有点(x,y),再加上 无穷远点O∞ ,构成一条椭圆曲线。 
     y2=x3+ax+b  (mod p) 
   其中 x,y属于0到p-1间的整数,并将这条椭圆曲线记为Ep(a,b)。 

   我们看一下y2=x3+x+1  (mod 23)的图像 
    
 

   是不是觉得不可思议?椭圆曲线,怎么变成了这般模样,成了一个一个离散的点? 
   椭圆曲线在不同的数域中会呈现出不同的样子,但其本质仍是一条椭圆曲线。举一个不太恰当的例子,好比是水,在常温下,是液体;到了零下,水就变成冰,成了固体;而温度上升到一百度,水又变成了水蒸气。但其本质仍是H2O。 

   Fp上的椭圆曲线同样有加法,但已经不能给以几何意义的解释。不过,加法法则和实数域上的差不多,请读者自行对比。 

   1 无穷远点 O∞是零元,有O∞+ O∞= O∞,O∞+P=P 
   2 P(x,y)的负元是 (x,-y),有P+(-P)= O∞ 
   3 P(x1,y1),Q(x2,y2)的和R(x3,y3) 有如下关系: 
     x3≡k2-x1-x2(mod p) 
     y3≡k(x1-x3)-y1(mod p) 
     其中若P=Q 则 k=(3x2+a)/2y1  若P≠Q,则k=(y2-y1)/(x2-x1


   例5.1 已知E23(1,1)上两点P(3,10),Q(9,7),求1)-P,2)P+Q,3) 2P。 
   解 1)  –P的值为(3,-10) 
      2)  k=(7-10)/(9-3)=-1/2,2的乘法逆元为12 因为2*12≡1 (mod 23) 
          k≡-1*12 (mod 23) 故 k=11。 
          x=112-3-9=109≡17 (mod 23); 
          y=11[3-(-6)]-10=89≡20 (mod 23) 
          故P+Q的坐标为(17,20) 
      3)  k=[3(32)+1]/(2*10)=1/4≡6 (mod 23) 
          x=62-3-3=30≡20 (mod 23) 
          y=6(3-7)-10=-34≡12 (mod 23) 
          故2P的坐标为(7,12) 
 
     
   最后,我们讲一下椭圆曲线上的点的阶。 
   如果椭圆曲线上一点P,存在最小的正整数n,使得数乘nP=O∞,则将n称为P的 ,若n不存在,我们说P是无限阶的。 
   事实上,在有限域上定义的椭圆曲线上所有的点的阶n都是存在的(证明,请参考近世代数方面的书) 


练习: 
  1 求出E11(1,6)上所有的点。 
  2 已知E11(1,6)上一点G(2,7),求2G到13G所有的值。 


六、椭圆曲线上简单的加密/解密 

   公开密钥算法总是要基于一个数学上的难题。比如RSA 依据的是:给定两个素数p、q 很容易相乘得到n,而对n进行因式分解却相对困难。那椭圆曲线上有什么难题呢? 

   考虑如下等式: 
   K=kG  [其中 K,G为Ep(a,b)上的点,k为小于n(n是点G的阶)的整数] 
   不难发现,给定k和G,根据加法法则,计算K很容易;但给定K和G,求k就相对困难了。 
   这就是椭圆曲线加密算法采用的难题。我们把点G称为基点(base point),k(k<n,n为基点G的阶)称为私有密钥(privte key),K称为公开密钥(public key)。 

   现在我们描述一个利用椭圆曲线进行加密通信的过程: 

   1、用户A选定一条椭圆曲线Ep(a,b),并取椭圆曲线上一点,作为基点G。 
   2、用户A选择一个私有密钥k,并生成公开密钥K=kG。 
   3、用户A将Ep(a,b)和点K,G传给用户B。 
   4、用户B接到信息后 ,将待传输的明文编码到Ep(a,b)上一点M(编码方法很多,这里不作讨论),并产生一个随机整数r(r<n)。 
   5、用户B计算点C1=M+rK;C2=rG。 
   6、用户B将C1、C2传给用户A。 
   7、用户A接到信息后,计算C1-kC2,结果就是点M。因为 
          C1-kC2=M+rK-k(rG)=M+rK-r(kG)=M 
      再对点M进行解码就可以得到明文。 

   在这个加密通信中,如果有一个偷窥者H ,他只能看到Ep(a,b)、K、G、C1、C2 而通过K、G 求k 或通过C2、G求r 都是相对困难的。因此,H无法得到A、B间传送的明文信息。 

 

   密码学中,描述一条Fp上的椭圆曲线,常用到六个参量: 
       T=(p,a,b,G,n,h)。 
   (p 、a 、b 用来确定一条椭圆曲线, 
   G为基点, 
   n为点G的阶, 
   h 是椭圆曲线上所有点的个数m与n相除的整数部分) 

   这几个参量取值的选择,直接影响了加密的安全性。参量值一般要求满足以下几个条件: 

   1、p 当然越大越安全,但越大,计算速度会变慢,200位左右可以满足一般安全要求; 
   2、p≠n×h; 
   3、pt≠1 (mod n),1≤t<20; 
   4、4a3+27b2≠0 (mod p); 
   5、n 为素数; 
   6、h≤4。 


七、椭圆曲线在软件注册保护的应用 

   我们知道将公开密钥算法作为软件注册算法的好处是Cracker很难通过跟踪验证算法得到注册机。下面,将简介一种利用Fp(a,b)椭圆曲线进行软件注册的方法。 


   软件作者按如下方法制作注册机(也可称为签名过程) 

   1、选择一条椭圆曲线Ep(a,b),和基点G; 
   2、选择私有密钥k(k<n,n为G的阶),利用基点G计算公开密钥K=kG; 
   3、产生一个随机整数r(r<n),计算点R=rG; 
   4、将用户名和点R的坐标值x,y作为参数,计算SHA(Secure Hash Algorithm 安全散列算法,类似于MD5)值,即Hash=SHA(username,x,y); 
   5、计算sn≡r - Hash * k (mod n) 
   6、将sn和Hash作为 用户名username的序列号 

软件验证过程如下:(软件中存有椭圆曲线Ep(a,b),和基点G,公开密钥K) 

   1、从用户输入的序列号中,提取sn以及Hash; 
   2、计算点R≡sn*G+Hash*K ( mod p ),如果sn、Hash正确,其值等于软件作者签名过程中点R(x,y)的坐标,因为 
      sn≡r-Hash*k (mod n) 
      所以 
       sn*G + Hash*K 
      =(r-Hash*k)*G+Hash*K 
      =rG-Hash*kG+Hash*K 
      =rG- Hash*K+ Hash*K 
      =rG=R ; 
   3、将用户名和点R的坐标值x,y作为参数,计算H=SHA(username,x,y); 
   4、如果H=Hash 则注册成功。如果H≠Hash ,则注册失败(为什么?提示注意点R与Hash的关联性)。 

   简单对比一下两个过程: 
   作者签名用到了:椭圆曲线Ep(a,b),基点G,私有密钥k,及随机数r。 
   软件验证用到了:椭圆曲线Ep(a,b),基点G,公开密钥K。 
   Cracker要想制作注册机,只能通过软件中的Ep(a,b),点G,公开密钥K ,并利用K=kG这个关系获得k后,才可以。而求k是很困难的。 


练习: 
   下面也是一种常于软件保护的注册算法,请认真阅读,并试回答签名过程与验证过程都用到了那些参数,Cracker想制作注册机,应该如何做。 

   软件作者按如下方法制作注册机(也可称为签名过程) 
   1、选择一条椭圆曲线Ep(a,b),和基点G; 
   2、选择私有密钥k(k<n),利用基点G计算公开密钥K=kG; 
   3、产生一个随机整数r(r<n),计算点R(x,y)=rG; 
   4、将用户名作为参数,计算Hash=SHA(username); 
   5、计算 x’=x  (mod n) 
   6、计算sn≡(Hash+x’*k)/r (mod n) 
   7、将sn和x’作为 用户名username的序列号 

   软件验证过程如下:(软件中存有椭圆曲线Ep(a,b),和基点G,公开密钥K) 
   1、从用户输入的序列号中,提取sn以及x’; 
   2、将用户名作为参数,计算Hash=SHA(username); 
   3、计算 R=(Hash*G+x’*K)/sn,如果sn、Hash正确,其值等于软件作者签名过程中点R(x,y),因为 
      sn≡(Hash+x’*k)/r (mod n) 
      所以 
       (Hash*G+x’*K)/sn 
      =(Hash*G+x’*K)/[(Hash+x’*k)/r] 
      =(Hash*G+x’*K)/[(Hash*G+x’*k*G)/(rG)] 
      =rG*[(Hash*G+x’*K)/(Hash*G+x’*K)] 
      =rG=R (mod p) 
   4、v≡x (mod n) 
   5、如果v=x’ 则注册成功。如果v≠x’ ,则注册失败。 


八、结语 

   历经半个多月断断续续的写作,这篇拙作终于算告一段落了。为写这篇文章,我查了大量的资料,但为了使文章更通俗易懂,我尽量避免涉及专业术语,F2n域上的椭圆曲线本文也没有涉及。不过,一些名词描述的可能还不太精确,希望众读者对文章的问题,多多批评指正。我也仅仅把这篇文章作为初稿,我会不断修订他的。最后感谢看雪、Sunbird、CCG以及看雪论坛所有成员对我的支持,感谢一切帮助过我的人,没有你们的鼓励,这篇文章我是没有动力写完的,谢谢,谢谢大家! 


2003-5-3 初稿,于看雪论坛 
2004-7-11二稿,修正一张图片

<全文完> 


主要参考文献 

张禾瑞,《近世代数基础》,高等教育出版社,1978 
闵嗣鹤 严士健,《初等数论》,高等教育出版社,1982 
段云所,《网络信息安全》第三讲,北大计算机系 
Michael Rosing ,chapter5《Implementing Elliptic Curve Cryptography》,Softbound,1998 
《SEC 1: Elliptic Curve Cryptography》,Certicom Corp.,2000 
《IEEE P1363a / D9》,2001 


┌───┐  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 
│\   ╱│  ▓  ☆        ECC加密算法入门介绍           ★  ▓ 
│ \╱/ │  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 
│╱∨  │  ▓  ☆版权所有 ZMWorm[CCG], 转载请保留完整性★  ▓ 
└─┴─┘  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
 
 
 
 
ARM的存储管理单元MMU
2008-01-19 22:06:08
 
    MMU提供的一个关键服务是,能使各个任务作为独立的程序在其自己的私有存储空间运行.地址转换过程允许运行的多个程序使用相同的虚拟地址,而各自存储在物理存储器的不同位置.
 
    区域可以是活跃的,也可以是睡眠的:活跃区域包含当前系统正在使用的代码或数据;睡眠区域包含当前不使用,但可能在短时间内变为活跃的代码或数据.睡眠区域是被保护的,因此当前正在运行的任务是不能访问的.MPU和MMU的主要区别是,MMU中增加了额外的硬件,以支持虚存.
 
    一个重定位寄存器只能转换一块存储空间.这块存储空间的大小由虚拟地址的偏移量部分所占的位数所决定.ARM的MMU中临时存放转换数据的一组重定位寄存器实际上是一个由64个重定位寄存器组成的全相联cache.这个cache就是转换旁路缓冲器--TLB.TLB缓存最近被访问的页的转换数据.MMU还使用主存中的表来存放描述系统中用到的虚拟存储器映射数据,这些转换数据的表就是页表.页表中的每个项代表了将虚拟存储器的一个页转换到物理存储器的一个页帧所需要的所有信息.
 
    在MMU中,区域被定义为一组页表的集合,并作为虚存中的连续页完全由软件控制.除了L1一级页表外,所有其他的页表都代表虚存的1MB空间.如果一个区域的大小大于1MB或者它跨过页表的1MB边界地址,那么就必须使用一组页表来描述这个区域.页表可以驻留在存储器中,而不必映射到MMU硬件.
 
    当在2个应用程序任务间实现上下文切换时,处理器其实要发生多次上下文切换.它先从用户模式任务切换到内核模式任务,以处理准备运行下一个应用程序任务时的上下文数据的移动;然后,它从内核模式任务切换到下一个上下文的新的用户模式任务.
 
    MMU在转换一个地址时失败,就会产生一个中止异常.MMU只有在转换失败,权限错误和域错误时,才会中止.
 
    L1住页表包含2种类型的页表项:保存指向二级页表起始地址指针的页表项和保存用于转换1MB页的页表项.L1页表也称为段页表.当L1页表作为页目录时,其页表项包含的是1MB虚拟空间的L2粗页表或L2细页表指针;当L1页表用于转换一个1MB的段时,其页表项包含的是物理存储器中1MB页帧的首地址.目录页表项和1MB的段页表项可以共存于L1主页表.CP15:c2寄存器保存转换表基地址TTB--指向L1主页表在虚存中的位置.
 
    TLB只支持两种类型的命令:清除TLB和锁定TLB中的转换数据.存储器访问时,MMU将虚拟地址的一部分与TLB中的所有值进行比较.如果TLB中已有所要的转换数据,即为一次TLB命中,则由TLB提供物理地址的转换数据.如果TLB中不存在有效的转换数据,即为一次TLB失效,则MMU会由硬件自动处理TLB失效,通过主存中的页表搜索有效的转换数据,并将其装入TLB的64行的一行.如果TLB中的某一行是锁定的,则当TLB清除命令发出时,它仍然保留在TLB中.
 
ARM的存储保护单元MPU
2008-01-19 22:03:38
 
    在受保护的系统中,主要有两类资源需要监视:存储器系统和外围设备.存储器中对区域的访问可以是读/写,只读或不可访问,基于当时的处理器模式--管理模式或用户模式,还有一些附加的权限.区域还有控制cache和写缓冲器属性的cache写策略.当处理器访问主存的一个区域时,MPU比较该区域的访问权限属性和当时的处理器模式.如果请求符合区域访问标准,则MPU允许内核读/写主存;如果存储器请求导致存储器访问违例,则MPU产生一个异常信号.
 
    区域与内核是冯.诺依曼结构还是哈佛结构无关.每个区域通过0~7的号码来标识和引用.区域的属性如下:
(1)区域可以相互重叠;
(2)每个区域都分配有一个优先级,该优先级与分配区域的权限无关;
(3)当区域重叠时,具有最高优先级的区域的属性可以覆盖其他区域的属性,优先级仅作用于重叠部分的地址;
(4)区域的起始地址必须是其大小的倍数;
(5)区域的大小可以是4KB~4GB之间的任何2的乘幂;
(6)访问所定义区域外的存储器将产生异常.如果是内核预取指令,则MPU产生预取指令中止异常;如果是存储器数据请求,则产生数据中止异常.
    
    在启用存储器保护单元之前,必须至少定义一个数据区域和一个指令区域,而且必须在启用cache和写缓冲器之前(或同时)启用存储器保护单元.控制器通过设置CP15的主寄存器c1~5来配置MPU.通过配置寄存器c2和c3来设置区域的cache和写缓冲器的属性,寄存器c5控制区域的访问权限,在寄存器c6里有8个或16个次寄存器用来定义每个区域的大小和位置.初始化MPU,cache和写缓冲器需要以下步骤:
(1)使用CP15:c6来定义指令和数据区域的大小和位置
(2)使用CP15:c5来设置每个区域的访问权限
(3)分别使用CP15:c2和CP15:c3来设置每个区域的cache和写缓冲器属性
(4)使用CP15:c1来使能cache和MPU
 
    每个内核有3个CP15寄存器用来控制区域的cache和写缓冲器属性.其中CP15:c2:c0:0和CP15:c2:c0:1两个寄存器保存D-cache和I-cache区域属性;第三个寄存器,CP15:c3:c0:0用于保存区域写缓冲器属性,并应用于存储器数据区域.当配置数据区域时,区域的cache位和写缓冲器位一起决定区域的策略.写缓冲器位有2个用途:使能和禁止区域的写缓冲器和设置区域的cache策略.区域的cache位控制写缓冲器位的作用.当cache位为0时,写缓冲器位为1,则使能写缓冲器;写缓冲器位为0,则禁用写缓冲器.当cache位为1时,cache和写缓冲器都被使能,此时写缓冲器位决定cache策略.若写缓冲器位为0,则区域使用直写策略;若写缓冲器位为1,则区域使用回写策略.
 
 
 

转载于:https://www.cnblogs.com/lihaiping/archive/2013/04/11/ecc.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值