diff --git a/Makefile.util.def b/Makefile.util.def
index 21314e04a..5d091f7f5 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -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;
diff --git a/grub-core/kern/i386/pc/startup.S b/grub-core/kern/i386/pc/startup.S
index 46e6b1fac..01825396c 100644
--- a/grub-core/kern/i386/pc/startup.S
+++ b/grub-core/kern/i386/pc/startup.S
@@ -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:
diff --git a/grub-core/partmap/gpt.c b/grub-core/partmap/gpt.c
index c9393d932..7f2c36143 100644
--- a/grub-core/partmap/gpt.c
+++ b/grub-core/partmap/gpt.c
@@ -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;
}
diff --git a/grub-core/partmap/msdos.c b/grub-core/partmap/msdos.c
index 921e2554e..f99e27a6e 100644
--- a/grub-core/partmap/msdos.c
+++ b/grub-core/partmap/msdos.c
@@ -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.");
diff --git a/include/grub/offsets.h b/include/grub/offsets.h
index 359b32244..d1cf8720f 100644
--- a/include/grub/offsets.h
+++ b/include/grub/offsets.h
@@ -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
diff --git a/include/grub/partition.h b/include/grub/partition.h
index 7ccb7cffd..e7e00ef7f 100644
--- a/include/grub/partition.h
+++ b/include/grub/partition.h
@@ -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;
diff --git a/include/grub/reed_solomon.h b/include/grub/reed_solomon.h
new file mode 100644
index 000000000..596dff246
--- /dev/null
+++ b/include/grub/reed_solomon.h
@@ -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 .
+ */
+
+#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
diff --git a/util/grub-setup.c b/util/grub-setup.c
index 0c5470830..1518bb0a8 100644
--- a/util/grub-setup.c
+++ b/util/grub-setup.c
@@ -48,6 +48,7 @@
#include
#include
#include "progname.h"
+#include
#define _GNU_SOURCE 1
#include
@@ -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, §ors);
+ 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
diff --git a/util/reed_solomon.c b/util/reed_solomon.c
new file mode 100644
index 000000000..c20d023e7
--- /dev/null
+++ b/util/reed_solomon.c
@@ -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 .
+ */
+
+#ifdef TEST
+#include
+#include
+#include
+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
+#include
+#include
+#include
+#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