]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
C part of Reed-Solomon
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Fri, 24 Sep 2010 12:05:47 +0000 (14:05 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Fri, 24 Sep 2010 12:05:47 +0000 (14:05 +0200)
Makefile.util.def
grub-core/kern/i386/pc/startup.S
grub-core/partmap/gpt.c
grub-core/partmap/msdos.c
include/grub/offsets.h
include/grub/partition.h
include/grub/reed_solomon.h [new file with mode: 0644]
util/grub-setup.c
util/reed_solomon.c [new file with mode: 0644]

index 21314e04a078707b7b3b54018d630a2f0ab06ef4..5d091f7f5b7117467bdabf1aab004b8eb2dd5a32 100644 (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;
 
index 46e6b1fac4bcae4632da7964ec23555e25a1ad02..01825396cbb8a5ae405b45c140dbaebb561cdda3 100644 (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:
index c9393d93250e863bf693aa9bfaed59f381953609..7f2c36143bbe95ecd678a85828bce8aaa136922d 100644 (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;
 }
index 921e2554e20aa92e2360bcb144fdfc8e3d63f113..f99e27a6ecf9f36f3dd648c9f720945cc6f1718b 100644 (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.");
index 359b32244579f1687569da6fac44e0ec407d7861..d1cf8720fb69f5136c12e0c526c3fa88547d6cef 100644 (file)
 /* 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
index 7ccb7cffd056e114ada4aa1bc94fd007eb3c67b5..e7e00ef7fa6e2ac50356edf5164fd359dbae3970 100644 (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;
diff --git a/include/grub/reed_solomon.h b/include/grub/reed_solomon.h
new file mode 100644 (file)
index 0000000..596dff2
--- /dev/null
@@ -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
index 0c5470830f691020e176cc34a57616639fb9ce00..1518bb0a8bf487f0dcba2cd9e4f991ba02dbdc61 100644 (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
diff --git a/util/reed_solomon.c b/util/reed_solomon.c
new file mode 100644 (file)
index 0000000..c20d023
--- /dev/null
@@ -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