Kaynağa Gözat

[int13con] Add basic ability to log to a local disk via INT 13

Several popular public cloud providers do not provide any sensible
mechanism for obtaining debug output from an OS which is failing to
boot.  For example, Amazon EC2 provides the "Get System Log" facility,
which occasionally deigns to report a random subset of the characters
emitted via the VM's serial port, but usually returns only a blank
screen.  (Amazingly, this is still superior to the debugging
facilities provided by Azure.)

Work around these shortcomings by adding a console type which sends
output to a magically detected raw disk partition, and including such
a partition within any iPXE .usb-format image.

To use this facility:

 - build an iPXE .usb image with CONSOLE_INT13 enabled

 - boot the cloud VM from this image

 - after the boot fails, attach the VM's boot disk to a second VM

 - from this second VM, use "less -f -R /dev/sdb3" (or similar) to
   view the iPXE output.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 yıl önce
ebeveyn
işleme
3ec8b67818

+ 284
- 0
src/arch/i386/interface/pcbios/int13con.c Dosyayı Görüntüle

@@ -0,0 +1,284 @@
1
+/*
2
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ *
19
+ * You can also choose to distribute this program under the terms of
20
+ * the Unmodified Binary Distribution Licence (as given in the file
21
+ * COPYING.UBDL), provided that you have satisfied its requirements.
22
+ */
23
+
24
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
+
26
+#include <stdint.h>
27
+#include <string.h>
28
+#include <errno.h>
29
+#include <ipxe/console.h>
30
+#include <ipxe/init.h>
31
+#include <realmode.h>
32
+#include <int13.h>
33
+#include <config/console.h>
34
+
35
+/** @file
36
+ *
37
+ * INT13 disk log console
38
+ *
39
+ */
40
+
41
+/* Set default console usage if applicable */
42
+#if ! ( defined ( CONSOLE_INT13 ) && CONSOLE_EXPLICIT ( CONSOLE_INT13 ) )
43
+#undef CONSOLE_INT13
44
+#define CONSOLE_INT13 ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
45
+#endif
46
+
47
+/** Disk drive number */
48
+#define INT13CON_DRIVE 0x80
49
+
50
+/** Log partition type */
51
+#define INT13CON_PARTITION_TYPE 0xe0
52
+
53
+/** Maximum number of outstanding unwritten characters */
54
+#define INT13CON_MAX_UNWRITTEN 64
55
+
56
+/** Log partition header */
57
+struct int13con_header {
58
+	/** Magic signature */
59
+	char magic[10];
60
+} __attribute__ (( packed ));
61
+
62
+/** Log partition magic signature */
63
+#define INT13CON_MAGIC "iPXE LOG\n\n"
64
+
65
+/** Sector buffer */
66
+static uint8_t __bss16_array ( int13con_buffer, [INT13_BLKSIZE] );
67
+#define int13con_buffer __use_data16 ( int13con_buffer )
68
+
69
+/** Disk address packet */
70
+static struct int13_disk_address __bss16 ( int13con_address );
71
+#define int13con_address __use_data16 ( int13con_address )
72
+
73
+/** Current LBA */
74
+static uint64_t int13con_lba;
75
+
76
+/** Maximum LBA */
77
+static uint64_t int13con_max_lba;
78
+
79
+/** Current offset within sector */
80
+static size_t int13con_offset;
81
+
82
+/** Number of unwritten characters */
83
+static size_t int13con_unwritten;
84
+
85
+struct console_driver int13con __console_driver;
86
+
87
+/**
88
+ * Read/write disk sector
89
+ *
90
+ * @v op		Operation
91
+ * @v lba		Logical block address
92
+ * @ret rc		Return status code
93
+ */
94
+static int int13con_rw ( unsigned int op, uint64_t lba ) {
95
+	uint8_t error;
96
+
97
+	/* Construct disk address packet */
98
+	int13con_address.bufsize = sizeof ( int13con_address );
99
+	int13con_address.count = 1;
100
+	int13con_address.buffer.segment = rm_ds;
101
+	int13con_address.buffer.offset = __from_data16 ( int13con_buffer );
102
+	int13con_address.lba = lba;
103
+
104
+	/* Issue INT13 */
105
+	__asm__ ( REAL_CODE ( "int $0x13\n\t" )
106
+		  : "=a" ( error )
107
+		  : "0" ( op << 8 ), "d" ( INT13CON_DRIVE ),
108
+		    "S" ( __from_data16 ( &int13con_address ) ) );
109
+	if ( error ) {
110
+		DBG ( "INT13CON operation %04x failed: %02x\n",
111
+		      op, error );
112
+		return -EIO;
113
+	}
114
+
115
+	return 0;
116
+}
117
+
118
+/**
119
+ * Write character to console
120
+ *
121
+ * @v character		Character
122
+ */
123
+static void int13con_putchar ( int character ) {
124
+	static int busy;
125
+	int rc;
126
+
127
+	/* Ignore if we are already mid-logging */
128
+	if ( busy )
129
+		return;
130
+	busy = 1;
131
+
132
+	/* Write character to buffer */
133
+	int13con_buffer[int13con_offset++] = character;
134
+	int13con_unwritten++;
135
+
136
+	/* Write sector to disk, if applicable */
137
+	if ( ( int13con_offset == INT13_BLKSIZE ) ||
138
+	     ( int13con_unwritten == INT13CON_MAX_UNWRITTEN ) ||
139
+	     ( character == '\n' ) ) {
140
+
141
+		/* Write sector to disk */
142
+		if ( ( rc = int13con_rw ( INT13_EXTENDED_WRITE,
143
+					  int13con_lba ) ) != 0 ) {
144
+			DBG ( "INT13CON could not write log\n" );
145
+			/* Ignore and continue; there's nothing we can do */
146
+		}
147
+
148
+		/* Reset count of unwritten characters */
149
+		int13con_unwritten = 0;
150
+	}
151
+
152
+	/* Move to next sector, if applicable */
153
+	if ( int13con_offset == INT13_BLKSIZE ) {
154
+
155
+		/* Disable console if we have run out of space */
156
+		if ( int13con_lba >= int13con_max_lba )
157
+			int13con.disabled = 1;
158
+
159
+		/* Clear log buffer */
160
+		memset ( int13con_buffer, 0, sizeof ( int13con_buffer ) );
161
+		int13con_offset = 0;
162
+
163
+		/* Move to next sector */
164
+		int13con_lba++;
165
+	}
166
+
167
+	/* Clear busy flag */
168
+	busy = 0;
169
+}
170
+
171
+/**
172
+ * Find log partition
173
+ *
174
+ * @ret rc		Return status code
175
+ */
176
+static int int13con_find ( void ) {
177
+	struct master_boot_record *mbr =
178
+		( ( struct master_boot_record * ) int13con_buffer );
179
+	struct int13con_header *hdr =
180
+		( ( struct int13con_header * ) int13con_buffer );
181
+	struct partition_table_entry part[4];
182
+	unsigned int i;
183
+	int rc;
184
+
185
+	/* Read MBR */
186
+	if ( ( rc = int13con_rw ( INT13_EXTENDED_READ, 0 ) ) != 0 ) {
187
+		DBG ( "INT13CON could not read MBR: %s\n", strerror ( rc ) );
188
+		return rc;
189
+	}
190
+
191
+	/* Check MBR magic */
192
+	if ( mbr->magic != INT13_MBR_MAGIC ) {
193
+		DBG ( "INT13CON incorrect MBR magic\n" );
194
+		DBG2_HDA ( 0, mbr, sizeof ( *mbr ) );
195
+		return -EINVAL;
196
+	}
197
+
198
+	/* Look for magic partition */
199
+	memcpy ( part, mbr->partitions, sizeof ( part ) );
200
+	for ( i = 0 ; i < ( sizeof ( part ) / sizeof ( part[0] ) ) ; i++ ) {
201
+
202
+		/* Skip partitions of the wrong type */
203
+		if ( part[i].type != INT13CON_PARTITION_TYPE )
204
+			continue;
205
+
206
+		/* Read partition header */
207
+		if ( ( rc = int13con_rw ( INT13_EXTENDED_READ,
208
+					  part[i].start ) ) != 0 ) {
209
+			DBG ( "INT13CON partition %d could not read header: "
210
+			      "%s\n", ( i + 1 ), strerror ( rc ) );
211
+			continue;
212
+		}
213
+
214
+		/* Check partition header */
215
+		if ( memcmp ( hdr->magic, INT13CON_MAGIC,
216
+			      sizeof ( hdr->magic ) ) != 0 ) {
217
+			DBG ( "INT13CON partition %d bad magic\n", ( i + 1 ) );
218
+			DBG2_HDA ( 0, hdr, sizeof ( *hdr ) );
219
+			continue;
220
+		}
221
+
222
+		/* Found log partition */
223
+		DBG ( "INT13CON partition %d at [%08x,%08x)\n", ( i + 1 ),
224
+		      part[i].start, ( part[i].start + part[i].length ) );
225
+		int13con_lba = part[i].start;
226
+		int13con_max_lba = ( part[i].start + part[i].length - 1 );
227
+
228
+		/* Initialise log buffer */
229
+		memset ( &int13con_buffer[ sizeof ( *hdr ) ], 0,
230
+			 ( sizeof ( int13con_buffer ) - sizeof ( *hdr ) ) );
231
+		int13con_offset = sizeof ( hdr->magic );
232
+
233
+		return 0;
234
+	}
235
+
236
+	DBG ( "INT13CON found no log partition\n" );
237
+	return -ENOENT;
238
+}
239
+
240
+/**
241
+ * Initialise INT13 console
242
+ *
243
+ */
244
+static void int13con_init ( void ) {
245
+	uint8_t error;
246
+	uint16_t check;
247
+	unsigned int discard_c;
248
+	unsigned int discard_d;
249
+	int rc;
250
+
251
+	/* Check for INT13 extensions */
252
+	__asm__ __volatile__ ( REAL_CODE ( "int $0x13\n\t"
253
+					   "setc %%al\n\t" )
254
+			       : "=a" ( error ), "=b" ( check ),
255
+				 "=c" ( discard_c ), "=d" ( discard_d )
256
+			       : "0" ( INT13_EXTENSION_CHECK << 8 ),
257
+				 "1" ( 0x55aa ), "3" ( INT13CON_DRIVE ) );
258
+	if ( error || ( check != 0xaa55 ) ) {
259
+		DBG ( "INT13CON missing extensions (%02x,%04x)\n",
260
+		      error, check );
261
+		return;
262
+	}
263
+
264
+	/* Locate log partition */
265
+	if ( ( rc = int13con_find() ) != 0)
266
+		return;
267
+
268
+	/* Enable console */
269
+	int13con.disabled = 0;
270
+}
271
+
272
+/**
273
+ * INT13 console initialisation function
274
+ */
275
+struct init_fn int13con_init_fn __init_fn ( INIT_CONSOLE ) = {
276
+	.initialise = int13con_init,
277
+};
278
+
279
+/** INT13 console driver */
280
+struct console_driver int13con __console_driver = {
281
+	.putchar = int13con_putchar,
282
+	.disabled = CONSOLE_DISABLED,
283
+	.usage = CONSOLE_INT13,
284
+};

