Skip to contentSkip to conversation

x86_64: build and use GDT on copied compressed kernel

PATCH In review Yinghai Lu 6 participants 26 messages May 13, 2007 – May 28, 2007

Subsystems (5)
lore
Share & tools
lore

Yinghai Lu

author
#0 lore raw

PATCH 1/1 x86_64: build and use GDT on copied compressed kernel

please check the patch.

YH

[PATCH]x86_64: build and use GDT on copied compressed kernel

Build and use GDT on copied compressed kernel postion, instead of using GDT in data segment of loaded compressed kernel. Otherwise decompressing compressed kernel image later in 64bit longmode, will overwrite GDT.

arch/x86_64/boot/compressed/head.S +17 -13
index f9d5692..1f5ac7c 100644
@@ -94,10 +94,23 @@ startup_32:
  * Prepare for entering 64 bit mode
  */
 
+/*
+ * Build early GDT table on copied compressed kernel image position, So GDT
+ * is not be overwriten when decompressing the compressed kernel. 
+ */
+	movw	$0x0020, gdt+0x00(%ebx)
+	movw	$0x0000, gdt+0x06(%ebx)
+	movl	$0x00000000, gdt+0x08(%ebx) /* NULL descriptor */
+	movl	$0x00000000, gdt+0x0c(%ebx)
+	movl	$0x0000ffff, gdt+0x10(%ebx) /* __KERNEL_CS */
+	movl	$0x00af9a00, gdt+0x14(%ebx)
+	movl	$0x0000ffff, gdt+0x18(%ebx) /* __KERNEL_DS */
+	movl	$0x00cf9200, gdt+0x1c(%ebx)
+
 	/* Load new GDT with the 64bit segments using 32bit descriptor */
-	leal	gdt(%ebp), %eax
-	movl	%eax, gdt+2(%ebp)
-	lgdt	gdt(%ebp)
+	leal	gdt(%ebx), %eax
+	movl	%eax, gdt+2(%ebx)
+	lgdt	gdt(%ebx)
 
 	/* Enable PAE mode */
 	xorl	%eax, %eax
@@ -182,7 +195,7 @@ no_longmode:
 	 * it may change in the future.
 	 */
 	.code64
-	.org 0x200
+	.org 0x400
 ENTRY(startup_64)
 	/* We come here either from startup_32 or directly from a
 	 * 64bit bootloader.  If we come here from a bootloader we depend on
@@ -287,15 +300,6 @@ relocated:
  */
 	jmp	*%rbp
 
-	.data
-gdt:
-	.word	gdt_end - gdt
-	.long	gdt
-	.word	0
-	.quad	0x0000000000000000	/* NULL descriptor */
-	.quad	0x00af9a000000ffff	/* __KERNEL_CS */
-	.quad	0x00cf92000000ffff	/* __KERNEL_DS */
-gdt_end:
 	.bss
 /* Stack for uncompression */
 	.balign 4
arch/x86_64/boot/compressed/vmlinux.lds +2 -0
index 94c13e5..6e041ef 100644
@@ -36,6 +36,8 @@ SECTIONS
 		*(COMMON)
 		. = ALIGN(8);
 		_end = . ;
+		gdt = . ;
+		. = . + 8 * 4;
 		. = ALIGN(4096);
 		pgtable = . ;
 		. = . + 4096 * 6;
#1 lore raw
Quoted 1 line · On Sunday 13 May 2007 07:33:14 Yinghai Lu wrote:

please check the patch.

Can you please give a little more description on why you think the patch is needed? Also we need a Signed-off-by line.

Thanks,
-Andi

author
#2 lore raw
Quoted 4 lines · Andi Kleen

On Sunday 13 May 2007 07:33:14 Yinghai Lu wrote:

Can you please give a little more description on why you think the patch is needed? Also we need a Signed-off-by line.

in the arch/x86_64/boot/compressed/head.S.
it switch to 64 bit mode and then copy compressed image to higher address. and jump the copied image, then decompressed the compressed image to final uncompressed kernel and it may overlapped with original loaded compressed kernel image.

before jump to 64 bit mode, it will create new page table in some place higher in copied compressed kernel image. but still use the GDT in place of original loaded compressed image. So the GDT will be overwriten in the middle of decompressing.

So we could change to build the GDT like page table together with copied compressed kernel.

http://lkml.org/lkml/2007/5/13/8

#3 lore raw
In reply to Yinghai Lu
Quoted 9 lines · Yinghai Lu

please check the patch.

YH

[PATCH]x86_64: build and use GDT on copied compressed kernel

Build and use GDT on copied compressed kernel postion, instead of using GDT in data segment of loaded compressed kernel. Otherwise decompressing compressed kernel image later in 64bit longmode, will overwrite GDT.

Overwriting the GDT should be fine because we don't reload the segments, after we overwrite it. GDT entries are only read when we load segments. Interrupts are disabled so that should not cause a problem.

Why do you think the current behaviour is a problem?

Eric

author
#4 lore raw
Quoted 5 lines · Eric W. Biederman

Overwriting the GDT should be fine because we don't reload the segments, after we overwrite it. GDT entries are only read when we load segments. Interrupts are disabled so that should not cause a problem.

Why do you think the current behaviour is a problem?

just think if GDT should not be overwriten when we are still under that GDT setting.
didn't have chance to test that in HDT or other debug device, when setting breaking point after that GDT is overwriten.

YH

#5 lore raw
Quoted 11 lines · Yinghai Lu
Quoted 5 lines · Eric W. Biederman

Overwriting the GDT should be fine because we don't reload the segments, after we overwrite it. GDT entries are only read when we load segments. Interrupts are disabled so that should not cause a problem.

Why do you think the current behaviour is a problem?

