summaryrefslogtreecommitdiff
path: root/util/nvmutil/nvmutil.h
blob: 831c161afa1c8e235a5132fc273025d5574ce167 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
/*
 * SPDX-License-Identifier: MIT
 *
 * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
 */

#ifndef NVMUTIL_H
#define NVMUTIL_H

#define MAX_CMD_LEN 50

#ifndef PATH_LEN
#define PATH_LEN 1024
#endif

#define OFF_ERR 0
#ifndef OFF_RESET
#define OFF_RESET 1
#endif

#ifndef MAX_ZERO_RW_RETRY
#define MAX_ZERO_RW_RETRY 5
#endif

#ifndef HAVE_REAL_PREAD_PWRITE
#define HAVE_REAL_PREAD_PWRITE 0
#endif

#ifndef LOOP_EAGAIN
#define LOOP_EAGAIN 1
#endif
#ifndef LOOP_EINTR
#define LOOP_EINTR 1
#endif

#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif

/*
 * Older versions of BSD to the early 2000s
 * could compile nvmutil, but pledge was
 * added in the 2010s. Therefore, for extra
 * portability, we will only pledge/unveil
 * on OpenBSD versions that have it.
 */

#if defined(__OpenBSD__) && defined(OpenBSD)
#if OpenBSD >= 604
#ifndef NVMUTIL_UNVEIL
#define NVMUTIL_UNVEIL 1
#endif
#endif
#if OpenBSD >= 509
#ifndef NVMUTIL_PLEDGE
#define NVMUTIL_PLEDGE 1
#endif
#endif
#endif

#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif

#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif

#ifndef O_BINARY
#define O_BINARY 0
#endif

#ifndef O_EXCL
#define O_EXCL 0
#endif

#ifndef O_CREAT
#define O_CREAT 0
#endif

#ifndef O_NONBLOCK
#define O_NONBLOCK 0
#endif

#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif

#ifndef O_NOFOLLOW
#define O_NOFOLLOW 0
#endif

#ifndef FD_CLOEXEC
#define FD_CLOEXEC 0
#endif

/*
 * Sizes in bytes:
 */

#define SIZE_1KB 1024
#define SIZE_4KB (4 * SIZE_1KB)
#define SIZE_8KB (8 * SIZE_1KB)
#define SIZE_16KB (16 * SIZE_1KB)
#define SIZE_128KB (128 * SIZE_1KB)

#define GBE_BUF_SIZE (SIZE_128KB)

/*
 * First 128 bytes of a GbE part contains
 * the regular NVM (Non-Volatile-Memory)
 * area. All of these bytes must add up,
 * truncated to 0xBABA.
 *
 * The full GbE region is 4KB, but only
 * the first 128 bytes are used here.
 *
 * There is a second 4KB part with the same
 * rules, and it *should* be identical.
 */

#define GBE_WORK_SIZE (SIZE_8KB)
#define GBE_PART_SIZE (GBE_WORK_SIZE >> 1)
#define NVM_CHECKSUM 0xBABA
#define NVM_SIZE 128
#define NVM_WORDS (NVM_SIZE >> 1)
#define NVM_CHECKSUM_WORD (NVM_WORDS - 1)

/*
 * Portable macro based on BSD nitems.
 * Used to count the number of commands (see below).
 */

#define items(x) (sizeof((x)) / sizeof((x)[0]))

/*
 * GbE files can be 8KB, 16KB or 128KB,
 * but we only need the two 4KB parts
 * from offset zero and offset 64KB in
 * a 128KB file, or zero and 8KB in a 16KB
 * file, or zero and 4KB in an 8KB file.
 *
 * The code will handle this properly.
 */

#ifndef X_LONG_MAX
#define X_LONG_MAX ((long)(~((long)1 << (sizeof(long)*CHAR_BIT-1))))
#endif

/*
 * Use these for .argc in command[]:
 */

#define ARGC_3 3
#define ARGC_4 4

#define NO_LOOP_EAGAIN 0
#define NO_LOOP_EINTR 0