+ 15
- 6
src/arch/i386/prefix/usbdisk.S Dosyayı Görüntüle

@@ -8,18 +8,27 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
8 8
 
9 9
 #include "mbr.S"
10 10
 
11
-/* Partition table: ZIP-compatible partition 4, 64 heads, 32 sectors/track */
11
+/* Partition table: 64 heads, 32 sectors/track (ZIP-drive compatible) */
12 12
 	.org 446
13 13
 	.space 16
14 14
 	.space 16
15
-	.space 16
16
-	.byte 0x80, 0x01, 0x01, 0x00
17
-	.byte 0xeb, 0x3f, 0x20, 0x01
15
+	/* Partition 3: log partition (for CONSOLE_INT13) */
16
+	.byte 0x00, 0x01, 0x01, 0x00
17
+	.byte 0xe0, 0x3f, 0x20, 0x00
18 18
 	.long 0x00000020
19
-	.long 0x00000fe0
19
+	.long 0x000007e0
20
+	/* Partition 4: boot partition */
21
+	.byte 0x80, 0x00, 0x01, 0x01
22
+	.byte 0xeb, 0x3f, 0x20, 0x02
23
+	.long 0x00000800
24
+	.long 0x00001000
20 25
 
21 26
 	.org 510
22 27
 	.byte 0x55, 0xaa
