C part of Reed-Solomon

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-09-24 14:05:47 +02:00
parent 6d0fa83c79
commit 4f0de6881c
9 changed files with 553 additions and 23 deletions

View File

@ -244,6 +244,7 @@ program = {
common = util/grub-setup.c;
common = util/raid.c;
common = util/lvm.c;
common = util/reed_solomon.c;
sparc64_ieee1275 = util/ieee1275/ofpath.c;

View File

@ -100,6 +100,8 @@ VARIABLE(grub_install_dos_part)
.long 0xFFFFFFFF
VARIABLE(grub_install_bsd_part)
.long 0xFFFFFFFF
reed_solomon_redundancy:
.long 0
#ifdef APPLE_CC
bss_start:

View File

@ -124,9 +124,9 @@ gpt_partition_map_iterate (grub_disk_t disk,
#ifdef GRUB_UTIL
static grub_err_t
gpt_partition_map_embed (struct grub_disk *disk, unsigned int nsectors,
gpt_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
grub_embed_type_t embed_type,
grub_disk_addr_t *sectors)
grub_disk_addr_t **sectors)
{
grub_disk_addr_t start = 0, len = 0;
unsigned i;
@ -168,13 +168,17 @@ gpt_partition_map_embed (struct grub_disk *disk, unsigned int nsectors,
"This GPT partition label has no BIOS Boot Partition;"
" embedding won't be possible!");
if (len < nsectors)
if (len < *nsectors)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
"Your BIOS Boot Partition is too small;"
" embedding won't be possible!");
for (i = 0; i < nsectors; i++)
sectors[i] = start + i;
*nsectors = len;
*sectors = grub_malloc (*nsectors * sizeof (**sectors));
if (!*sectors)
return grub_errno;
for (i = 0; i < *nsectors; i++)
(*sectors)[i] = start + i;
return GRUB_ERR_NONE;
}

View File

@ -145,9 +145,9 @@ grub_partition_msdos_iterate (grub_disk_t disk,
#ifdef GRUB_UTIL
static grub_err_t
pc_partition_map_embed (struct grub_disk *disk, unsigned int nsectors,
pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
grub_embed_type_t embed_type,
grub_disk_addr_t *sectors)
grub_disk_addr_t **sectors)
{
grub_disk_addr_t end = ~0ULL;
struct grub_msdos_partition_mbr mbr;
@ -232,11 +232,15 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int nsectors,
break;
}
if (end >= nsectors + 1)
if (end >= *nsectors + 1)
{
int i;
for (i = 0; i < nsectors; i++)
sectors[i] = 1 + i;
*nsectors = end - 1;
*sectors = grub_malloc (*nsectors * sizeof (**sectors));
if (!*sectors)
return grub_errno;
for (i = 0; i < *nsectors; i++)
(*sectors)[i] = 1 + i;
return GRUB_ERR_NONE;
}
@ -245,7 +249,7 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int nsectors,
"This msdos-style partition label has no "
"post-MBR gap; embedding won't be possible!");
if (nsectors > 62)
if (*nsectors > 62)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
"Your core.img is unusually large. "
"It won't fit in the embedding area.");

View File

@ -34,11 +34,16 @@
/* The offset of GRUB_INSTALL_BSD_PART. */
#define GRUB_KERNEL_I386_PC_INSTALL_BSD_PART 0x18
/* Offset of reed_solomon_redundancy. */
#define GRUB_KERNEL_I386_PC_REED_SOLOMON_REDUNDANCY 0x1c
/* The offset of multiboot signature. */
#define GRUB_KERNEL_I386_PC_MULTIBOOT_SIGNATURE 0x1c
#define GRUB_KERNEL_I386_PC_MULTIBOOT_SIGNATURE 0x20
/* The size of the first region which won't be compressed. */
#define GRUB_KERNEL_I386_PC_RAW_SIZE 0x5D8
#define GRUB_KERNEL_I386_PC_RAW_SIZE 0x5E0
#define GRUB_KERNEL_I386_PC_NO_REED_SOLOMON_PART 0x300
/* The offset of GRUB_PREFIX. */
#define GRUB_KERNEL_I386_PC_PREFIX GRUB_KERNEL_I386_PC_RAW_SIZE

View File

