~ubuntu-branches/ubuntu/feisty/lphdisk/feisty

« back to all changes in this revision

Viewing changes to lphdisk.c

  • Committer: Bazaar Package Importer
  • Author(s): Masato Taruishi
  • Date: 2001-08-27 18:13:48 UTC
  • Revision ID: james.westby@ubuntu.com-20010827181348-1zmr0z5xwjau5mgu
Tags: upstream-0.9
ImportĀ upstreamĀ versionĀ 0.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * lphdisk - Hibernation Parititon Prep Utility for laptops running Phoenix
 
3
 *           NoteBIOS.
 
4
 *
 
5
 *     Copyright 2000-2001 Patrick D. Ashmore <pda@procyon.com> and
 
6
 *                         Alex Stewart <alex@foogod.com>
 
7
 *     This software is released under the Artistic License
 
8
 *
 
9
 *     lphdisk 0.9
 
10
 */
 
11
 
 
12
/* A couple of notes:
 
13
    - Partition numbers are always passed and used base-one (i.e. the first 
 
14
      partition is partition 1, not 0).  This ensures consistency both 
 
15
      throughout the program and with convention elsewhere, reducing the risk 
 
16
      of something using a different partition than it thought it was.  This 
 
17
      does mean, however, that if you ever see something like "pi[partnum]", 
 
18
      it is probably _wrong_ (it probably should be "pi[partnum-1]").  
 
19
      Be careful!
 
20
*/
 
21
 
 
22
#include <stdio.h>
 
23
#include <stdlib.h>
 
24
#include <unistd.h>
 
25
#include <string.h>
 
26
#include <stdarg.h>
 
27
#include <getopt.h>
 
28
#include <errno.h>
 
29
#include <fcntl.h>
 
30
#include <sys/stat.h>
 
31
#include <sys/io.h>
 
32
#include "lrmi.h"
 
33
#include "vbe.h"
 
34
 
 
35
/* General Program Defines: */
 
36
 
 
37
#define SECTOR_SIZE    512     /* Bytes per sector */
 
38
#define PP_BLOCK_SIZE  2       /* Sectors per "block" in /proc/partitions */
 
39
#define MBR_SIZE       512     /* Boot record is 512-bytes long */
 
40
#define TABLE_START    0x1BE   /* Start of partition table in MBR */
 
41
#define TABLE_MAGIC    0x1FE   /* Start of "magic number" in MBR */
 
42
#define TABLE_ENTRY    16      /* Length of a single partition table entry */
 
43
#define PART_TYPE      4       /* Position of partition type byte */
 
44
#define PART_START     8       /* Position of start sector (32-bit value) */
 
45
#define PART_SIZE      12      /* Position of partition size (32-bit value) */
 
46
#define MAGIC_ID       0xAA55  /* "magic number" (little-endian reversed) */
 
47
#define HIBERNATION_ID 0xA0    /* NoteBIOS Hibernation partition ID */
 
48
#define EXTENDED_ID    0x05    /* ID for an "extended partition" area */
 
49
#define PROBE_PADDING  2048    /* Add 2MB "padding" to probed requirements */
 
50
 
 
51
/* Error Exit Codes: */
 
52
 
 
53
#define ERR_USAGE      1       /* User requested usage message */
 
54
#define ERR_BADARGS    2       /* User entered bad command arguments */
 
55
#define ERR_OPEN       3       /* Open of device failed */
 
56
#define ERR_STAT       4       /* Statting open device failed */
 
57
#define ERR_BADFILE    5       /* Bad file type */
 
58
#define ERR_READ       6       /* Read error */
 
59
#define ERR_TABLE      7       /* Error reading/checking partition table */
 
60
#define ERR_FINDPART   8       /* Problem determining which partition to use */
 
61
#define ERR_WRITE      9       /* Problem formatting hibernate partition */
 
62
#define ERR_CANTPROBE  10      /* Asked to probe only, but can't obtain info */
 
63
 
 
64
/* Structs and Type Declarations: */
 
65
 
 
66
typedef struct {
 
67
  int     type;                /* Partition type ID */
 
68
  size_t  start;               /* Start sector of partition */
 
69
  size_t  size;                /* Length of partition in sectors */
 
70
} partinfo;
 
71
 
 
72
/* Global Variables: */
 
73
 
 
74
const char *version_string =
 
75
  "lphdisk 0.9, by Patrick D. Ashmore and Alex Stewart";
 
76
 
 
77
const char *pp_filename      = "/proc/partitions";
 
78
const char *mtrr_filename    = "/proc/mtrr";
 
79
const char *meminfo_filename = "/proc/meminfo";
 
80
 
 
81
int force_flag = 0;            /* "force" is off by default */
 
82
int quiet_flag = 0;            /* Be noisy by default */
 
83
int debug_flag = 0;            /* Debugging messages off by default */
 
84
int write_flag = 1;            /* Do actually write things, though */
 
85
int probeonly_flag = 0;        /* Continue after probing by default */
 
86
 
 
87
char phasers[] = "stun";       /* We come in peace! (Shoot to kill!) */
 
88
 
 
89
/* The header, with sector and checksum values set to 0, looks like this:    */
 
90
/*                      __ __                            __ __ __ __         */
 
91
/*   54 69 6D 4F  00 00 00 00  00 00 00 00  02 00 00 00  00 00 00 00         */
 
92
/*                    Checksum                         Size in Sectors       */
 