just think if GDT should not be overwriten when we are still under that GDT setting.
didn't have chance to test that in HDT or other debug device, when setting breaking point after that GDT is overwriten.

It would be interesting to see if you could produce a failure using that gdt, I don't think you will be able to, even with a debugger.

For data segments we never use that GDT, it is only for the code segment we use the GDT and that only to get us into 64bit mode.

Eric

author
#6 lore raw
Quoted 4 lines · Eric W. Biederman

For data segments we never use that GDT, it is only for the code segment we use the GDT and that only to get us into 64bit mode.

after the patch linux:/home/yhlu/xx/xx/kernel/linux-2.6/arch/x86_64/boot/compressed # readelf -e vmlinux
...
Section Headers:

 [Nr] Name              Type             Address           Offset
      Size              EntSize          Flags  Link  Info  Align
 [ 0]                   NULL             0000000000000000  00000000
      0000000000000000  0000000000000000           0     0     0
 [ 1] .text             PROGBITS         0000000000000000  00100000
      000000000032617c  0000000000000000  AX       0     0     16
 [ 2] .rodata           PROGBITS         0000000000326180  00426180
      0000000000000400  0000000000000000   A       0     0     32
 [ 3] .eh_frame         PROGBITS         0000000000326580  00426580
      0000000000000210  0000000000000000   A       0     0     8
 [ 4] .data             PROGBITS         0000000000326790  00426790
      0000000000000008  0000000000000000  WA       0     0     8
 [ 5] .bss              NOBITS           00000000003267a0  00426798
      000000000000b860  0000000000000000  WA       0     0     32
 [ 6] .comment          PROGBITS         0000000000000000  00426798
      0000000000000035  0000000000000000           0     0     1
 [ 7] .shstrtab         STRTAB           0000000000000000  004267cd
      0000000000000047  0000000000000000           0     0     1
 [ 8] .symtab           SYMTAB           0000000000000000  00426a98
      00000000000007c8  0000000000000018           9    60     8
 [ 9] .strtab           STRTAB           0000000000000000  00427260
      0000000000000350  0000000000000000           0     0     1

Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)

after the patch, it seems .data section size still have 8 bytes. before that will 0x28.
don't know what are the 8 bytes.

YH

#7 lore raw
Quoted 33 lines · Yinghai Lu
Quoted 4 lines · Eric W. Biederman

For data segments we never use that GDT, it is only for the code segment we use the GDT and that only to get us into 64bit mode.

after the patch linux:/home/yhlu/xx/xx/kernel/linux-2.6/arch/x86_64/boot/compressed # readelf -e vmlinux
...
Section Headers:

 [Nr] Name              Type             Address           Offset
      Size              EntSize          Flags  Link  Info  Align
 [ 0]                   NULL             0000000000000000  00000000
      0000000000000000  0000000000000000           0     0     0
 [ 1] .text             PROGBITS         0000000000000000  00100000
      000000000032617c  0000000000000000  AX       0     0     16
 [ 2] .rodata           PROGBITS         0000000000326180  00426180
      0000000000000400  0000000000000000   A       0     0     32
 [ 3] .eh_frame         PROGBITS         0000000000326580  00426580
      0000000000000210  0000000000000000   A       0     0     8
 [ 4] .data             PROGBITS         0000000000326790  00426790
      0000000000000008  0000000000000000  WA       0     0     8
 [ 5] .bss              NOBITS           00000000003267a0  00426798
      000000000000b860  0000000000000000  WA       0     0     32
 [ 6] .comment          PROGBITS         0000000000000000  00426798
      0000000000000035  0000000000000000           0     0     1
 [ 7] .shstrtab         STRTAB           0000000000000000  004267cd
      0000000000000047  0000000000000000           0     0     1
 [ 8] .symtab           SYMTAB           0000000000000000  00426a98
      00000000000007c8  0000000000000018           9    60     8
 [ 9] .strtab           STRTAB           0000000000000000  00427260
      0000000000000350  0000000000000000           0     0     1
Quoted 8 lines

Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)

after the patch, it seems .data section size still have 8 bytes. before that will 0x28.
don't know what are the 8 bytes.

Probably variables in misc.c

Eric

author
#8 lore raw
Quoted 1 line · Eric W. Biederman

Probably variables in misc.c

it is vidmem in misc.c

YH

author
#9 lore raw

Eric,

I got one question about kexec and bzImage on 64bit kernel.

The bzImage for 64bit kernel must be loaded under 4G? but vmlinux for 64bit kernel could be loaded above 4G.

So is there any plan to use 64bit entry point (startup_64) in arch/x86_64/boot/compressed/vmlinux in bzImage? so you may load bzImage above 4G too.

YH

#10 lore raw
Quoted 10 lines · Yinghai Lu

Eric,

I got one question about kexec and bzImage on 64bit kernel.

The bzImage for 64bit kernel must be loaded under 4G? but vmlinux for 64bit kernel could be loaded above 4G.

So is there any plan to use 64bit entry point (startup_64) in arch/x86_64/boot/compressed/vmlinux in bzImage? so you may load bzImage above 4G too.

We have big boot protocol revision coming up to more clearly document what is needed to work with the native entry points. It is a 2.6.23 targeted thing and that is one of the things that we are likely to look at.

Eric

author
#11 lore raw
Quoted 4 lines · Eric W. Biederman

We have big boot protocol revision coming up to more clearly document what is needed to work with the native entry points. It is a 2.6.23 targeted thing and that is one of the things that we are likely to look at.

Good, where is the thread for that?

YH

#12 lore raw
Quoted 7 lines · Yinghai Lu
Quoted 4 lines · Eric W. Biederman

We have big boot protocol revision coming up to more clearly document what is needed to work with the native entry points. It is a 2.6.23 targeted thing and that is one of the things that we are likely to look at.