@ -48,8 +48,9 @@ struct grub_partition_map
const grub_partition_t partition));
#ifdef GRUB_UTIL
/* Determine sectors available for embedding. */
grub_err_t (*embed) (struct grub_disk *disk, unsigned int nsectors,
grub_embed_type_t embed_type, grub_disk_addr_t *sectors);
grub_err_t (*embed) (struct grub_disk *disk, unsigned int *nsectors,
grub_embed_type_t embed_type,
grub_disk_addr_t **sectors);
#endif
};
typedef struct grub_partition_map *grub_partition_map_t;

View File

@ -0,0 +1,30 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRUB_REED_SOLOMON_HEADER
#define GRUB_REED_SOLOMON_HEADER 1
void
grub_reed_solomon_add_redundancy (void *buffer, grub_size_t data_size,
grub_size_t redundancy);
void
grub_reed_solomon_recover (void *buffer, grub_size_t data_size,
grub_size_t redundancy);
#endif

View File

@ -48,6 +48,7 @@
#include <assert.h>
#include <grub/emu/getroot.h>
#include "progname.h"
#include <grub/reed_solomon.h>
#define _GNU_SOURCE 1
#include <argp.h>
@ -336,9 +337,10 @@ setup (const char *dir,
grub_partition_t container = dest_dev->disk->partition;
int multiple_partmaps = 0;
grub_err_t err;
grub_disk_addr_t sectors[core_sectors];
grub_disk_addr_t *sectors;
int i;
grub_fs_t fs;
unsigned int nsec;
/* Unlike root_dev, with dest_dev we're interested in the partition map even
if dest_dev itself is a whole disk. */
@ -419,8 +421,11 @@ setup (const char *dir,
goto unable_to_embed;
}
err = dest_partmap->embed (dest_dev->disk, core_sectors,
GRUB_EMBED_PCBIOS, sectors);
nsec = core_sectors;
err = dest_partmap->embed (dest_dev->disk, &nsec,
GRUB_EMBED_PCBIOS, &sectors);
if (nsec > 2 * core_sectors)
nsec = 2 * core_sectors;
if (err)
{
@ -439,6 +444,20 @@ setup (const char *dir,
write_rootdev (core_img, root_dev, boot_img, first_sector);
core_img = realloc (core_img, nsec * GRUB_DISK_SECTOR_SIZE);
first_block = (struct grub_boot_blocklist *) (core_img
+ GRUB_DISK_SECTOR_SIZE
- sizeof (*block));
*(grub_uint32_t *) (core_img + GRUB_DISK_SECTOR_SIZE
+ GRUB_KERNEL_I386_PC_REED_SOLOMON_REDUNDANCY)
= grub_host_to_target32 (nsec * GRUB_DISK_SECTOR_SIZE - core_size);
grub_reed_solomon_add_redundancy (core_img + GRUB_KERNEL_I386_PC_NO_REED_SOLOMON_PART + GRUB_DISK_SECTOR_SIZE,
core_size - GRUB_KERNEL_I386_PC_NO_REED_SOLOMON_PART - GRUB_DISK_SECTOR_SIZE,
nsec * GRUB_DISK_SECTOR_SIZE
- core_size);
/* Make sure that the second blocklist is a terminator. */
block = first_block - 1;
block->start = 0;
@ -446,14 +465,13 @@ setup (const char *dir,
block->segment = 0;
/* Write the core image onto the disk. */
for (i = 0; i < core_sectors; i++)
for (i = 0; i < nsec; i++)
grub_disk_write (dest_dev->disk, sectors[i], 0,
(core_size - i * GRUB_DISK_SECTOR_SIZE
< GRUB_DISK_SECTOR_SIZE) ? core_size
- i * GRUB_DISK_SECTOR_SIZE
: GRUB_DISK_SECTOR_SIZE,
GRUB_DISK_SECTOR_SIZE,
core_img + i * GRUB_DISK_SECTOR_SIZE);
grub_free (sectors);
goto finish;
}
#endif

465
util/reed_solomon.c Normal file
View File

@ -0,0 +1,465 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef TEST
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef unsigned int grub_size_t;
typedef unsigned char grub_uint8_t;
typedef unsigned short grub_uint16_t;
#define xmalloc malloc
#define grub_memset memset
#define grub_memcpy memcpy
#else
#include <grub/types.h>
#include <grub/reed_solomon.h>
#include <grub/util/misc.h>
#include <grub/misc.h>
#endif
#define GF_SIZE 8
typedef grub_uint8_t gf_single_t;
typedef grub_uint16_t gf_double_t;
const gf_single_t gf_polynomial = 0x1d;
#define SECTOR_SIZE 512
#define MAX_BLOCK_SIZE (200 * SECTOR_SIZE)
static gf_single_t
gf_reduce (gf_double_t a)
{
int i;
for (i = GF_SIZE - 1; i >= 0; i--)
if (a & (1ULL << (i + GF_SIZE)))
a ^= (((gf_double_t) gf_polynomial) << i);
return a & ((1ULL << GF_SIZE) - 1);
}
static gf_single_t
gf_mul (gf_single_t a, gf_single_t b)
{
gf_double_t res = 0;
int i;
for (i = 0; i < GF_SIZE; i++)
if (b & (1 << i))
res ^= ((gf_double_t) a) << i;
return gf_reduce (res);
}
static int
bin_log2 (gf_double_t a)
{
int i = 0;
while (a)
{
a >>= 1;
i++;
}
return i - 1;
}
static gf_single_t
gf_invert (gf_single_t a)
{
/* We start with: */
/* 1 * a + 0 * p = a */
/* 0 * a + 1 * p = p */
gf_double_t x1 = 1, y1 = 0, z1 = a, x2 = 0, y2 = 1;
gf_double_t z2 = gf_polynomial | (1ULL << GF_SIZE), t;
/* invariant: z1 < z2*/
while (z1 != 0)
{
int k;
k = bin_log2 (z2) - bin_log2 (z1);
x2 ^= (x1 << k);
y2 ^= (y1 << k);
z2 ^= (z1 << k);
if (z1 >= z2)
{
t = x2;
x2 = x1;
x1 = t;
t = y2;
y2 = y1;
y1 = t;
t = z2;
z2 = z1;
z1 = t;
}
}
return gf_reduce (x2);
}
static gf_single_t
pol_evaluate (gf_single_t *pol, grub_size_t degree, gf_single_t x)
{
int i;
gf_single_t xn = 1, s = 0;
for (i = degree; i >= 0; i--)
{
s ^= gf_mul (pol[i], xn);
xn = gf_mul (x, xn);
}
return s;
}
static void
rs_encode (gf_single_t *data, grub_size_t s, grub_size_t rs)
{
gf_single_t *rs_polynomial, a = 1;
int i, j;
gf_single_t *m;
m = xmalloc ((s + rs) * sizeof (gf_single_t));
grub_memcpy (m, data, s * sizeof (gf_single_t));
grub_memset (m + s, 0, rs * sizeof (gf_single_t));
rs_polynomial = xmalloc ((rs + 1) * sizeof (gf_single_t));
grub_memset (rs_polynomial, 0, (rs + 1) * sizeof (gf_single_t));
rs_polynomial[rs] = 1;
/* Multiply with X - a^r */
for (j = 0; j < rs; j++)
{
if (a & (1 << (GF_SIZE - 1)))
{
a <<= 1;
a ^= gf_polynomial;
}
else
a <<= 1;
for (i = 0; i < rs; i++)
rs_polynomial[i] = rs_polynomial[i + 1] ^ gf_mul (a, rs_polynomial[i]);
rs_polynomial[rs] = gf_mul (a, rs_polynomial[rs]);
}
for (j = 0; j < s; j++)
if (m[j])
{
gf_single_t f = m[j];
for (i = 0; i <= rs; i++)
m[i+j] ^= gf_mul (rs_polynomial[i], f);
}
free (rs_polynomial);
grub_memcpy (data + s, m + s, rs * sizeof (gf_single_t));
free (m);
}
static void
syndroms (gf_single_t *m, grub_size_t s, grub_size_t rs,
gf_single_t *sy)
{
gf_single_t xn = 1;
int i;
for (i = 0; i < rs; i++)
{
if (xn & (1 << (GF_SIZE - 1)))
{
xn <<= 1;
xn ^= gf_polynomial;
}
else
xn <<= 1;
sy[i] = pol_evaluate (m, s + rs - 1, xn);
}
}
static void
rs_recover (gf_single_t *m, grub_size_t s, grub_size_t rs)
{
grub_size_t rs2 = rs / 2;
gf_single_t *sigma;
gf_single_t *errpot;
int *errpos;
gf_single_t *sy;
int errnum = 0;
int i, j;
sigma = xmalloc (rs2 * sizeof (gf_single_t));
errpot = xmalloc (rs2 * sizeof (gf_single_t));
errpos = xmalloc (rs2 * sizeof (int));
sy = xmalloc (rs * sizeof (gf_single_t));
syndroms (m, s, rs, sy);
{
gf_single_t *eq;
int *chosen;
eq = xmalloc (rs2 * (rs2 + 1) * sizeof (gf_single_t));
chosen = xmalloc (rs2 * sizeof (int));
for (i = 0; i < rs; i++)
if (sy[i] != 0)
break;
/* No error detected. */
if (i == rs)
return;
for (i = 0; i < rs2; i++)
for (j = 0; j < rs2 + 1; j++)
eq[i * (rs2 + 1) + j] = sy[i+j];
grub_memset (sigma, 0, rs2 * sizeof (gf_single_t));
grub_memset (chosen, -1, rs2 * sizeof (int));
for (i = 0 ; i < rs2; i++)
{
int nzidx;
int k;
gf_single_t r;
for (nzidx = 0; nzidx < rs2 && (eq[i * (rs2 + 1) + nzidx] == 0);
nzidx++);
if (nzidx == rs2)
{
break;
}
chosen[i] = nzidx;
r = gf_invert (eq[i * (rs2 + 1) + nzidx]);
for (j = 0; j < rs2 + 1; j++)
eq[i * (rs2 + 1) + j] = gf_mul (eq[i * (rs2 + 1) + j], r);
for (j = i + 1; j < rs2; j++)
{
gf_single_t rr = eq[j * (rs2 + 1) + nzidx];
for (k = 0; k < rs2 + 1; k++)
eq[j * (rs2 + 1) + k] ^= gf_mul (eq[i * (rs2 + 1) + k], rr);;
}
}
for (i = rs2 - 1; i >= 0; i--)
{
gf_single_t s = 0;
if (chosen[i] == -1)
continue;
for (j = 0; j < rs2; j++)
s ^= gf_mul (eq[i * (rs2 + 1) + j], sigma[j]);
s ^= eq[i * (rs2 + 1) + rs2];
sigma[chosen[i]] = s;
}
free (chosen);
free (eq);
}
{
gf_single_t xn = 1, xx = gf_invert (2), yn = 1;
int lp = 0;
for (i = 0; i < rs + s; i++)
{
gf_single_t ev = (gf_mul (pol_evaluate (sigma, rs2 - 1, xn), xn) ^ 1);
if (ev == 0)
{
errpot[errnum] = yn;
errpos[errnum++] = s + rs - i - 1;
}
yn = gf_mul (yn, 2);
xn = gf_mul (xn, xx);
}
}
{
gf_single_t *eq;
int *chosen;
gf_single_t *errvals;
eq = xmalloc (rs * (errnum + 1) * sizeof (gf_single_t));
chosen = xmalloc (rs * sizeof (int));
errvals = xmalloc (errnum * sizeof (int));
grub_memset (chosen, -1, rs * sizeof (int));
grub_memset (errvals, 0, errnum * sizeof (gf_single_t));
for (j = 0; j < errnum; j++)
eq[j] = errpot[j];
eq[errnum] = sy[0];
for (i = 1; i < rs; i++)
{
for (j = 0; j < errnum; j++)
eq[(errnum + 1) * i + j] = gf_mul (errpot[j],
eq[(errnum + 1) * (i - 1) + j]);
eq[(errnum + 1) * i + errnum] = sy[i];
}
for (i = 0 ; i < rs; i++)
{
int nzidx;
int k;
gf_single_t r;
for (nzidx = 0; nzidx < errnum && (eq[i * (errnum + 1) + nzidx] == 0);
nzidx++);
if (nzidx == errnum)
continue;
chosen[i] = nzidx;
r = gf_invert (eq[i * (errnum + 1) + nzidx]);
for (j = 0; j < errnum + 1; j++)
eq[i * (errnum + 1) + j] = gf_mul (eq[i * (errnum + 1) + j], r);
for (j = i + 1; j < rs; j++)
{
gf_single_t rr = eq[j * (errnum + 1) + nzidx];
for (k = 0; k < errnum + 1; k++)
eq[j * (errnum + 1) + k] ^= gf_mul (eq[i * (errnum + 1) + k], rr);
}
}
for (i = rs - 1; i >= 0; i--)
{
gf_single_t s = 0;
if (chosen[i] == -1)
continue;
for (j = 0; j < errnum; j++)
s ^= gf_mul (eq[i * (errnum + 1) + j], errvals[j]);
s ^= eq[i * (errnum + 1) + errnum];
errvals[chosen[i]] = s;
}
for (i = 0; i < errnum; i++)
m[errpos[i]] ^= errvals[i];
}
free (sy);
}
static void
decode_block (gf_single_t *ptr, grub_size_t s,
gf_single_t *rptr, grub_size_t rs)
{
grub_size_t ss;
int i, j, k;
for (i = 0; i < SECTOR_SIZE; i++)
{
grub_size_t ds = (s + SECTOR_SIZE - 1 - i) / SECTOR_SIZE;
grub_size_t rr = (rs + SECTOR_SIZE - 1 - i) / SECTOR_SIZE;
gf_single_t m[ds + rr];
for (j = 0; j < ds; j++)
m[j] = ptr[SECTOR_SIZE * j + i];
for (j = 0; j < rr; j++)
m[j + ds] = rptr[SECTOR_SIZE * j + i];
rs_recover (m, ds, rr);
for (j = 0; j < ds; j++)
ptr[SECTOR_SIZE * j + i] = m[j];
}
}
static void
encode_block (gf_single_t *ptr, grub_size_t s,
gf_single_t *rptr, grub_size_t rs)
{
grub_size_t ss;
int i, j;
for (i = 0; i < SECTOR_SIZE; i++)
{
grub_size_t ds = (s + SECTOR_SIZE - 1 - i) / SECTOR_SIZE;
grub_size_t rr = (rs + SECTOR_SIZE - 1 - i) / SECTOR_SIZE;
gf_single_t m[ds + rr];
for (j = 0; j < ds; j++)
m[j] = ptr[SECTOR_SIZE * j + i];
rs_encode (m, ds, rr);
for (j = 0; j < rr; j++)
rptr[SECTOR_SIZE * j + i] = m[j + ds];
}
}
void
grub_reed_solomon_add_redundancy (void *buffer, grub_size_t data_size,
grub_size_t redundancy)
{
grub_size_t s = data_size;
grub_size_t rs = redundancy;
gf_single_t *ptr = buffer;
gf_single_t *rptr = ptr + s;
while (s > 0)
{
grub_size_t tt;
grub_size_t cs, crs;
cs = s;
crs = rs;
tt = cs + crs;
if (tt > MAX_BLOCK_SIZE)
{
cs = (cs * MAX_BLOCK_SIZE) / tt;
crs = (crs * MAX_BLOCK_SIZE) / tt;
}
encode_block (ptr, cs, rptr, crs);
ptr += cs;
rptr += crs;
s -= cs;
rs -= crs;
}
}
void
grub_reed_solomon_recover (void *ptr, grub_size_t s, grub_size_t rs)
{
gf_single_t *rptr = ptr + s;
while (s > 0)
{
grub_size_t tt;
grub_size_t cs, crs;
cs = s;
crs = rs;
tt = cs + crs;
if (tt > MAX_BLOCK_SIZE)
{
cs = cs * MAX_BLOCK_SIZE / tt;
crs = crs * MAX_BLOCK_SIZE / tt;
}
decode_block (ptr, cs, rptr, crs);
ptr += cs;
rptr += crs;
s -= cs;
rs -= crs;
}
}
#ifdef TEST
int
main (int argc, char **argv)
{
FILE *in, *out;
grub_size_t s, rs;
char *buf;
in = fopen ("tst.bin", "rb");
if (!in)
return 1;
fseek (in, 0, SEEK_END);
s = ftell (in);
fseek (in, 0, SEEK_SET);
rs = 1024 * ((s + MAX_BLOCK_SIZE - 1) / (MAX_BLOCK_SIZE - 1024));
buf = xmalloc (s + rs + SECTOR_SIZE);
fread (buf, 1, s, in);
grub_reed_solomon_add_redundancy (buf, s, rs);
out = fopen ("tst_rs.bin", "wb");
fwrite (buf, 1, s + rs, out);
fclose (out);
grub_memset (buf + 512 * 15, 0, 512);
out = fopen ("tst_dam.bin", "wb");
fwrite (buf, 1, s + rs, out);
fclose (out);
grub_reed_solomon_recover (buf, s, rs);
out = fopen ("tst_rec.bin", "wb");
fwrite (buf, 1, s, out);
fclose (out);
return 0;
}
#endif