93
 
 
94
const unsigned char header_base[] = {   /* first 16 bytes of an empty header */
 
95
  0x54, 0x69, 0x6D, 0x4F,   0x00, 0x00, 0x00, 0x00,
 
96
  0x00, 0x00, 0x00, 0x00,   0x02, 0x00, 0x00, 0x00,
 
97
};
 
98
 
 
99
/*****************************************************************************/
 
100
/*                      General Purpose Utility Routines                     */
 
101
/*****************************************************************************/
 
102
 
 
103
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
104
/* The following is a hack to take advantage of the ext2 "_llseek" system    */
 
105
/* call to do seeks to "long long" offsets under linux (this is needed to    */
 
106
/* seek to sectors beyond 4194303 (2GB)).  This isn't directly supported by  */
 
107
/* glibc, so we need to make our own interface function for it.  We should   */
 
108
/* be able to get the _NR__llseek define from linux/unistd.h.  From this we  */
 
109
/* can construct a wrapper to perform the right system call.                 */
 
110
 
 
111
#include <linux/unistd.h>       /* for __NR__llseek */
 
112
 
 
113
typedef long long lloff_t;
 
114
 
 
115
#ifdef __NR__llseek
 
116
 
 
117
static _syscall5(int,_llseek, unsigned int,fd, unsigned long,offset_high,
 
118
                 unsigned long,offset_low, lloff_t *,result,
 
119
                 unsigned int,origin)
 
120
 
 
121
lloff_t llseek (unsigned int fd, lloff_t offset, unsigned int origin) {
 
122
  lloff_t result;
 
123
  int retval;
 
124
 
 
125
  retval = _llseek (fd, ((unsigned long long) offset) >> 32,
 
126
                   ((unsigned long long) offset) & 0xffffffff,
 
127
                   &result, origin);
 
128
  return (retval == -1 ? (lloff_t) retval : result);
 
129
}
 
130
 
 
131
#else /* __NR__llseek */
 
132
 
 
133
/* Somehow, __NR__llseek wasn't in linux/unistd.h.  This shouldn't ever      */
 
134
/* happen, but better safe than sorry.. The best we can do is emulate it     */
 
135
/* with lseek, and hope we don't get an offset that's too large (throw an    */
 
136
/* error if we do)                                                           */
 
137
 
 
138
lloff_t llseek (unsigned int fd, lloff_t offset, unsigned int origin) {
 
139
  off_t offt_offset = (off_t) offset;
 
140
 
 
141
  if ((lloff_t)offt_offset != offset) {
 
142
    /* converting to off_t and back yields different result, indicating an */
 
143
    /* overflow.. */
 
144
    errno = EINVAL;
 
145
    return -1;
 
146
  } else {
 
147
    return lseek(fd, offt_offset, origin);
 
148
  }
 
149
}
 
150
 
 
151
#endif /* __NR__llseek */
 
152
 
 
153
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
154
 
 
155
#define get16(p) get_int_le(p,2)
 
156
#define get32(p) get_int_le(p,4)
 
157
 
 
158
/* get_int_le(): Pull an integer in little-endian format from 'buf'.        */
 
159
/* 'numbytes' should be 2 or 4, for an 16-bit or 32-bit value.  Returns the */
 
160
/* value read.  Generally called using the get16 and get32 macros above.    */
 
161
 
 
162
int get_int_le (const unsigned char *buf, int numbytes) {
 
163
  int value = 0;
 
164
  int i;
 
165
 
 
166
  for (i=numbytes-1; i>=0; i--) {
 
167
    value = (value << 8) + buf[i];
 
168
  }
 
169
  return value;
 
170
}
 
171
 
 
172
#define put16(p,v) put_int_le(p,2,v)
 
173
#define put32(p,v) put_int_le(p,4,v)
 
174
 
 
175
/* put_int_le(): Insert 'value' into 'buf' in little-endian format. */
 
176
/* 'numbytes' should be 2 or 4, for an 16-bit or 32-bit value.      */
 
177
/* Generally called using the put16 and put32 macros above.         */
 
178
 
 
179
void put_int_le (unsigned char *buf, int numbytes, int value) {
 
180
  int i;
 
181
 
 
182
  for (i=numbytes-1; i>=0; i--) {
 
183
    buf[i] = value & 0xFF;
 
184
    value = value >> 8;
 
185
  }
 
186
}
 
187
 
 
188
/* seek_sector(): Seek to a particular sector on the disk.  Returns zero on */
 
189
/* success, nonzero on error. */
 
190
 
 
191
int seek_sector (int fd, size_t secno) {
 
192
  lloff_t offset = (lloff_t) secno * SECTOR_SIZE;
 
193
 
 
194
  if (llseek(fd, offset, SEEK_SET) == (lloff_t) -1)
 
195
    return -1;
 
196
 
 
197
  return 0;
 
198
}
 
199
 
 
200
/*****************************************************************************/
 
201
/*                         lphdisk-specific Functions                        */
 
202
/*****************************************************************************/
 
203
 
 
204
/* debug(): Write a debugging message (if debug_flag is set).  Arguments are */
 
205
/* the same as for printf.                                                   */
 
206
 
 
207
void debug (char *fmt, ...) {
 
208
  va_list ap;
 
209
 
 
210
  va_start(ap, fmt);
 
211
  if (debug_flag) vprintf(fmt, ap);
 
212
  va_end(ap);
 
213
}
 
214
 
 
215
/* do_write(): Write 'count' bytes from 'buf' to file 'fd'. Returns number */
 