Good, where is the thread for that?

Look on the virtualization list may lkml was cc'd about getting Xen and lguest. "boot bzImages under paravirt" I think was the subject on one of the pieces.

A lot of this work is held up in part by the need to get 2.6.22 out, and in part because we have so much in flight work for the early phases of boot that we need to let things settle before it we can do more.

Eric

#13 lore raw

PATCH 1/1 serial: set RTS and DTR if flow is 'r'

In reply to Eric W. Biederman

[PATCH] serial: set RTS and DTR if flow is 'r'

if the serial console flow is set to 'r', We need to set RTS and DTR. Some UARTs on other side need these bit set, otherwise will send char to or receive char from the host that kernel is runing esp for kernel boot stage.

BTW:
earlyprintk and early_uart are hard coded to set DTR/RTS.

Cc Russell King <[email protected]>
Cc Andi Kleen <[email protected]>
Cc Bjorn Helgaas <[email protected]>
drivers/serial/8250.c +0 -0
index 48e259a..439a00a 100644
@@ -2154,6 +2154,16 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,

}
serial_outp(up, UART_FCR, fcr); /* set fcr */
}
+
+ /* if serial console flow is 'r', we need to set RTS and DTR to MCR.
+ * some uarts on other side don't support no flow control. So we state
+ * console=ttyS0,9600n8r in kernel command line to make those uart can
+ * work.
+ */
+ if (termios->c_cflag & CRTSCTS) {
+ up->port.mctrl |= TIOCM_RTS | TIOCM_DTR;
+ }
+
serial8250_set_mctrl(&up->port, up->port.mctrl); spin_unlock_irqrestore(&up->port.lock, flags);
}

#14 lore raw

Re: [PATCH] serial: set RTS and DTR if flow is 'r'

Quoted 21 lines · On Mon, 14 May 2007 10:26:26 -0700 Yinghai Lu wrote:

[PATCH] serial: set RTS and DTR if flow is 'r'

if the serial console flow is set to 'r', We need to set RTS and DTR. Some UARTs on other side need these bit set, otherwise will send char to or receive char from the host that kernel is runing esp for kernel boot stage.

BTW:
earlyprintk and early_uart are hard coded to set DTR/RTS.

Cc Russell King <[email protected]>
Cc Andi Kleen <[email protected]>
Cc Bjorn Helgaas <[email protected]>
drivers/serial/8250.c +0 -0
index 48e259a..439a00a 100644
@@ -2154,6 +2154,16 @@ serial8250_set_termios(struct uart_port *port, 
 struct ktermios *termios,
 }
Quoted 1 line

serial_outp(up, UART_FCR, fcr); /* set fcr */

Something is eating tabs above and presumably in the new patch lines...

Quoted 7 lines

}
+
+ /* if serial console flow is 'r', we need to set RTS and DTR to MCR.
+ * some uarts on other side don't support no flow control. So we state
+ * console=ttyS0,9600n8r in kernel command line to make those uart can
+ * work.
+ */

/*
* Linux long comment style is
* like this.
*/

Quoted 3 lines

+ if (termios->c_cflag & CRTSCTS) {
+ up->port.mctrl |= TIOCM_RTS | TIOCM_DTR;
+ }

Don't use braces on one-statement blocks.

Quoted 4 lines

+
serial8250_set_mctrl(&up->port, up->port.mctrl);
spin_unlock_irqrestore(&up->port.lock, flags);
}

---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***

#15 lore raw

PATCH 1/1 serial: set RTS and DTR if flow is 'r' --- resend

In reply to Yinghai Lu

[PATCH] serial: set RTS and DTR if flow is 'r'

if the serial console flow is set to 'r', We need to set RTS and DTR. Some UARTs on other side need these bit set, otherwise will not send char to or receive char from the host that kernel is runing esp for kernel boot stage.

BTW:
earlyprintk and early_uart are hard coded to set DTR/RTS.

Cc Russell King <[email protected]>
Cc Andi Kleen <[email protected]>
Cc Bjorn Helgaas <[email protected]>
drivers/serial/8250.c +0 -0
index 48e259a..4e6f8be 100644
@@ -2154,6 +2154,15 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,

}
serial_outp(up, UART_FCR, fcr); /* set fcr */
}
+
+ /* if serial console flow is 'r', we need to set RTS and DTR to MCR.
+ * some uarts on other side don't support no flow control. So we state
+ * console=ttyS0,9600n8r in kernel command line to make those uart can
+ * work.
+ */
+ if (termios->c_cflag & CRTSCTS)
+ up->port.mctrl |= TIOCM_RTS | TIOCM_DTR;
+
serial8250_set_mctrl(&up->port, up->port.mctrl); spin_unlock_irqrestore(&up->port.lock, flags);
}

#16 lore raw

Re: [PATCH] serial: set RTS and DTR if flow is 'r' --- resend

Quoted 6 lines · Yinghai Lu

[PATCH] serial: set RTS and DTR if flow is 'r'

if the serial console flow is set to 'r', We need to set RTS and DTR. Some UARTs on other side need these bit set, otherwise will not send char to or
receive char from the host that kernel is runing esp for kernel boot stage.

Patch is still broken. Patch is completely wrong as well - we do _not_ want to set the RTS and DTR bits here, and is in fact undoing a fix I put in over the 2.4 drivers.

If CRTSCTS is enabled via userspace, and the port is not being used as a console, we do not want to raise RTS _until_ we are ready to receive characters. Your patch breaks this.

Moreover, I question the reasoning. RTS is normally raised to allow the remote end to send characters. Since the kernel console is output _only_, until userspace opens it, there's little point in raising RTS. RTS will be raised when userspace opens it.