/*
 * Used for checking whether.
 * a file is a file via stat().
 *
 * Portable macro for compatibility
 * with older unix e.g. v7 unix (has S_IFREG),
 * 4.2bsd (has S_IFMT) or POSIX (has S_ISREG)
 *
 * Fallback works where S_IFREG == 0100000
 * (classic unix bitmask)
 */

#ifndef S_ISREG
#if defined(S_IFMT) && defined(S_IFREG)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#elif defined(S_IFREG)
#define S_ISREG(m) (((m) & S_IFREG) != 0)
#else
#error "can't determine types with stat()"
#endif
#endif

#define IO_READ 0
#define IO_WRITE 1
#define IO_PREAD 2
#define IO_PWRITE 3

/*
 * Used as indices for command[]
 * MUST be in the same order as entries in command[]
 */

#define CMD_DUMP 0
#define CMD_SETMAC 1
#define CMD_SWAP 2
#define CMD_COPY 3
#define CMD_CAT 4
#define CMD_CAT16 5
#define CMD_CAT128 6

#define ARG_NOPART 0
#define ARG_PART 1

#define SKIP_CHECKSUM_READ 0
#define CHECKSUM_READ 1

#define SKIP_CHECKSUM_WRITE 0
#define CHECKSUM_WRITE 1

/*
 * portable timeval
 */
struct x_st_timeval {
	long tv_sec;
	long tv_usec;
};

struct commands {
	unsigned long chk;
	char *str;
	void (*run)(void);
	int argc;
	unsigned char arg_part;
	unsigned char chksum_read;
	unsigned char chksum_write;
	unsigned long rw_size; /* within the 4KB GbE part */
	int flags; /* e.g. O_RDWR or O_RDONLY */
};

struct macaddr {
	char *str; /* set to rmac, or argv string */
	char rmac[18]; /* xx:xx:xx:xx:xx:xx */
	unsigned short mac_buf[3];
};

struct xfile {
	int gbe_fd;
	int tmp_fd;

	char *tname; /* path of tmp file */
	char *fname; /* path of gbe file */

	unsigned char *buf; /* work memory for files */

	int io_err_gbe; /* intermediary write (verification) */
	int io_err_gbe_bin; /* final write (real file) */
	int rw_check_err_read[2];
	int rw_check_partial_read[2];
	int rw_check_bad_part[2];

	int post_rw_checksum[2];

	dev_t gbe_dev;
	ino_t gbe_ino;

	dev_t tmp_dev;
	ino_t tmp_ino;

	off_t gbe_file_size;
	off_t gbe_tmp_size;

	unsigned long part;
	unsigned char part_modified[2];
	unsigned char part_valid[2];

	unsigned char real_buf[GBE_BUF_SIZE];
	unsigned char bufcmp[GBE_BUF_SIZE]; /* compare gbe/tmp/reads */

	unsigned char pad[GBE_WORK_SIZE]; /* the file that wouldn't die */
};

/*
 * BE CAREFUL when editing this
 * to ensure that you also update
 * the tables in new_xstate()
 */
struct xstate {
	struct commands cmd[7];
	struct macaddr mac;
	struct xfile f;

	char *argv0;

	unsigned long i; /* index to cmd[] for current command */
	int no_cmd;

	/* store size of a struct here.
	   (can be used to copy old state) */
	unsigned long xsize;
};




static struct xstate *new_xstate(void);

/*
 * Sanitize command tables.
 */
void sanitize_command_list(void);
void sanitize_command_index(unsigned long c);

/*
 * Argument handling (user input)
 */
void set_cmd(int argc, char *argv[]);
void set_cmd_args(int argc, char *argv[]);
unsigned long conv_argv_part_num(const char *part_str);
int xstrxcmp(const char *a, const char *b, unsigned long maxlen);

/*
 * Prep files for reading
 */
void open_gbe_file(void);
int lock_file(int fd);
void xopen(int *fd, const char *path, int flags, struct stat *st);

/*
 * Read GbE file and verify
 * checksums.
 *
 * After this, we can run commands.
 */
void copy_gbe(void);
void read_file(void);
void read_checksums(void);
int good_checksum(unsigned long partnum);

/*
 * Execute user command on GbE data.
 * These are stubs that call helpers.
 */