216
/* of bytes written. (this is a wrapper for the write() system call which  */
 
217
/* simply checks write_flag first, to support the -n command line option)  */
 
218
 
 
219
ssize_t do_write (int fd, const void *buf, size_t count) {
 
220
//static FILE *f = 0;
 
221
 
 
222
  if (write_flag) {
 
223
//if (f == 0) f = fdopen(fd, "a");
 
224
//return fwrite(buf, 1, count, f);
 
225
    return write(fd, buf, count);
 
226
  } else {
 
227
    return count;
 
228
  }
 
229
}
 
230
 
 
231
/* read_mbr(): Read the Master Boot Record from sector 0 of 'fd' and store */
 
232
/* it in 'buf'.  'buf' must point to a buffer at least MBR_SIZE.  Returns  */
 
233
/* nonzero on error.                                                       */
 
234
 
 
235
int read_mbr (int fd, unsigned char *buf) {
 
236
  if (seek_sector(fd, 0) ||
 
237
     (read(fd, buf, MBR_SIZE) != MBR_SIZE)) {
 
238
    if (errno) {
 
239
      perror("read_mbr");
 
240
    } else {
 
241
      fprintf(stderr, "read_mbr: short read\n");
 
242
    }
 
243
    return 1;
 
244
  }
 
245
 
 
246
  return 0;
 
247
}
 
248
 
 
249
/* parse_table(): Take an MBR in 'buf', parse the important bits of the      */
 
250
/* primary partition table, and store them in 'pi' (an array of 4 partinfo   */
 
251
/* structs, one for each primary partition).  Performs basic sanity checking */
 
252
/* on what it finds.  Returns nonzero on error.                              */
 
253
 
 
254
int parse_table (const unsigned char *buf, partinfo *pi) {
 
255
  int i, i_start, i_end;
 
256
  int j, j_start, j_end;
 
257
  const unsigned char *pos;
 
258
 
 
259
  /* Do some minimal sanity checking by verifying that the sector we've */
 
260
  /* read has the right (two-byte) magic number at the end of it for a  */
 
261
  /* partition table */
 
262
 
 
263
  if (get16(buf + TABLE_MAGIC) != MAGIC_ID) {
 
264
    debug("magic number=%04X\n", get16(buf + TABLE_MAGIC));
 
265
    fprintf(stderr, "parse_table: Invalid partition table magic number!\n");
 
266
    return 1;
 
267
  }
 
268
 
 
269
  /* Parse the important bits of the 4 primary partitions in the MBR */
 
270
 
 
271
  debug("Parsing Partition table:\n");
 
272
  for (i=1; i<5; i++) {
 
273
    pos = buf + TABLE_START + ((i-1) * TABLE_ENTRY);
 
274
    pi[i-1].type  = *(pos + PART_TYPE);
 
275
    pi[i-1].start = get32(pos + PART_START);
 
276
    pi[i-1].size  = get32(pos + PART_SIZE);
 
277
    debug("  %d: type=%02x, start=%u, size=%u\n", i, pi[i-1].type,
 
278
          pi[i-1].start, pi[i-1].size);
 
279
 
 
280
    /* Also sanity-check the partition's allocation on the disk.. */
 
281
 
 
282
    if (pi[i-1].type) {
 
283
      i_start = pi[i-1].start;
 
284
      i_end = i_start + pi[i-1].size;
 
285
      for (j=0; j<i; j++) {
 
286
        j_start = pi[j-1].start;
 
287
        j_end = j_start + pi[j-1].size;
 
288
        if (pi[j-1].type &&
 
289
            (((i_start >= j_start) && (i_start < j_end)) ||
 
290
             ((j_start >= i_start) && (j_start < i_end)))) {
 
291
          fprintf(stderr, "parse_table: Partition %d overlaps with"
 
292
                  " partition %d!\n", i, j);
 
293
          return 1;
 
294
        }
 
295
      }
 
296
    }
 
297
  }
 
298
 
 
299
  return 0;
 
300
}
 
301
 
 
302
/* check_proc_partitions(): Take parsed partition information for a device  */
 
303
/* (in 'pi' array) and attempt to correlate it with what the kernel reports */
 
304
/* in /proc/partitions for that device.  'dev' indicates the major/minor    */
 
305
/* numbers for the device that 'pi' contains info for.  Returns nonzero on  */
 
306
/* error. */
 