There may be some sense, however, in allowing DTR to be permanently raised for serial consoles. However, I would err on caution since DTR is used to reliably instruct modems to drop the line, so making such a change would be undesirable for those folk.

Signature · 3 lines
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:
#17 lore raw

Re: [PATCH] serial: set RTS and DTR if flow is 'r' --- resend

Quoted 5 lines · Russell King

There may be some sense, however, in allowing DTR to be permanently raised for serial consoles. However, I would err on caution since DTR is used to reliably instruct modems to drop the line, so making such a change would be undesirable for those folk.

please check following is OK.

YH

[PATCH] serial: set DTR in uart for kernel serial console

Some UARTs on other side need host uart DTR is set, otherwise will not receive char from the host that kernel is runing during kernel boot stage.

BTW:
earlyprintk and early_uart are hard coded to set DTR/RTS.

Cc Russell King <[email protected]>
Cc Andi Kleen <[email protected]>
Cc Bjorn Helgaas <[email protected]>

*diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c*
index 48e259a..29c051c 100644
*--- a/drivers/serial/8250.c*
*+++ b/drivers/serial/8250.c*
@@ -2154,6 +2154,12 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,

		}
		serial_outp(up, UART_FCR, fcr);		/* set fcr */
	}

+
+ /* some uarts on other side don't support no flow control. So we set
+ * DTR in host uart to make them happy
+ */
+ up->port.mctrl |= TIOCM_DTR;
+

	serial8250_set_mctrl(&up->port, up->port.mctrl);
	spin_unlock_irqrestore(&up->port.lock, flags);
}
#18 lore raw

Re: [PATCH] serial: set RTS and DTR if flow is 'r' --- resend

Quoted 7 lines · Yinghai Lu
Quoted 5 lines · Russell King

There may be some sense, however, in allowing DTR to be permanently raised for serial consoles. However, I would err on caution since DTR is used to reliably instruct modems to drop the line, so making such a change would be undesirable for those folk.

please check following is OK.

Still wrong, and unfortunately you haven't understood what I was saying at all because this is worse. ;(

Do not put the code in serial8250_set_termios. It is the wrong place.

Quoted 35 lines

YH

[PATCH] serial: set DTR in uart for kernel serial console

Some UARTs on other side need host uart DTR is set, otherwise will not receive char from the host that kernel is runing during kernel boot stage.

BTW:
earlyprintk and early_uart are hard coded to set DTR/RTS.

Cc Russell King <[email protected]>
Cc Andi Kleen <[email protected]>
Cc Bjorn Helgaas <[email protected]>

*diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c* index 48e259a..29c051c 100644
*--- a/drivers/serial/8250.c*
*+++ b/drivers/serial/8250.c*

(quoted diff) +6 -0
@@ -2154,6 +2154,12 @@ serial8250_set_termios(struct uart_port *port, 
 struct ktermios *termios,
 		}
 		serial_outp(up, UART_FCR, fcr);		/* set fcr */
 	}
+
+	/* some uarts on other side don't support no flow control. So we set
+	 * DTR in host uart to make them happy
+	 */
+	up->port.mctrl |= TIOCM_DTR;
+
 	serial8250_set_mctrl(&up->port, up->port.mctrl);
 	spin_unlock_irqrestore(&up->port.lock, flags);

}

Signature · 3 lines
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:
#19 lore raw

Re: [PATCH] serial: set RTS and DTR if flow is 'r' --- resend

Quoted 5 lines · Russell King

Still wrong, and unfortunately you haven't understood what I was saying at all because this is worse. ;(

Do not put the code in serial8250_set_termios. It is the wrong place.

I don't want to put that there. the problem is serial8250_set_mctrl called by will clear DTR bit.

uart_set_options==>serial8250_set_termios ==> serial8250_set_mctrl will clear DTR bit

YH

static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) {
struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned char mcr = 0;

if (mctrl & TIOCM_RTS)
mcr |= UART_MCR_RTS;
if (mctrl & TIOCM_DTR)
mcr |= UART_MCR_DTR;
if (mctrl & TIOCM_OUT1)
mcr |= UART_MCR_OUT1;
if (mctrl & TIOCM_OUT2)
mcr |= UART_MCR_OUT2;
if (mctrl & TIOCM_LOOP)
mcr |= UART_MCR_LOOP;

mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;

serial_out(up, UART_MCR, mcr);
}

#20 lore raw

Re: [PATCH] serial: set RTS and DTR if flow is 'r' --- resend

Quoted 9 lines · Yinghai Lu
Quoted 5 lines · Russell King

Still wrong, and unfortunately you haven't understood what I was saying at all because this is worse. ;(

Do not put the code in serial8250_set_termios. It is the wrong place.

I don't want to put that there. the problem is serial8250_set_mctrl called by will clear DTR bit.

We're at a stalemate then.

Signature · 3 lines
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:
#21 lore raw

Re: [PATCH] serial: set RTS and DTR if flow is 'r' --- resend

Quoted 8 lines · Russell King
Quoted 2 lines · Yinghai Lu

I don't want to put that there. the problem is serial8250_set_mctrl called by will clear DTR bit.

We're at a stalemate then.

How about put that line into
serial8250_console_setup
or
serial8250_isa_init_ports

YH

#22 lore raw

PATCH 1/1 serial: set DTR in uart for kernel serial console

In reply to Yinghai Lu

Hope format is OK. Sending with KMail Message/Insert File

YH

[PATCH] serial: set DTR in uart for kernel serial console

Some UARTs on other side need host uart DTR is set, otherwise will not receive char from the host that kernel is runing during kernel boot stage.

BTW:
earlyprintk and early_uart are hard coded to set DTR/RTS.