void run_cmd(void);
void check_command_num(unsigned long c);
unsigned char valid_command(unsigned long c);

/*
 * Helper functions for command: setmac
 */
void cmd_helper_setmac(void);
void parse_mac_string(void);
unsigned long xstrxlen(const char *scmp, unsigned long maxlen);
void set_mac_byte(unsigned long mac_byte_pos);
void set_mac_nib(unsigned long mac_str_pos,
    unsigned long mac_byte_pos, unsigned long mac_nib_pos);
unsigned short hextonum(char ch_s);
unsigned long rlong(void);
unsigned long entropy_jitter(void);
int x_i_gettimeofday(struct x_st_timeval *tv, void *tz);
void write_mac_part(unsigned long partnum);

/*
 * Helper functions for command: dump
 */
void cmd_helper_dump(void);
void print_mac_from_nvm(unsigned long partnum);
void hexdump(unsigned long partnum);

/*
 * Helper functions for command: swap
 */
void cmd_helper_swap(void);

/*
 * Helper functions for command: copy
 */
void cmd_helper_copy(void);

/*
 * Helper functions for commands:
 * cat, cat16 and cat128
 */
void cmd_helper_cat(void);
void cat_buf(unsigned char *b);

/*
 * After command processing, write
 * the modified GbE file back.
 *
 * These are stub functions: check
 * below for the actual functions.
 */
void write_gbe_file(void);
void set_checksum(unsigned long part);
unsigned short calculated_checksum(unsigned long p);

/*
 * Helper functions for accessing
 * the NVM area during operation.
 */
unsigned short nvm_word(unsigned long pos16, unsigned long part);
void set_nvm_word(unsigned long pos16,
    unsigned long part, unsigned short val16);
void set_part_modified(unsigned long p);
void check_nvm_bound(unsigned long pos16, unsigned long part);
void check_bin(unsigned long a, const char *a_name);

/*
 * Helper functions for stub functions
 * that handle GbE file reads/writes.
 */
void rw_gbe_file_part(unsigned long p, int rw_type,
    const char *rw_type_str);
void write_to_gbe_bin(void);
int gbe_mv(void);
void check_written_part(unsigned long p);
void report_io_err_rw(void);
int fsync_dir(const char *path);
unsigned char *gbe_mem_offset(unsigned long part, const char *f_op);
off_t gbe_file_offset(unsigned long part, const char *f_op);
off_t gbe_x_offset(unsigned long part, const char *f_op,
    const char *d_type, off_t nsize, off_t ncmp);
long rw_gbe_file_exact(int fd, unsigned char *mem, unsigned long nrw,
    off_t off, int rw_type);
long rw_file_exact(int fd, unsigned char *mem, unsigned long len,
    off_t off, int rw_type, int loop_eagain, int loop_eintr,
    unsigned long max_retries, int off_reset);
long prw(int fd, void *mem, unsigned long nrw,
    off_t off, int rw_type, int loop_eagain, int loop_eintr,
    int off_reset);
int io_args(int fd, void *mem, unsigned long nrw,
    off_t off, int rw_type);
int check_file(int fd, struct stat *st);
long rw_over_nrw(long r, unsigned long nrw);
#if !defined(HAVE_REAL_PREAD_PWRITE) || \
    HAVE_REAL_PREAD_PWRITE < 1
off_t lseek_loop(int fd, off_t off,
    int whence, int loop_eagain, int loop_eintr);
#endif
int try_err(int loop_err, int errval);

/*
 * Error handling and cleanup
 */
void usage(void);
void err(int nvm_errval, const char *msg, ...);
int exit_cleanup(void);
const char *getnvmprogname(void);

/*
 * a special kind of hell
 */
char *new_tmpfile(int *fd, int local, const char *path);
int x_i_mkstemp(char *template);
char *x_c_strrchr(const char *s, int c);
/* x_i_rename not suitable
 * for atomic writes. kept
 * commentted for use in a
 * library in the future */
/*
int x_i_rename(const char *src, const char *dst);
*/
char *x_c_tmpdir(void);
int x_i_close(int fd);
void *x_v_memcpy(void *dst,
    const void *src, unsigned long n);