307
 
 
308
int check_proc_partitions (dev_t dev, const partinfo *pi) {
 
309
  FILE *f;
 
310
  int devmajor = major(dev);
 
311
  int devminor = minor(dev);
 
312
  int checked[4] = {0,0,0,0};
 
313
  int result, major, minor, size, partition;
 
314
  int i;
 
315
 
 
316
  if (!(f = fopen(pp_filename, "r"))) {
 
317
    fprintf(stderr, "Unable to open %s: %s\n", pp_filename, strerror(errno));
 
318
    return 1;
 
319
  }
 
320
  
 
321
  fscanf(f, "%*[^\n]\n");             /* Read the header line and discard it */
 
322
 
 
323
  while (result != EOF) {
 
324
    result = fscanf(f, "%d %d %d %*[^\n]\n", &major, &minor, &size);
 
325
    if (result == 3 && major == devmajor) {
 
326
      if (minor > devminor && minor < devminor+5) {
 
327
 
 
328
        /* Found one within the first four partitions of our disk.. */
 
329
 
 
330
        partition = minor - devminor;
 
331
 
 
332
        if (!(pi[partition-1].type)) {
 
333
          debug("Warning: Partition %d is listed in %s, but is not present in "
 
334
                "partition table!\n", partition, pp_filename);
 
335
          return 1;
 
336
        }
 
337
 
 
338
        if (size != (pi[partition-1].size / PP_BLOCK_SIZE)) {
 
339
 
 
340
          /* Sizes don't match! */
 
341
 
 
342
          if (pi[partition-1].type == EXTENDED_ID) {
 
343
 
 
344
            /* It's ok, the extended partition doesn't have an accurate size */
 
345
            /* listed in /proc/partitions, so we can't expect it to match.   */
 
346
 
 
347
          } else {
 
348
            debug("Warning: Size of partition %d in partition table (%u) does"
 
349
                  " not match size listed in %s (%u)!\n", partition,
 
350
                  pi[partition-1].size, pp_filename, size * PP_BLOCK_SIZE);
 
351
            return 1;
 
352
          }
 
353
        }
 
354
 
 
355
        /* Everything checks out for this partition.. */
 
356
 
 
357
        checked[partition-1] = 1;
 
358
      }
 
359
    }
 
360
  }
 
361
 
 
362
  for (i=1; i<5; i++) {
 
363
    if (pi[i-1].type && !checked[i-1]) {
 
364
 
 
365
      /* Found a partition in the table that isn't in /proc/partitions! */
 
366
 
 
367
      if (pi[i-1].type == HIBERNATION_ID) {
 
368
 
 
369
        /* We'll make an exception if the (apparently newly created)         */
 
370
        /* partition is the hibernation partition and there's nothing else   */
 
371
        /* odd about the disk (if the user just created it, and we're not in */
 
372
        /* danger of clobbering some other partition, it'll save on reboots  */
 
373
        /* if they can run this immediately afterward, and it should be      */
 
374
        /* safe). */
 
375
 
 
376
        debug("Hibernation partition %d is not listed in %s.\n", i,
 
377
              pp_filename);
 
378
      } else {
 
379
        debug("Partition %d is listed in partition table, but is not"
 
380
              " present in %s!\n", i, pp_filename);
 
381
        return 1;
 
382
      }
 
383
    }
 
384
  }
 
385
 
 
386
  /* All systems are go! */
 
387
  return 0;
 
388
}
 
389
 
 
390
/* seek_a0(): Given a partinfo array ('pi'), find the partition with the  */
 
391
/* right ID for a hibernation partition.  Returns the partition number if */
 
392
/* found, 0 if not found, or -1 if more than one partition matches.       */
 
393
 
 
394
int seek_a0 (const partinfo *pi) { /* FUNCTION - find a0 partition */
 
395
  int i;
 
396
  int partition = 0;
 
397
 
 
398
  for (i=1; i<5; i++) {
 
399
    if (pi[i-1].type && (pi[i-1].type == HIBERNATION_ID)) {
 
400
      debug("Hibernation partition found on partition %d\n", i+1);
 
401
      if (partition == 0) {
 
402
        partition = i;
 
403
      } else {
 
404
        partition = -1;
 
405
      }
 
406
    }
 
407
  }
 
408
 
 
409
  return partition;  
 
410
}
 
411
 
 
412
/* create_header(): Given a buffer of size SECTOR_SIZE in 'buf', create a */
 
413
/* NoteBIOS hibernation header sector appropriate for a hibernation       */
 
414
/* partition of size 'partsize'.                                          */
 
415
 
 
416
void create_header (unsigned char *buf, size_t partsize) {
 
417
  unsigned int word;                    /* byte pair for checksum addition */
 
418
  unsigned int checksum;                /* running checksum total */
 
419
  int i;                                /* general counter variable */
 
420
 
 
421
  memcpy(buf, header_base, 16);         /* Start off with the base header */
 
422
 
 
423
  buf[16] = partsize & 0xFF;         /* first byte in sector size */
 
424
  buf[17] = (partsize >> 8) & 0xFF;  /* second byte in sector size */
 
425
  buf[18] = (partsize >> 16) & 0xFF; /* third byte in sector size */
 
426
  buf[19] = (partsize >> 24) & 0xFF; /* fourth byte in sector size */
 
427
 
 
428
  debug("create_header: read_sz_b0: %02X\n", buf[16]);
 
429
  debug("create_header: read_sz_b1: %02X\n", buf[17]);
 
430
  debug("create_header: read_sz_b2: %02X\n", buf[18]);
 
431
  debug("create_header: read_sz_b3: %02X\n", buf[19]);
 
432
  debug("create_header: Bytes for total sectors: %08X\n", partsize);
 
433
 
 
434
  for (i = 20; i < SECTOR_SIZE; i++) buf[i] = 0xFF; /* header filler with FFs */
 
435
 
 
436
  checksum = 0;
 
437
  for (i = SECTOR_SIZE-2; i > 6; i -= 2) {      /* compute the checksum */
 
438
    
 
439
    word = get16(buf+i); 
 
440
    checksum = checksum + word;                 /* add word to checksum */
 
441
 
 
442
    debug("create_header: i=%03d W=%04X CS=%08X %08X %08X\n",
 
443
      i, word, checksum, -checksum, ~checksum);
 
444
 
 
445
  }
 
446
 
 
447
  checksum = ~checksum;                 /* invert checksum */
 
448
  buf[6] = checksum & 0xFF;             /* significant high byte of checksum */
 
449
  buf[7] = (checksum >> 8) & 0xFF;      /* significant low byte of checksum */
 
450
 
 
451
  debug("create_header: Bytes (in order) to insert in checksum spot:");
 
452
  debug(" %02X %02X\n", buf[6], buf[7]);
 
453
}
 