Cc Russell King <[email protected]>
Cc Andi Kleen <[email protected]>
Cc Bjorn Helgaas <[email protected]>
drivers/serial/8250.c +5 -0
index 48e259a..a925547 100644
@@ -2397,6 +2397,11 @@ static void __init serial8250_isa_init_ports(void)
 		up->mcr_force = ALPHA_KLUDGE_MCR;
 
 		up->port.ops = &serial8250_pops;
+
+		/* some uarts on other side don't support no flow control. So we set
+		 * DTR in host uart to make them happy  --- YHLU
+		 */
+		up->port.mctrl |= TIOCM_DTR; 
 	}
 
 	for (i = 0, up = serial8250_ports;
#23 lore raw

PATCH] serial: convert early_uart to earlycon for 8250

PATCH] serial: convert early_uart to earlycon for 8250

Beacuse SERIAL_PORT_DFNS is removed from include/asm-i386/serial.h and include/asm-x86_64/serial.h. the serial8250_ports need to be probed late in serial initializing stage. the console_init=>serial8250_console_init=> register_console=>serial8250_console_setup will return -ENDEV, and console ttyS0 can not be enabled at that time.
need to wait till uart_add_one_port in drivers/serial/serial_core.c to call register_console to get console ttyS0. that is too late.

Make early_uart to use early_param, so uart console can be used earlier. Make it to be bootconsole with CON_BOOT flag, so can use console handover feature. and it will switch to corresponding normal serial console automatically.

new command line will be:

	console=uart8250,io,0x3f8,9600n8
	console=uart8250,mmio,0xff5e0000,115200n8
or
	earlycon=uart8250,io,0x3f8,9600n8
	earlycon=uart8250,mmio,0xff5e0000,115200n8

it will print in very early stage:

	Early serial console at I/O port 0x3f8 (options '9600n8')
	console [uart0] enabled
later for console it will print:
	console handover: boot [uart0] -> real [ttyS0]
Signed-off-by <[email protected]>
 Documentation/kernel-parameters.txt |   12 +++-
 arch/i386/kernel/head.S             |    8 ++
 arch/ia64/kernel/setup.c            |    4 -
 arch/x86_64/kernel/head.S           |   15 ++++-
 drivers/serial/8250.c               |   26 +--------
 drivers/serial/8250_early.c         |  103 ++++++++++++++----------------------
 drivers/serial/Kconfig              |    8 ++
 drivers/serial/serial_core.c        |    5 +
 include/asm-i386/fixmap.h           |    2 
 include/asm-i386/io.h               |   13 ++++
 include/asm-ia64/io.h               |    4 +
 include/asm-x86_64/fixmap.h         |    4 +
 include/asm-x86_64/io.h             |   13 ++++
 include/linux/console.h             |    1 
 include/linux/serial.h              |    6 --
 include/linux/serial_8250.h         |    3 +
 include/linux/serial_core.h         |    5 +
 init/main.c                         |    5 +
 kernel/printk.c                     |   17 +++++
 19 files changed, 156 insertions(+), 98 deletions(-)
Documentation/kernel-parameters.txt +10 -2
index 09220a1..634d809 100644
@@ -444,8 +444,16 @@ and is between 256 and 4096 characters. It is defined in the file
 			Documentation/networking/netconsole.txt for an
 			alternative.
 
-		uart,io,<addr>[,options]
-		uart,mmio,<addr>[,options]
+		uart8250,io,<addr>[,options]
+		uart8250,mmio,<addr>[,options]
+			Start an early, polled-mode console on the 8250/16550
+			UART at the specified I/O port or MMIO address,
+			switching to the matching ttyS device later.  The
+			options are the same as for ttyS, above.
+
+	earlycon=	[KNL] Output early console device and options.
+		uart8250,io,<addr>[,options]
+		uart8250,mmio,<addr>[,options]
 			Start an early, polled-mode console on the 8250/16550
 			UART at the specified I/O port or MMIO address,
 			switching to the matching ttyS device later.  The
arch/i386/kernel/head.S +8 -0
index f74dfc4..8271466 100644
@@ -168,6 +168,12 @@ page_pde_offset = (__PAGE_OFFSET >> 20);
 .section .init.text,"ax",@progbits
 #endif
 
+	/* Do an early initialization of the fixmap area */
+	movl $(swapper_pg_dir - __PAGE_OFFSET), %edx
+	movl $(swapper_pg_pmd - __PAGE_OFFSET), %eax
+	addl $0x007, %eax			/* 0x007 = PRESENT+RW+USER */
+	movl %eax, 4092(%edx)
+
 #ifdef CONFIG_SMP
 ENTRY(startup_32_smp)
 	cld
@@ -507,6 +513,8 @@ ENTRY(_stext)
 .section ".bss.page_aligned","w"
 ENTRY(swapper_pg_dir)
 	.fill 1024,4,0
+ENTRY(swapper_pg_pmd)
+	.fill 1024,4,0
 ENTRY(empty_zero_page)
 	.fill 4096,1,0
 
arch/ia64/kernel/setup.c +0 -4
index eaa6a24..dd7f95b 100644
@@ -390,10 +390,6 @@ early_console_setup (char *cmdline)
 	if (!efi_setup_pcdp_console(cmdline))
 		earlycons++;
 #endif
-#ifdef CONFIG_SERIAL_8250_CONSOLE
-	if (!early_serial_console_init(cmdline))
-		earlycons++;
-#endif
 
 	return (earlycons) ? 0 : -1;
 }
arch/x86_64/kernel/head.S +14 -1
index 1fab487..941c84b 100644
@@ -73,7 +73,11 @@ startup_64:
 	addq	%rbp, init_level4_pgt + (511*8)(%rip)
 
 	addq	%rbp, level3_ident_pgt + 0(%rip)
