2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
23 #define xcalloc calloc
24 #define xmalloc malloc
25 #define grub_memset memset
26 #define grub_memcpy memcpy
35 typedef unsigned int grub_size_t
;
36 typedef unsigned char grub_uint8_t
;
38 #include <grub/types.h>
39 #include <grub/reed_solomon.h>
40 #include <grub/util/misc.h>
41 #include <grub/misc.h>
45 #define SECTOR_SIZE 512
46 #define MAX_BLOCK_SIZE (200 * SECTOR_SIZE)
50 typedef unsigned int grub_size_t
;
51 typedef unsigned char grub_uint8_t
;
53 #include <grub/types.h>
54 #include <grub/misc.h>
57 #define REED_SOLOMON_ATTRIBUTE __attribute__ ((regparm(3)))
59 #define REED_SOLOMON_ATTRIBUTE
62 grub_reed_solomon_recover (void *ptr_
, grub_size_t s
, grub_size_t rs
)
63 REED_SOLOMON_ATTRIBUTE
;
65 #define REED_SOLOMON_ATTRIBUTE
69 typedef grub_uint8_t gf_single_t
;
70 #define GF_POLYNOMIAL 0x1d
71 #define GF_INVERT2 0x8e
72 #if defined (STANDALONE) && !defined (TEST)
75 #define ATTRIBUTE_TEXT __attribute__ ((section("_text,_text")))
77 #define ATTRIBUTE_TEXT __attribute__ ((section(".text")))
80 static gf_single_t
* const gf_powx ATTRIBUTE_TEXT
= (void *) 0x100000;
81 static gf_single_t
* const gf_powx_inv ATTRIBUTE_TEXT
= (void *) 0x100200;
82 static int *const chosenstat ATTRIBUTE_TEXT
= (void *) 0x100300;
83 static gf_single_t
*const sigma ATTRIBUTE_TEXT
= (void *) 0x100700;
84 static gf_single_t
*const errpot ATTRIBUTE_TEXT
= (void *) 0x100800;
85 static int *const errpos ATTRIBUTE_TEXT
= (void *) 0x100900;
86 static gf_single_t
*const sy ATTRIBUTE_TEXT
= (void *) 0x100d00;
87 static gf_single_t
*const mstat ATTRIBUTE_TEXT
= (void *) 0x100e00;
88 static gf_single_t
*const errvals ATTRIBUTE_TEXT
= (void *) 0x100f00;
89 static gf_single_t
*const eqstat ATTRIBUTE_TEXT
= (void *) 0x101000;
90 /* Next available address: (void *) 0x112000. */
93 static gf_single_t gf_powx
[255 * 2];
94 static gf_single_t gf_powx_inv
[256];
95 static int chosenstat
[256];
96 static gf_single_t sigma
[256];
97 static gf_single_t errpot
[256];
98 static int errpos
[256];
99 static gf_single_t sy
[256];
100 static gf_single_t mstat
[256];
101 static gf_single_t errvals
[256];
102 static gf_single_t eqstat
[65536 + 256];
106 #pragma GCC diagnostic push
107 #pragma GCC diagnostic ignored "-Warray-bounds"
111 gf_mul (gf_single_t a
, gf_single_t b
)
113 if (a
== 0 || b
== 0)
115 return gf_powx
[(int) gf_powx_inv
[a
] + (int) gf_powx_inv
[b
]];
118 static inline gf_single_t
119 gf_invert (gf_single_t a
)
121 return gf_powx
[255 - (int) gf_powx_inv
[a
]];
128 grub_uint8_t cur
= 1;
131 for (i
= 0; i
< 255; i
++)
134 gf_powx
[i
+ 255] = cur
;
135 gf_powx_inv
[cur
] = i
;
136 if (cur
& (1ULL << (GF_SIZE
- 1)))
137 cur
= (cur
<< 1) ^ GF_POLYNOMIAL
;
144 pol_evaluate (gf_single_t
*pol
, grub_size_t degree
, int log_x
)
149 for (i
= degree
; i
>= 0; i
--)
152 s
^= gf_powx
[(int) gf_powx_inv
[pol
[i
]] + log_xn
];
154 if (log_xn
>= ((1 << GF_SIZE
) - 1))
155 log_xn
-= ((1 << GF_SIZE
) - 1);
160 #if !defined (STANDALONE)
162 rs_encode (gf_single_t
*data
, grub_size_t s
, grub_size_t rs
)
164 gf_single_t
*rs_polynomial
;
167 m
= xcalloc (s
+ rs
, sizeof (gf_single_t
));
168 grub_memcpy (m
, data
, s
* sizeof (gf_single_t
));
169 rs_polynomial
= xcalloc (rs
+ 1, sizeof (gf_single_t
));
170 rs_polynomial
[rs
] = 1;
171 /* Multiply with X - a^r */
172 for (j
= 0; j
< rs
; j
++)
174 for (i
= 0; i
< rs
; i
++)
175 if (rs_polynomial
[i
])
176 rs_polynomial
[i
] = (rs_polynomial
[i
+ 1]
177 ^ gf_powx
[j
+ (int) gf_powx_inv
[rs_polynomial
[i
]]]);
179 rs_polynomial
[i
] = rs_polynomial
[i
+ 1];
180 if (rs_polynomial
[rs
])
181 rs_polynomial
[rs
] = gf_powx
[j
+ (int) gf_powx_inv
[rs_polynomial
[rs
]]];
183 for (j
= 0; j
< s
; j
++)
186 gf_single_t f
= m
[j
];
187 for (i
= 0; i
<= rs
; i
++)
188 m
[i
+j
] ^= gf_mul (rs_polynomial
[i
], f
);
190 free (rs_polynomial
);
191 grub_memcpy (data
+ s
, m
+ s
, rs
* sizeof (gf_single_t
));
197 gauss_eliminate (gf_single_t
*eq
, int n
, int m
, int *chosen
)
201 for (i
= 0 ; i
< n
; i
++)
206 for (nzidx
= 0; nzidx
< m
&& (eq
[i
* (m
+ 1) + nzidx
] == 0);
211 r
= gf_invert (eq
[i
* (m
+ 1) + nzidx
]);
212 for (j
= 0; j
< m
+ 1; j
++)
213 eq
[i
* (m
+ 1) + j
] = gf_mul (eq
[i
* (m
+ 1) + j
], r
);
214 for (j
= i
+ 1; j
< n
; j
++)
216 gf_single_t rr
= eq
[j
* (m
+ 1) + nzidx
];
217 for (k
= 0; k
< m
+ 1; k
++)
218 eq
[j
* (m
+ 1) + k
] ^= gf_mul (eq
[i
* (m
+ 1) + k
], rr
);
224 gauss_solve (gf_single_t
*eq
, int n
, int m
, gf_single_t
*sol
)
228 for (i
= 0; i
< n
; i
++)
230 for (i
= 0; i
< m
; i
++)
232 gauss_eliminate (eq
, n
, m
, chosenstat
);
233 for (i
= n
- 1; i
>= 0; i
--)
236 if (chosenstat
[i
] == -1)
238 for (j
= 0; j
< m
; j
++)
239 s
^= gf_mul (eq
[i
* (m
+ 1) + j
], sol
[j
]);
240 s
^= eq
[i
* (m
+ 1) + m
];
241 sol
[chosenstat
[i
]] = s
;
246 rs_recover (gf_single_t
*mm
, grub_size_t s
, grub_size_t rs
)
248 grub_size_t rs2
= rs
/ 2;
252 for (i
= 0; i
< (int) rs
; i
++)
253 sy
[i
] = pol_evaluate (mm
, s
+ rs
- 1, i
);
255 for (i
= 0; i
< (int) rs
; i
++)
259 /* No error detected. */
265 for (i
= 0; i
< (int) rs2
; i
++)
266 for (j
= 0; j
< (int) rs2
+ 1; j
++)
267 eqstat
[i
* (rs2
+ 1) + j
] = sy
[i
+j
];
269 for (i
= 0; i
< (int) rs2
; i
++)
272 gauss_solve (eqstat
, rs2
, rs2
, sigma
);
275 for (i
= 0; i
< (int) (rs
+ s
); i
++)
276 if (pol_evaluate (sigma
, rs2
- 1, 255 - i
) == gf_powx
[i
])
278 errpot
[errnum
] = gf_powx
[i
];
279 errpos
[errnum
++] = s
+ rs
- i
- 1;
282 for (j
= 0; j
< errnum
; j
++)
284 eqstat
[errnum
] = sy
[0];
285 for (i
= 1; i
< (int) rs
; i
++)
287 for (j
= 0; j
< (int) errnum
; j
++)
288 eqstat
[(errnum
+ 1) * i
+ j
] = gf_mul (errpot
[j
],
289 eqstat
[(errnum
+ 1) * (i
- 1)
291 eqstat
[(errnum
+ 1) * i
+ errnum
] = sy
[i
];
294 gauss_solve (eqstat
, rs
, errnum
, errvals
);
296 for (i
= 0; i
< (int) errnum
; i
++)
297 mm
[errpos
[i
]] ^= errvals
[i
];
302 decode_block (gf_single_t
*ptr
, grub_size_t s
,
303 gf_single_t
*rptr
, grub_size_t rs
)
306 for (i
= 0; i
< SECTOR_SIZE
; i
++)
308 grub_size_t ds
= (s
+ SECTOR_SIZE
- 1 - i
) / SECTOR_SIZE
;
309 grub_size_t rr
= (rs
+ SECTOR_SIZE
- 1 - i
) / SECTOR_SIZE
;
315 for (j
= 0; j
< (int) ds
; j
++)
316 mstat
[j
] = ptr
[SECTOR_SIZE
* j
+ i
];
317 for (j
= 0; j
< (int) rr
; j
++)
318 mstat
[j
+ ds
] = rptr
[SECTOR_SIZE
* j
+ i
];
320 rs_recover (mstat
, ds
, rr
);
322 for (j
= 0; j
< (int) ds
; j
++)
323 ptr
[SECTOR_SIZE
* j
+ i
] = mstat
[j
];
328 #pragma GCC diagnostic pop
331 #if !defined (STANDALONE)
333 encode_block (gf_single_t
*ptr
, grub_size_t s
,
334 gf_single_t
*rptr
, grub_size_t rs
)
337 for (i
= 0; i
< SECTOR_SIZE
; i
++)
339 grub_size_t ds
= (s
+ SECTOR_SIZE
- 1 - i
) / SECTOR_SIZE
;
340 grub_size_t rr
= (rs
+ SECTOR_SIZE
- 1 - i
) / SECTOR_SIZE
;
346 m
= xmalloc (ds
+ rr
);
347 for (j
= 0; j
< ds
; j
++)
348 m
[j
] = ptr
[SECTOR_SIZE
* j
+ i
];
349 rs_encode (m
, ds
, rr
);
350 for (j
= 0; j
< rr
; j
++)
351 rptr
[SECTOR_SIZE
* j
+ i
] = m
[j
+ ds
];
357 #if !defined (STANDALONE)
359 grub_reed_solomon_add_redundancy (void *buffer
, grub_size_t data_size
,
360 grub_size_t redundancy
)
362 grub_size_t s
= data_size
;
363 grub_size_t rs
= redundancy
;
364 gf_single_t
*ptr
= buffer
;
365 gf_single_t
*rptr
= ptr
+ s
;
368 tmp
= xmalloc (data_size
);
369 grub_memcpy (tmp
, buffer
, data_size
);
384 if (tt
> MAX_BLOCK_SIZE
)
386 cs
= ((cs
* (MAX_BLOCK_SIZE
/ 512)) / tt
) * 512;
387 crs
= ((crs
* (MAX_BLOCK_SIZE
/ 512)) / tt
) * 512;
389 encode_block (ptr
, cs
, rptr
, crs
);
397 assert (grub_memcmp (tmp
, buffer
, data_size
) == 0);
404 void REED_SOLOMON_ATTRIBUTE
405 grub_reed_solomon_recover (void *ptr_
, grub_size_t s
, grub_size_t rs
)
407 gf_single_t
*ptr
= ptr_
;
408 gf_single_t
*rptr
= ptr
+ s
;
415 for (cptr
= rptr
+ rs
- 1; cptr
>= rptr
; cptr
--)
418 if (rptr
+ rs
- 1 - cptr
> (grub_ssize_t
) rs
/ 2)
430 if (tt
> MAX_BLOCK_SIZE
)
432 cs
= ((cs
* (MAX_BLOCK_SIZE
/ 512)) / tt
) * 512;
433 crs
= ((crs
* (MAX_BLOCK_SIZE
/ 512)) / tt
) * 512;
435 decode_block (ptr
, cs
, rptr
, crs
);
445 main (int argc
, char **argv
)
451 grub_memset (gf_powx
, 0xee, sizeof (gf_powx
));
452 grub_memset (gf_powx_inv
, 0xdd, sizeof (gf_powx_inv
));
459 in
= grub_util_fopen ("tst.bin", "rb");
462 fseek (in
, 0, SEEK_END
);
464 fseek (in
, 0, SEEK_SET
);
466 buf
= xmalloc (s
+ rs
+ SECTOR_SIZE
);
467 fread (buf
, 1, s
, in
);
470 grub_reed_solomon_add_redundancy (buf
, s
, rs
);
472 out
= grub_util_fopen ("tst_rs.bin", "wb");
473 fwrite (buf
, 1, s
+ rs
, out
);
476 out
= grub_util_fopen ("tst_rs.bin", "rb");
477 fseek (out
, 0, SEEK_END
);
479 fseek (out
, 0, SEEK_SET
);
483 buf
= xmalloc (s
+ rs
+ SECTOR_SIZE
);
484 fread (buf
, 1, s
+ rs
, out
);
488 grub_memset (buf
+ 512 * 15, 0, 512);
491 out
= grub_util_fopen ("tst_dam.bin", "wb");
492 fwrite (buf
, 1, s
+ rs
, out
);
494 grub_reed_solomon_recover (buf
, s
, rs
);
496 out
= grub_util_fopen ("tst_rec.bin", "wb");
497 fwrite (buf
, 1, s
, out
);