int x_i_memcmp(const void *a,
    const void *b, unsigned long n);
int x_i_fchmod(int fd, mode_t mode);
int x_try_fdpath(const char *prefix,
    int fd, mode_t mode);
unsigned long x_conv_fd(char *buf,
    unsigned long n);
int x_i_fsync(int fd);




/* asserts */

/* type asserts */
typedef char static_assert_char_is_8_bits[(CHAR_BIT == 8) ? 1 : -1];
typedef char static_assert_char_is_1[(sizeof(char) == 1) ? 1 : -1];
typedef char static_assert_unsigned_char_is_1[
    (sizeof(unsigned char) == 1) ? 1 : -1];
typedef char static_assert_unsigned_short_is_2[
    (sizeof(unsigned short) >= 2) ? 1 : -1];
typedef char static_assert_short_is_2[(sizeof(short) >= 2) ? 1 : -1];
typedef char static_assert_unsigned_int_is_4[
    (sizeof(unsigned int) >= 4) ? 1 : -1];
typedef char static_assert_unsigned_long_is_4[
    (sizeof(unsigned long) >= 4) ? 1 : -1];
typedef char static_assert_int_ge_32[(sizeof(int) >= 4) ? 1 : -1];
typedef char static_assert_twos_complement[
    ((-1 & 3) == 3) ? 1 : -1
];
typedef char assert_unsigned_long_ptr[
    (sizeof(unsigned long) >= sizeof(void *)) ? 1 : -1
];

/*
 * We set _FILE_OFFSET_BITS 64, but we only handle
 * but we only need smaller files, so require 4-bytes.
 * Some operating systems ignore the define, hence assert:
 */
typedef char static_assert_off_t_is_32[(sizeof(off_t) >= 4) ? 1 : -1];

/*
 * asserts (variables/defines sanity check)
 */
typedef char assert_argc3[(ARGC_3==3)?1:-1];
typedef char assert_argc4[(ARGC_4==4)?1:-1];
typedef char assert_read[(IO_READ==0)?1:-1];
typedef char assert_write[(IO_WRITE==1)?1:-1];
typedef char assert_pread[(IO_PREAD==2)?1:-1];
typedef char assert_pwrite[(IO_PWRITE==3)?1:-1];
typedef char assert_pathlen[(PATH_LEN>=256)?1:-1];
/* commands */
typedef char assert_cmd_dump[(CMD_DUMP==0)?1:-1];
typedef char assert_cmd_setmac[(CMD_SETMAC==1)?1:-1];
typedef char assert_cmd_swap[(CMD_SWAP==2)?1:-1];
typedef char assert_cmd_copy[(CMD_COPY==3)?1:-1];
typedef char assert_cmd_cat[(CMD_CAT==4)?1:-1];
typedef char assert_cmd_cat16[(CMD_CAT16==5)?1:-1];
typedef char assert_cmd_cat128[(CMD_CAT128==6)?1:-1];
/* bool */
typedef char bool_arg_nopart[(ARG_NOPART==0)?1:-1];
typedef char bool_arg_part[(ARG_PART==1)?1:-1];
typedef char bool_skip_checksum_read[(SKIP_CHECKSUM_READ==0)?1:-1];
typedef char bool_checksum_read[(CHECKSUM_READ==1)?1:-1];
typedef char bool_skip_checksum_write[(SKIP_CHECKSUM_WRITE==0)?1:-1];
typedef char bool_checksum_write[(CHECKSUM_WRITE==1)?1:-1];
typedef char bool_loop_eintr[(LOOP_EINTR==1||LOOP_EINTR==0)?1:-1];
typedef char bool_loop_eagain[(LOOP_EAGAIN==1||LOOP_EAGAIN==0)?1:-1];
typedef char bool_no_loop_eintr[(NO_LOOP_EINTR==0)?1:-1];
typedef char bool_no_loop_eagain[(NO_LOOP_EAGAIN==0)?1:-1];
typedef char bool_off_err[(OFF_ERR==0)?1:-1];
typedef char bool_off_reset[(OFF_RESET==0||OFF_RESET==1)?1:-1];


#endif