+
 	addq	%rbp, level3_kernel_pgt + (510*8)(%rip)
+	addq	%rbp, level3_kernel_pgt + (511*8)(%rip)
+
+	addq	%rbp, level2_fixmap_pgt + (506*8)(%rip)
 
 	/* Add an Identity mapping if I am above 1G */
 	leaq	_text(%rip), %rdi
@@ -314,7 +318,16 @@ NEXT_PAGE(level3_kernel_pgt)
 	.fill	510,8,0
 	/* (2^48-(2*1024*1024*1024)-((2^39)*511))/(2^30) = 510 */
 	.quad	level2_kernel_pgt - __START_KERNEL_map + _KERNPG_TABLE
-	.fill	1,8,0
+	.quad	level2_fixmap_pgt - __START_KERNEL_map + _PAGE_TABLE
+
+NEXT_PAGE(level2_fixmap_pgt)
+	.fill	506,8,0
+	.quad	level1_fixmap_pgt - __START_KERNEL_map + _PAGE_TABLE
+	/* 8MB reserved for vsyscalls + a 2MB hole = 4 + 1 entries */
+	.fill	5,8,0
+
+NEXT_PAGE(level1_fixmap_pgt)
+	.fill	512,8,0
 
 NEXT_PAGE(level2_ident_pgt)
 	/* Since I easily can, map the first 1G.
drivers/scsi/Kconfig +0 -0
drivers/serial/8250.c +5 -21
index c84dab0..e341fb9 100644
@@ -2367,6 +2367,9 @@ static struct uart_ops serial8250_pops = {
 	.request_port	= serial8250_request_port,
 	.config_port	= serial8250_config_port,
 	.verify_port	= serial8250_verify_port,
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+	.find_port_for_earlycon = serial8250_find_port_for_earlycon,
+#endif
 };
 
 static struct uart_8250_port serial8250_ports[UART_NR];
@@ -2402,6 +2405,7 @@ static void __init serial8250_isa_init_ports(void)
 	for (i = 0, up = serial8250_ports;
 	     i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
 	     i++, up++) {
+		printk(KERN_INFO "serial8250_isa_init_ports 2  idx=%d\n",i);
 		up->port.iobase   = old_serial_port[i].port;
 		up->port.irq      = irq_canonicalize(old_serial_port[i].irq);
 		up->port.uartclk  = old_serial_port[i].baud_base * 16;
@@ -2533,7 +2537,7 @@ static int __init serial8250_console_init(void)
 }
 console_initcall(serial8250_console_init);
 
-static int __init find_port(struct uart_port *p)
+int __init find_port_serial8250(struct uart_port *p)
 {
 	int line;
 	struct uart_port *port;
@@ -2546,26 +2550,6 @@ static int __init find_port(struct uart_port *p)
 	return -ENODEV;
 }
 
-int __init serial8250_start_console(struct uart_port *port, char *options)
-{
-	int line;
-
-	line = find_port(port);
-	if (line < 0)
-		return -ENODEV;
-
-	add_preferred_console("ttyS", line, options);
-	printk("Adding console on ttyS%d at %s 0x%lx (options '%s')\n",
-		line, port->iotype == UPIO_MEM ? "MMIO" : "I/O port",
-		port->iotype == UPIO_MEM ? (unsigned long) port->mapbase :
-		    (unsigned long) port->iobase, options);
-	if (!(serial8250_console.flags & CON_ENABLED)) {
-		serial8250_console.flags &= ~CON_PRINTBUFFER;
-		register_console(&serial8250_console);
-	}
-	return line;
-}
-
 #define SERIAL8250_CONSOLE	&serial8250_console
 #else
 #define SERIAL8250_CONSOLE	NULL
drivers/serial/8250_early.c +42 -61

178 lines not on this page · raw message

drivers/serial/Kconfig +8 -0
index 315ea99..4206ace 100644
@@ -62,6 +62,14 @@ config SERIAL_8250_CONSOLE
 	  kernel will automatically use the first serial line, /dev/ttyS0, as
 	  system console.
 
+	  you can set that using a kernel command line option such as
+	  "earlycon=uart8250,io,0x3f8,9600n8"
+	  "earlycon=uart8250,mmio,0xff5e0000,115200n8" or
+	  "console=uart8250,io,0x3f8,9600n8"
+	  "console=uart8250,mmio,0xff5e0000,115200n8".
+	  and it will switch to normal serial console when correponding port is
+	  ready.
+
 	  If unsure, say N.
 
 config SERIAL_8250_GSC
drivers/serial/serial_core.c +4 -1
index 326020f..4f60767 100644
@@ -2303,8 +2303,11 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
 	 * It may be that the port was not available.
 	 */
 	if (port->type != PORT_UNKNOWN &&
-	    port->cons && !(port->cons->flags & CON_ENABLED))
+	    port->cons && !(port->cons->flags & CON_ENABLED)) {
+		if(port->ops && port->ops->find_port_for_earlycon)
+			port->ops->find_port_for_earlycon();
 		register_console(port->cons);
+	}
 
 	/*
 	 * Ensure UPF_DEAD is not set.
include/asm-i386/fixmap.h +2 -0
index 80ea052..249e753 100644
@@ -54,6 +54,8 @@ extern unsigned long __FIXADDR_TOP;
 enum fixed_addresses {
 	FIX_HOLE,
 	FIX_VDSO,
+	FIX_DBGP_BASE,
+	FIX_EARLYCON_MEM_BASE,
 #ifdef CONFIG_X86_LOCAL_APIC
 	FIX_APIC_BASE,	/* local (CPU) APIC) -- required for SMP or not */
 #endif
