diff -Naur lilo-21.7.5/geometry.c lilo-21.7.5a/geometry.c
--- lilo-21.7.5/geometry.c	Tue Apr 17 15:38:21 2001
+++ lilo-21.7.5a/geometry.c	Tue Aug 28 10:29:45 2001
@@ -39,6 +39,18 @@
 #endif
 #endif
 
+struct lv_bmap {
+    __u32 lv_block;
+    __u16 lv_dev;
+};
+
+#ifndef LV_BMAP
+#define LV_BMAP                                _IOWR(0xfe, 0x30, 1)
+#endif
+#ifndef LVM_GET_IOP_VERSION
+#define LVM_GET_IOP_VERSION            _IOR(0xfe, 0x98, 1)
+#endif
+
 #ifndef HDIO_GETGEO
 #define HDIO_GETGEO HDIO_REQ
 #endif
@@ -278,6 +290,40 @@
 }
 
 
+void lvm_bmap(struct lv_bmap *lbm)
+{
+    DEVICE dev;
+    static int lvmfd = -1;
+    static dev_t last_dev = 0;
+
+    if (lbm->lv_dev != last_dev) {
+       char lvm_char[] = "/dev/lvm";
+       unsigned short iop;
+
+       if (lvmfd != -1)
+           close(lvmfd);
+
+       if ((lvmfd = open(lvm_char, lbm->lv_dev, O_RDONLY)) < 0)
+           die("can't open LVM char device %s\n", lvm_char);
+
+       if (ioctl(lvmfd, LVM_GET_IOP_VERSION, &iop) < 0)
+           die("LVM_GET_IOP_VERSION failed on %s\n", lvm_char);
+
+       if (iop < 10)
+           die("LVM IOP %d not supported for booting\n", iop);
+       close(lvmfd);
+
+       lvmfd = dev_open(&dev, lbm->lv_dev, O_RDONLY);
+       if (lvmfd < 0)
+           die("can't open LVM block device %#x\n", lbm->lv_dev);
+       last_dev = lbm->lv_dev;
+    }
+    if (ioctl(lvmfd, LV_BMAP, lbm) < 0) {
+       perror(__FUNCTION__);
+       pdie("LV_BMAP error or ioctl unsupported, can't have image in LVM.\n");
+    }
+}
+
 void geo_query_dev(GEOMETRY *geo,int device,int all)
 {
     DEVICE dev;
@@ -468,6 +514,34 @@
     DT_ENTRY *walk;
     int inherited,keep_cyls;
 
+    /*
+     * Find underlying device (PV) for LVM.  It is OK if the underlying PV is
+     * really an MD RAID1 device, because the geometry of the RAID1 device is
+     * exactly the same as the underlying disk, so FIBMAP and LV_BMAP will
+     * return the correct block numbers regardless of MD (I think, not tested).
+     *
+     * We do a quick test to see if the LVM LV_BMAP ioctl is working correctly.
+     * It should map the two blocks with the same difference as they were input,
+     * with a constant offset from their original block numbers.  If this is not
+     * the case then LV_BMAP is not working correctly (some widely distributed
+     * kernels did not have working LV_BMAP support, some just oops here).
+     */
+    if (MAJOR(device) == MAJOR_LVM)
+    {
+       struct lv_bmap lbmA, lbmB;
+#define DIFF 255
+
+       lbmA.lv_dev = lbmB.lv_dev = device;
+       lbmA.lv_block = 0;
+       lbmB.lv_block = DIFF;
+
+       lvm_bmap(&lbmA);
+       lvm_bmap(&lbmB);
+       if (lbmB.lv_block - lbmA.lv_block != DIFF)
+           die("This version of LVM does not support boot LVs");
+       device = geo->base_dev = lbmA.lv_dev;
+    }
+    /* Find underlying device for MD RAID */
     if (MAJOR(device) == MD_MAJOR) {
         char mdxxx[16];
 	int md_fd, pass;
@@ -574,7 +648,8 @@
     if (fstat(geo->fd,&st) < 0) die("fstat %s: %s",name,strerror(errno));
     if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
 	die("%s: neither a reg. file nor a block dev.",name);
-    geo_get(geo,S_ISREG(st.st_mode) ? st.st_dev : st.st_rdev,user_dev,1);
+    geo->dev = S_ISREG(st.st_mode) ? st.st_dev : st.st_rdev;
+    geo_get(geo, geo->dev, user_dev, 1);
     geo->file = S_ISREG(st.st_mode);
     geo->boot = 0;
 #ifndef FIGETBSZ
@@ -600,16 +675,15 @@
 int geo_open_boot(GEOMETRY *geo,char *name)
 {
     struct stat st;
-    dev_t dev;
 
     if (stat(name,&st) < 0) die("stat %s: %s",name,strerror(errno));
     if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
 	die("%s: neither a reg. file nor a block dev.",name);
-    dev = S_ISREG(st.st_mode) ? st.st_dev : st.st_rdev;
-    if (MAJOR(dev) == MAJOR_FD) geo->fd = 0;
+    geo->dev = S_ISREG(st.st_mode) ? st.st_dev : st.st_rdev;
+    if (MAJOR(geo->dev) == MAJOR_FD) geo->fd = 0;
     else if ((geo->fd = open(name,O_NOACCESS)) < 0)
 	    die("open %s: %s",name,strerror(errno));
-    geo_get(geo,dev,-1,0);
+    geo_get(geo, geo->dev, -1, 0);
     geo->file = S_ISREG(st.st_mode);
     geo->boot = 1;
     geo->spb = 1;
@@ -652,6 +726,17 @@
 	if (!block) {
 	    return 0;
 	}
+    }
+    if (MAJOR(geo->dev) == MAJOR_LVM) {
+       struct lv_bmap lbm;
+
+       lbm.lv_dev = geo->dev;
+       lbm.lv_block = block;
+
+       lvm_bmap(&lbm);
+       if (lbm.lv_dev != geo->base_dev)
+           die("LVM boot LV cannot be on multiple PVs\n");
+       block = lbm.lv_block;
     }
     sector = block*geo->spb+((offset/SECTOR_SIZE) % geo->spb);
     sector += geo->start;
diff -Naur lilo-21.7.5/geometry.h lilo-21.7.5a/geometry.h
--- lilo-21.7.5/geometry.h	Thu Apr  5 22:49:26 2001
+++ lilo-21.7.5a/geometry.h	Tue Aug 28 10:31:05 2001
@@ -16,6 +16,7 @@
     int spb; /* sectors per block */
     int fd,file;
     int boot; /* non-zero after geo_open_boot */
+    dev_t dev, base_dev; /* real device if remapping (LVM, etc) */
 } GEOMETRY;
 
 typedef struct _dt_entry {
diff -Naur lilo-21.7.5/lilo.h lilo-21.7.5a/lilo.h
--- lilo-21.7.5/lilo.h	Tue Mar 20 15:30:29 2001
+++ lilo-21.7.5a/lilo.h	Tue Aug 28 10:31:27 2001
@@ -44,6 +44,7 @@
 #define MAJOR_DAC960	48 /* First Mylex DAC960 PCI RAID controller */
 #define MAJOR_IDE5	56 /* IDE on fifth interface */
 #define MAJOR_IDE6	57 /* IDE on sixth interface */
+#define MAJOR_LVM      58 /* Logical Volume Manager block device */
 #define MAJOR_IDE7	88 /* IDE on seventh interface */
 #define MAJOR_IDE8	89 /* IDE on eighth interface */
 #define MAJOR_IDE9	90 /* IDE on ninth interface */