23 28
 
24
-/* Skip to start of partition */
29
+/* Skip to start of log partition */
25 30
 	.org 32 * 512
31
+	.ascii "iPXE LOG\n\n"
32
+
33
+/* Skip to start of boot partition */
34
+	.org 2048 * 512

+ 1
- 0
src/arch/x86/include/bits/errfile.h Dosyayı Görüntüle

@@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
21 21
 #define ERRFILE_guestinfo	( ERRFILE_ARCH | ERRFILE_CORE | 0x000a0000 )
22 22
 #define ERRFILE_apm		( ERRFILE_ARCH | ERRFILE_CORE | 0x000b0000 )
23 23
 #define ERRFILE_vesafb		( ERRFILE_ARCH | ERRFILE_CORE | 0x000c0000 )
24
+#define ERRFILE_int13con	( ERRFILE_ARCH | ERRFILE_CORE | 0x000d0000 )
24 25
 
25 26
 #define ERRFILE_bootsector     ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
26 27
 #define ERRFILE_bzimage	       ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )

+ 3
- 0
src/config/config.c Dosyayı Görüntüle

@@ -84,6 +84,9 @@ REQUIRE_OBJECT ( debugcon );
84 84
 #ifdef CONSOLE_VESAFB
85 85
 REQUIRE_OBJECT ( vesafb );
86 86
 #endif
87
+#ifdef CONSOLE_INT13
88
+REQUIRE_OBJECT ( int13con );
89
+#endif
87 90
 
88 91
 /*
89 92
  * Drag in all requested network protocols

+ 1
- 0
src/config/console.h Dosyayı Görüntüle

@@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
23 23
 //#define	CONSOLE_VMWARE		/* VMware logfile console */
24 24
 //#define	CONSOLE_DEBUGCON	/* Debug port console */
25 25
 //#define	CONSOLE_VESAFB		/* VESA framebuffer console */
26
+//#define	CONSOLE_INT13		/* INT13 disk log console */
26 27
 
27 28
 #define	KEYBOARD_MAP	us
28 29
 

Loading…
İptal
Kaydet