include/asm-i386/io.h +13 -0
index e797586..6c8748d 100644
@@ -130,6 +130,19 @@ extern void iounmap(volatile void __iomem *addr);
 extern void *bt_ioremap(unsigned long offset, unsigned long size);
 extern void bt_iounmap(void *addr, unsigned long size);
 
+#include <asm/pgtable.h>
+#include <asm/fixmap.h>
+
+static inline void __iomem * fix_ioremap (unsigned idx, unsigned long phys)
+{
+	void __iomem * vaddr;
+	set_fixmap_nocache(idx, phys & PAGE_MASK);
+	vaddr = (void __iomem *)__fix_to_virt(idx);
+	vaddr += phys & ~PAGE_MASK;
+
+	return vaddr;
+}
+
 /* Use early IO mappings for DMI because it's initialized early */
 #define dmi_ioremap bt_ioremap
 #define dmi_iounmap bt_iounmap
include/asm-ia64/io.h +4 -0
index eb17a86..e29eaf8 100644
@@ -423,6 +423,10 @@ extern void __iomem * ioremap(unsigned long offset, unsigned long size);
 extern void __iomem * ioremap_nocache (unsigned long offset, unsigned long size);
 extern void iounmap (volatile void __iomem *addr);
 
+/* for console=uart8250,mmio,0xffe5000,9600n8 */
+#define FIX_EARLYCON_MEM_BASE 1
+#define fix_ioremap(idx, phys) ioremap(phys, 64)
+
 /* Use normal IO mappings for DMI */
 #define dmi_ioremap ioremap
 #define dmi_iounmap(x,l) iounmap(x)
include/asm-x86_64/fixmap.h +3 -1
index e90e167..2acb9b7 100644
@@ -35,6 +35,8 @@ enum fixed_addresses {
 	VSYSCALL_LAST_PAGE,
 	VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1,
 	VSYSCALL_HPET,
+	FIX_DBGP_BASE,
+	FIX_EARLYCON_MEM_BASE,
 	FIX_HPET_BASE,
 	FIX_APIC_BASE,	/* local (CPU) APIC) -- required for SMP or not */
 	FIX_IO_APIC_BASE_0,
@@ -84,7 +86,7 @@ static __always_inline unsigned long fix_to_virt(const unsigned int idx)
 	if (idx >= __end_of_fixed_addresses)
 		__this_fixmap_does_not_exist();
 
-        return __fix_to_virt(idx);
+	return __fix_to_virt(idx);
 }
 
 #endif
include/asm-x86_64/io.h +13 -0
index de2cd9a..e2d66de 100644
@@ -145,6 +145,19 @@ extern void early_iounmap(void *addr, unsigned long size);
 extern void __iomem * ioremap_nocache (unsigned long offset, unsigned long size);
 extern void iounmap(volatile void __iomem *addr);
 
+#include <asm/pgtable.h>
+#include <asm/fixmap.h>
+
+static inline void __iomem * fix_ioremap (unsigned idx, unsigned long phys)
+{
+	void __iomem * vaddr;
+	set_fixmap_nocache(idx, phys & PAGE_MASK);
+	vaddr = (void __iomem *)__fix_to_virt(idx);
+	vaddr += phys & ~PAGE_MASK;
+
+	return vaddr;
+}
+
 /*
  * ISA I/O bus memory addresses are 1:1 with the physical address.
  */
include/linux/console.h +1 -0
index 62ef6e1..4cb7749 100644
@@ -107,6 +107,7 @@ struct console {
 };
 
 extern int add_preferred_console(char *name, int idx, char *options);
+extern int update_console_cmdline_console_index(char *name, int idx_old, int idx_new);
 extern void register_console(struct console *);
 extern int unregister_console(struct console *);
 extern struct console *console_drivers;