454
 
 
455
/* do_format(): Actually do the formatting of a hibernate partition.  'fd' */
 
456
/* is a descriptor for the "master" disk device (_not_ the partition       */
 
457
/* device), and 'pinfo' contains the partinfo struct for the appropriate   */
 
458
/* partition to use.  Returns nonzero on failure.                          */
 
459
 
 
460
int do_format (int fd, partinfo pinfo) { 
 
461
  int i;
 
462
  unsigned char *buf;
 
463
  unsigned int start_sector = pinfo.start;
 
464
  unsigned int partsize = pinfo.size;
 
465
 
 
466
  buf = malloc(SECTOR_SIZE);
 
467
  if (!buf) {
 
468
    fprintf(stderr, "do_format: memory allocation failed!\n");
 
469
    return 1;
 
470
  }
 
471
 
 
472
  create_header(buf, partsize);
 
473
 
 
474
/* debug section */
 
475
  if (debug_flag) {
 
476
    char *spacer;                       /* debug output beautifier :) */
 
477
    debug("do_format: Partition header:\ndo_format: ");
 
478
    for (i=0; i<512; i++) {                             
 
479
 
 
480
      if ((i+1) % 16 == 0) {
 
481
         spacer = "\ndo_format: ";
 
482
      } else {
 
483
        if ((i+1) % 8 == 0) {
 
484
          spacer = "   ";
 
485
        } else if ((i+1) % 4 == 0) {
 
486
          spacer = "  ";
 
487
        } else {
 
488
          spacer = " ";
 
489
        }
 
490
      }
 
491
 
 
492
      debug("%02X%s", buf[i], spacer);
 
493
    }
 
494
    debug("End of header...\n");
 
495
  }
 
496
/* end debug section */
 
497
 
 
498
  debug("seeking to sector %u...\n", start_sector);
 
499
  seek_sector(fd, start_sector);       /* position to beginning of partition */
 
500
 
 
501
  /* Write two copies of the header sector */
 
502
  if ((do_write(fd, buf, SECTOR_SIZE) != SECTOR_SIZE) ||
 
503
      (do_write(fd, buf, SECTOR_SIZE) != SECTOR_SIZE)) {
 
504
    perror("do_format(header)");
 
505
    return 1;
 
506
  }
 
507
 
 
508
  for (i = 0; i < SECTOR_SIZE; i++) buf[i] = 0x50;
 
509
 
 
510
  /* Write partsize-2 "blank" sectors */
 
511
  for (i = 3; i <= partsize; i++) {
 
512
    if (do_write(fd, buf, SECTOR_SIZE) != SECTOR_SIZE) {
 
513
      perror("do_format");
 
514
      return 1;
 
515
    }
 
516
                                /* only update output every 50 sectors */
 
517
    if (((i % 50) == 0) || (i == partsize)) {
 
518
      if (quiet_flag == 0) {
 
519
        printf("Formatting sector %d of %u.", i, partsize);
 
520
        printf(" (sectors of %d bytes)\r", SECTOR_SIZE);
 
521
      }
 
522
    }
 
523
  }
 
524
 
 
525
  return 0;
 
526
}
 
527
 
 
528
/* mtrr_physmem(): Use /proc/mtrr to attempt to determine the amount of   */
 
529
/* physical RAM in the system.  Returns the size of RAM (in KB) indicated */
 
530
/* by /proc/mtrr, or zero if it could not determine an appropriate value. */
 
531
 
 
532
int mtrr_physmem(void) {
 
533
  FILE *f;
 
534
  int base, size;
 
535
 
 
536
  if (!(f = fopen(mtrr_filename, "r"))) {
 
537
    debug("Unable to open %s: %s\n", mtrr_filename, strerror(errno));
 
538
    return 0;
 
539
  }
 
540
  if ((fscanf(f, "reg%*d: base=0x%*x (%dMB), size=%dMB", &base, &size) != 2) ||
 
541
      (base != 0)) {
 
542
    debug("Parse of %s failed.\n", mtrr_filename);
 
543
    return 0;
 
544
  }
 
545
  fclose(f);
 
546
 
 
547
  size *= 1024;
 
548
  debug("%s reports main RAM as %d KB\n", mtrr_filename, size);
 
549
 
 
550
  return size;
 
551
}
 
552
 
 
553
/* meminfo_physmem(): Use /proc/meminfo to attempt to determine the amount   */
 
554
/* of physical RAM in the system.  Returns the size of RAM (in KB) indicated */
 
555
/* by /proc/meminfo, or zero if it could not determine an appropriate value. */
 
556
 
 
557
int meminfo_physmem(void) {
 
558
  FILE *f;
 
559
  unsigned int size;
 
560
  int ramsize;
 
561
 
 
562
  if (!(f = fopen(meminfo_filename, "r"))) {
 
563
    debug("Unable to open %s: %s\n", meminfo_filename, strerror(errno));
 
564
    return 0;
 
565
  }
 
566
  fscanf(f, "%*[^\n]\n");             /* Read the header line and discard it */
 
567
 
 
568
  if (fscanf(f, "Mem: %u", &size) != 1) {
 
569
    debug("Parse of %s failed.\n", meminfo_filename);
 
570
    return 0;
 
571
  }
 
572
  fclose(f);
 
573
 
 
574
  /* convert to KB and then round up to the next power of 2 (since RAM */
 
575
  /* sizes don't come in anything else, so this should correct for the */
 
576
  /* kernel size, etc)                                                 */
 
577
  size >>= 10;
 
578
  debug("%s reports memory size of %d KB", meminfo_filename, size);
 
579
  for (ramsize = 1; size; size >>= 1) ramsize <<= 1;
 
580
 
 
581
  debug(" (adjusted=%d)\n", ramsize);
 
582
 
 
583
  return ramsize;
 
584
}
 