include/linux/serial.h +0 -6
index 33fc8cb..deb7143 100644
@@ -177,11 +177,5 @@ struct serial_icounter_struct {
 #ifdef __KERNEL__
 #include <linux/compiler.h>
 
-/* Allow architectures to override entries in serial8250_ports[] at run time: */
-struct uart_port;	/* forward declaration */
-extern int early_serial_setup(struct uart_port *port);
-extern int early_serial_console_init(char *options);
-extern int serial8250_start_console(struct uart_port *port, char *options);
-
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SERIAL_H */
include/linux/serial_8250.h +3 -0
index 71310d8..0d79fc5 100644
@@ -60,4 +60,7 @@ void serial8250_unregister_port(int line);
 void serial8250_suspend_port(int line);
 void serial8250_resume_port(int line);
 
+extern int find_port_serial8250(struct uart_port *p);
+extern int serial8250_find_port_for_earlycon(void);
+
 #endif
include/linux/serial_core.h +5 -0
index a3ac4c8..ac2b13c 100644
@@ -199,6 +199,11 @@ struct uart_ops {
 	void		(*config_port)(struct uart_port *, int);
 	int		(*verify_port)(struct uart_port *, struct serial_struct *);
 	int		(*ioctl)(struct uart_port *, unsigned int, unsigned long);
+
+	/*
+	 * for earlycon to console switch
+	 */
+	int		(*find_port_for_earlycon)(void);
 };
 
 #define UART_CONFIG_TYPE	(1 << 0)
init/main.c +4 -1
index eb8bdba..58dbe75 100644
@@ -452,7 +452,10 @@ static int __init do_early_param(char *param, char *val)
 	struct obs_kernel_param *p;
 
 	for (p = __setup_start; p < __setup_end; p++) {
-		if (p->early && strcmp(param, p->str) == 0) {
+		if ((p->early && strcmp(param, p->str) == 0) ||
+		    (strcmp(param, "console") == 0 &&
+		     strcmp(p->str, "earlycon") == 0)
+		) {
 			if (p->setup_func(val) != 0)
 				printk(KERN_WARNING
 				       "Malformed early option '%s'\n", param);
kernel/printk.c +17 -0
index 0bbdeac..8f3d91e 100644
@@ -726,6 +726,20 @@ int __init add_preferred_console(char *name, int idx, char *options)
 	return 0;
 }
 
+int __init update_console_cmdline_console_index(char *name, int idx_old, int idx_new)
+{
+	int i;
+
+	for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
+		if (strcmp(console_cmdline[i].name, name) == 0 &&
+			  console_cmdline[i].index == idx_old) {
+				console_cmdline[i].index = idx_new;
+				return 0;
+		}
+
+	return 0;
+}
+
 #ifndef CONFIG_DISABLE_CONSOLE_SUSPEND
 /**
  * suspend_console - suspend the console subsystem
@@ -991,6 +1005,9 @@ void register_console(struct console *console)
 		       console->name, console->index);
 		unregister_console(bootconsole);
 		console->flags &= ~CON_PRINTBUFFER;
+	} else {
+		printk(KERN_INFO "console [%s%d] enabled\n",
+		       console->name, console->index);
 	}
 
 	/*
#24 lore raw

Re: PATCH] serial: convert early_uart to earlycon for 8250

I can't comment on the arch specific bits. As a general note, I think this is over complex. For instance, the additional hook in serial_core to call the find_port_for_earlycon method isn't needed because you can call serial8250_find_port_for_earlycon() from within serial8250_console_setup(). You can also modify co->index from within there without needing update_console_cmdline_console_index().

Bjorn needs to review the 8250_early changes.

Apart from that, two other comments:

drivers/serial/8250.c static void __init serial8250_isa_init_ports(void) · line 2408

Quoted 9 lines · Yinghai Lu
drivers/serial/8250.c +1 -0
index c84dab0..e341fb9 100644
@@ -2402,6 +2405,7 @@ static void __init serial8250_isa_init_ports(void)
 	for (i = 0, up = serial8250_ports;
 	     i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
 	     i++, up++) {
+		printk(KERN_INFO "serial8250_isa_init_ports 2  idx=%d\n",i);

Is this a debugging printk?

drivers/serial/8250.c static int __init serial8250_console_init(void) · line 2540

Quoted 9 lines
drivers/serial/8250.c +1 -1
 		up->port.iobase   = old_serial_port[i].port;
 		up->port.irq      = irq_canonicalize(old_serial_port[i].irq);
 		up->port.uartclk  = old_serial_port[i].baud_base * 16;
@@ -2533,7 +2537,7 @@ static int __init serial8250_console_init(void)
 }
 console_initcall(serial8250_console_init);
 
-static int __init find_port(struct uart_port *p)
+int __init find_port_serial8250(struct uart_port *p)

If this is going to become globally visible, please name it serial8250_find_port to match the style of the rest of the file.

Quoted 9 lines
drivers/serial/8250.c +0 -0
 {
 	int line;
 	struct uart_port *port;
include/linux/serial.h +0 -0
index 33fc8cb..deb7143 100644
@@ -177,11 +177,5 @@ struct serial_icounter_struct {
 #ifdef __KERNEL__

include/linux/serial.h struct serial_icounter_struct { · line 181

Quoted 10 lines
include/linux/serial.h +0 -6
 #include <linux/compiler.h>
 
-/* Allow architectures to override entries in serial8250_ports[] at run time: */
-struct uart_port;	/* forward declaration */
-extern int early_serial_setup(struct uart_port *port);
-extern int early_serial_console_init(char *options);
-extern int serial8250_start_console(struct uart_port *port, char *options);
-
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SERIAL_H */

Good - this needed to be killed.

Signature · 3 lines
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:
author
#25 lore raw

Re: PATCH] serial: convert early_uart to earlycon for 8250

Quoted 6 lines · Russell King

I can't comment on the arch specific bits. As a general note, I think this is over complex. For instance, the additional hook in serial_core to call the find_port_for_earlycon method isn't needed because you can call serial8250_find_port_for_earlycon() from within serial8250_console_setup(). You can also modify co->index from within there without needing update_console_cmdline_console_index().

Good point, I will try to remove that hook.

Quoted 6 lines

Bjorn needs to review the 8250_early changes.

Apart from that, two other comments:

On Tue, May 22, 2007 at 12:31:59PM -0700, Yinghai Lu wrote:

Quoted 11 lines

drivers/serial/8250.c static void __init serial8250_isa_init_ports(void) · line 2408

Quoted 9 lines
drivers/serial/8250.c +1 -0
index c84dab0..e341fb9 100644
@@ -2402,6 +2405,7 @@ static void __init serial8250_isa_init_ports(void)
      for (i = 0, up = serial8250_ports;
           i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
           i++, up++) {
+             printk(KERN_INFO "serial8250_isa_init_ports 2  idx=%d\n",i);

Is this a debugging printk?

Oh.

Quoted 8 lines
Quoted 8 lines
drivers/serial/8250.c +0 -0
              up->port.iobase   = old_serial_port[i].port;
              up->port.irq      = irq_canonicalize(old_serial_port[i].irq);
              up->port.uartclk  = old_serial_port[i].baud_base * 16;
@@ -2533,7 +2537,7 @@ static int __init serial8250_console_init(void)
 }
 console_initcall(serial8250_console_init);
 
Quoted 5 lines

drivers/serial/8250.c static int __init serial8250_console_init(void) · line 2540

Quoted 2 lines
drivers/serial/8250.c +1 -1
-static int __init find_port(struct uart_port *p)
+int __init find_port_serial8250(struct uart_port *p)

If this is going to become globally visible, please name it serial8250_find_port to match the style of the rest of the file.

I see.

YH