585
 
 
586
/* get_physmem(): Use all available methods to get a best guess of the     */
 
587
/* amount of physical RAM in the system.  Returns the size of RAM (in KB), */
 
588
/* or zero if it could not determine an appropriate value.                 */
 
589
 
 
590
int get_physmem(void) {
 
591
  int mtrr_size, meminfo_size;
 
592
 
 
593
  /* First try /proc/mtrr, as this gives us actual physical memory on most */
 
594
  /* systems.  This info won't exist on earlier kernels or if the kernel   */
 
595
  /* wasn't compiled with it enabled, though. (this can also give rather   */
 
596
  /* wonky info on non-IA32 systems, or possibly really odd IA32 ones, but */
 
597
  /* it's very unlikely we'll find a laptop with one of these              */
 
598
  /* configurations, especially one with a NoteBIOS in it)                 */
 
599
 
 
600
  mtrr_size = mtrr_physmem();
 
601
 
 
602
  /* Now try /proc/meminfo.  This is the total usable memory reported by the */
 
603
  /* kernel, and should always be available, but this can be reduced by the  */
 
604
  /* kernel's size and internal allocations, as well as being overridable by */
 
605
  /* boot parameters, etc.  The meminfo_physmem() routine attempts to        */
 
606
  /* compensate for the space used by the kernel, but can't do anything      */
 
607
  /* about boot parameters or some other things that can change this value.  */
 
608
 
 
609
  meminfo_size = meminfo_physmem();
 
610
 
 
611
  if (mtrr_size >= meminfo_size) {
 
612
    debug("get_physmem: RAM size is %d KB\n", mtrr_size);
 
613
    return mtrr_size;
 
614
  } else {
 
615
    debug("get_physmem: RAM size is %d KB\n", meminfo_size);
 
616
    return meminfo_size;
 
617
  }
 
618
}
 
619
 
 
620
/* vesa_videomem(): Use VESA BIOS Extension calls to attempt to query the  */
 
621
/* amount of RAM on the video card (this should work on almost all modern  */
 
622
/* models).  This routine uses a copy of Josh Vanderhoof's LRMI library    */
 
623
/* taken from svgalib to perform the appropriate BIOS interrupts.  Returns */
 
624
/* the amount of video RAM detected (in KB) or zero if it was unable to    */
 
625
/* obtain a number.                                                        */
 
626
 
 
627
int vesa_videomem(void) {
 
628
  struct LRMI_regs r;
 
629
  struct vbe_info_block *info;
 
630
  int video_mem;
 
631
 
 
632
  /* (we do this first so we can catch the case of the user not being root  */
 
633
  /* (and quietly handle it with a debug message) before LRMI_init fails to */
 
634
  /* open /dev/mem and prints an error message (since the user not being    */
 
635
  /* root is not technically an error, it just reduces some of the          */
 
636
  /* functionality we can provide)                                          */
 
637
  /* Allow read/write to all IO ports: */
 
638
 
 
639
  if (iopl(3) == -1) {
 
640
    debug("Can't set privilege level for VESA probe: %s\n", strerror(errno));
 
641
    return 0;
 
642
  }
 
643
 
 
644
  if (!LRMI_init()) {
 
645
    debug("LRMI initialization failed.\n");
 
646
    iopl(0);
 
647
    return 0;
 
648
  }
 
649
 
 
650
  info = LRMI_alloc_real(sizeof(struct vbe_info_block));
 
651
 
 
652
  if (info == NULL) {
 
653
    debug("vesa_videomem: can't alloc real mode memory.\n");
 
654
    iopl(0);
 
655
    return 0;
 
656
  }
 
657
 
 
658
  memcpy(info->vbe_signature, "VBE2", 4);
 
659
 
 
660
  memset(&r, 0, sizeof(r));
 
661
  r.eax = 0x4f00;
 
662
  r.es = (unsigned int)info >> 4;
 
663
  r.edi = 0;
 
664
 
 
665
  if (!LRMI_int(0x10, &r)) {
 
666
    debug("Can't get VESA info (vm86 failure).\n");
 
667
    iopl(0);
 
668
    return 0;
 
669
  }
 
670
 
 
671
  /* Set privilege level back to normal now that we're done with it */
 
672
  iopl(0);
 
673
  
 
674
  if ((r.eax & 0xffff) != 0x4f ||
 
675
      strncmp(info->vbe_signature, "VESA", 4) != 0) {
 
676
    debug("No VESA bios.\n");
 
677
    return 0;
 
678
  }
 
679
 
 
680
  debug("VBE Version %x.%x\n", (int)(info->vbe_version >> 8) & 0xff,
 
681
        (int)info->vbe_version & 0xff);
 
682
 
 
683
  debug("Video card identified as: %s\n",
 
684
        (char *)(info->oem_string_seg * 16 + info->oem_string_off));
 
685
 
 
686
  /* The VESA BIOS call returns memory in 64KB units.  Multiply to get KB.. */
 
687
 
 
688
  video_mem = (int)(info->total_memory) * 64;
 
689
 
 
690
  debug("Total video memory: %u KB\n", video_mem);
 
691
 
 
692
  LRMI_free_real(info);
 
693
 
 
694
  return video_mem;
 
695
}
 
696
 
 
697
/* get_videomem(): Use all available methods to get a best guess of the     */
 
698
/* amount of RAM in the video card.  Returns the size of RAM (in KB),       */
 
699
/* or zero if it could not determine an appropriate value.                  */
 
700
/* (currently vesa_videomem() is the only method, so this is just a wrapper */
 
701
/* define)                                                                  */
 
702
 
 
703
#define get_videomem() vesa_videomem()
 
704
 
 
705
/*****************************************************************************/
 
706
/*                                Main Program                               */
 
707
/*****************************************************************************/
 
708
 
 
709
char *argv0;
 
710
 
 
711
const char short_opts[] = "hpqdnf";
 
712
const struct option long_opts[] = {
 
713
  {"help",      0, 0, 'h'},
 
714
  {"probeonly", 0, 0, 'p'},
 
715
  {"quiet",     0, 0, 'q'},
 
716
  {"debug",     0, 0, 'd'},
 
717
  {"nowrite",   0, 0, 'n'},
 
718
  {"force",     0, 0, 'f'},
 
719
{0,0,0,0}};
 
720
 
 
721
const char usage_string[] = "\
 
722
Usage: %1$s [options] [device]
 
723
Prepare a hibernation partition for APM suspend-to-disk.
 
724
 
 
725
options:
 
726
  -h, --help       Display brief usage and option information (this screen)
 
727
  -p, --probeonly  Only calculate and display required size, do not format
 
728
  -q, --quiet      Turn off informational messages, useful for scripts
 
729
  -d, --debug      Turn on (verbose) debugging messages
 
730
  -n, --nowrite    Do not actually write to the disk
 
731
  -f, --force      **DANGEROUS**  Format without regard to potential problems
 
732
 
 
733
'device' should be a raw disk device (not a partition).  The default device
 
734
is /dev/hda.
 
735
 
 
736
(%2$s)\n\n";
 
737
 
 
738
void print_usage (void) {
 
739
  char *progname = rindex(argv0, '/');
 
740
  progname = progname ? progname+1 : argv0;
 
741
  printf(usage_string, progname, version_string);
 
742
}
 
743
 
 
744
int main (int argc, char **argv) {      /* MAIN FUNCTION - the beast */
 
745
  char drive[FILENAME_MAX] = "/dev/hda";/* Default to /dev/hda */
 
746
  int fd;
 
747
  struct stat st;
 
748
  unsigned char mbr_buf[MBR_SIZE];
 
749
  partinfo pi[4];
 
750
  dev_t dev;
 
751
  int partition;
 
752
  int ramsize, vramsize, required_size;
 
753
  size_t required_sectors;
 
754
 
 
755
  argv0 = argv[0];
 
756
 
 
757
  while (1) {                           /* loop to parse command line args */
 
758
    int c;                              /* current getopt option */
 
759
 
 
760
    c = getopt_long (argc, argv, short_opts, long_opts, 0);
 
761
    if (c == -1) break;                 /* break if error */
 
762
 
 
763
    switch (c) {                        /* case switch for options */
 
764
      case 'h':                         /* case h - help */
 
765
        print_usage();                  /* print help information */
 
766
        return ERR_USAGE;
 
767
 
 
768
      case 'p':                         /* case p - probeonly */
 
769
        probeonly_flag = 1;
 
770
        break;
 
771
 
 
772
      case 'q':                         /* case q - be quiet! */
 
773
        quiet_flag = 1;                 /* set quiet flag */
 
774
        break;
 
775
 
 
776
      case 'd':                         /* case d - debug on */
 
777
        debug_flag = 1;
 
778
        break;
 
779
 
 
780
      case 'n':                         /* case n - no write */
 
781
        write_flag = 0;
 
782
        break;
 
783
 
 
784
      case 'f':                         /* case f - force */
 
785
        force_flag = 1;                 /* use the force */
 
786
        break;
 
787
 
 
788
      case '?':
 
789
      case ':':
 
790
        print_usage();
 
791
        return ERR_BADARGS;
 
792
 
 
793
    }
 
794
  }
 
795
 
 
796
  if (optind < argc) strcpy(drive, argv[optind++]);
 
797
 
 
798
  if (optind < argc) {                /* extra argument routine */
 
799
    printf("Unexpected arguments: "); /* print error */
 
800
    while (optind < argc) {
 
801
      printf ("%s ", argv[optind++]); /* print bad args */
 
802
    }
 
803
    printf ("\n");
 
804
    print_usage();
 
805
    return ERR_BADARGS;
 
806
  }
 
807
 
 
808
  /* That takes care of argument parsing, now on with the actual work... */
 
809
 
 
810
  if (!(ramsize = get_physmem())) {
 
811
    if (!quiet_flag) printf("Warning: Cannot determine physical RAM.\n");
 
812
    required_size = 0;
 
813
  } else if (!(vramsize = get_videomem())) {
 
814
    if (!quiet_flag) printf("Warning: Unable to determine the amount of video"
 
815
                            " RAM.\n");
 
816
    required_size = 0;
 
817
    required_sectors = 0;
 
818
  } else {
 
819
    required_size = ramsize + vramsize + PROBE_PADDING;
 
820
    required_sectors = (((size_t)required_size * 1024) / SECTOR_SIZE) + 2;
 
821
  }
 
822
 
 
823
  if (!required_size) {
 
824
    if (!quiet_flag) printf("Reccomended partition size is unknown.\n");
 
825
  } else {
 
826
    if (!quiet_flag) printf("Reccomended partition size is %d MB"
 
827
                            " (%d sectors)\n", ((required_size+1023) >> 10),
 
828
                            required_sectors);
 
829
  }
 
830
 
 
831
  if (probeonly_flag) {
 
832
    return (required_size ? 0 : ERR_CANTPROBE);
 
833
  }
 
834
 
 
835
  /* Open the file. */
 
836
 
 
837
  if ((fd = open(drive, O_RDWR)) == -1) {
 
838
    fprintf(stderr, "Error: cannot open %s: %s\n", drive, strerror(errno));
 
839
 
 
840
    /* This error pops up a lot if someone runs lphdisk with no arguments    */
 
841
    /* and isn't root, which can happen if they've just                      */
 
842
    /* installed/encountered it and don't know what it does or how it works. */
 
843
    /* Give 'em a hint if this is the case.                                  */
 
844
 
 
845
    if (argc == 1) printf("(Try '%s --help' for help)\n", argv0);
 
846
 
 
847
    return ERR_OPEN;
 
848
  }
 
849
 
 
850
  /* Are we looking at a block device? */
 
851
 
 
852
  if (fstat(fd, &st)) {
 
853
    perror("fstat");
 
854
    return ERR_STAT;
 
855
  }
 
856
  if (S_ISBLK(st.st_mode)) {
 
857
    dev = st.st_rdev;
 
858
    debug("%s is a block device (major=%d, minor=%d)\n", drive, major(dev),
 
859
          minor(dev));
 
860
  } else {
 
861
    if (force_flag) {
 
862
      if (!quiet_flag) fprintf(stderr, "Warning: %s is not a block device.\n",
 
863
                               drive);
 
864
      dev = 0;
 
865
    } else {
 
866
      fprintf(stderr, "Error: %s is not a block device (override with -f)\n",
 
867
              drive);
 
868
      return ERR_BADFILE;
 
869
    }
 
870
  }
 
871
 
 
872
  /* Read the MBR and parse the partition table. */
 
873
 
 
874
  if (read_mbr(fd, mbr_buf)) {
 
875
    fprintf(stderr, "Unable to read master boot record.\n");
 
876
    return ERR_READ;
 
877
  }
 
878
 
 
879
  if (parse_table(mbr_buf, pi)) {
 
880
    fprintf(stderr, "Unable to parse partition table.\n");
 
881
    return ERR_TABLE;
 
882
  }
 
883
 
 
884
  /* If we're using a block device, then verify it against /proc/partitions */
 
885
 
 
886
  if (dev && check_proc_partitions(dev, pi)) {
 
887
    if (force_flag) {
 
888
      if (!quiet_flag) fprintf(stderr, "Warning: /proc/partitions does not"
 
889
                                       " match partition table.\n");
 
890
    } else {
 
891
      fprintf(stderr, "Error: /proc/partitions does not match partition table"
 
892
                      " (override with -f).\n");
 
893
      return ERR_TABLE;
 
894
    }
 
895
  }
 
896
 
 
897
  /* Find the right partition. */
 
898
 
 
899
  partition = seek_a0(pi);
 
900
  if (!partition) {
 
901
    fprintf(stderr, "Error: Unable to find partition of type %02X.\n",
 
902
            HIBERNATION_ID);
 
903
    return ERR_FINDPART;
 
904
  } else if (partition < 0) {
 
905
    fprintf(stderr, "Error: More than one partition is of type %02X.  Unable"
 
906
                    " to determine which to use.\n", HIBERNATION_ID);
 
907
    return ERR_FINDPART;
 
908
  }
 
909
 
 
910
 
 
911
  /* So far so good.. now it's time to actually _do_ it. */
 
912
 
 
913
  if (!quiet_flag) printf("Creating hibernate area on %s, partition %d...\n",
 
914
                          drive, partition);
 
915
 
 
916
  /* Check for a couple of things to warn people about.. */
 
917
 
 
918
  if ((dev != makedev(3, 0)) && !quiet_flag) {
 
919
    fprintf(stderr, "Warning: The BIOS will probably be unable to use this"
 
920
            " hibernate partition\n         because it is not on the first IDE"
 
921
            " drive.\n");
 
922
  }
 
923
 
 
924
  if ((pi[partition-1].size < required_sectors) && !quiet_flag) {
 
925
    fprintf(stderr, "Warning: hibernate partition size (%d) is smaller than"
 
926
                    " reccomended size (%d).\n", pi[partition-1].size,
 
927
            required_sectors);
 
928
  }
 
929
 
 
930
  /* And off we go! */
 
931
 
 
932
  if (do_format(fd, pi[partition-1])) {
 
933
    if (!quiet_flag) printf("\n");
 
934
    fprintf(stderr, "Format failed.\n");
 
935
    return ERR_WRITE;
 
936
  } else {
 
937
    if (!quiet_flag) printf("\nFormat complete.\n");
 
938
  }
 
939
 
 
940
  /* All done..  Clean up and exit. */
 
941
 
 
942
  close(fd);
 
943
  return 0;
 
944
}