When accessing data that is not "near", either only one segment is
used or one segment is always zero. Leave %gs == 0 at all times
throughout the C code; this reduces the number of segment loads
needed.
Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
---
arch/x86/boot/a20.c | 11 +++++------
arch/x86/boot/header.S | 3 +++
arch/x86/boot/regs.c | 3 ++-
arch/x86/boot/video-bios.c | 5 ++---
arch/x86/boot/video-mode.c | 5 ++---
arch/x86/boot/video.c | 7 +++----
arch/x86/realmode/rm/wakeup_asm.S | 10 ++++++----
7 files changed, 23 insertions(+), 21 deletions(-)
diff --git a/arch/x86/boot/a20.c b/arch/x86/boot/a20.c
index bda042933a05..3ab6cd8eaa31 100644
--- a/arch/x86/boot/a20.c
+++ b/arch/x86/boot/a20.c
@@ -56,20 +56,19 @@ static int a20_test(int loops)
int ok = 0;
int saved, ctr;
- set_fs(0x0000);
- set_gs(0xffff);
+ set_fs(0xffff);
- saved = ctr = rdfs32(A20_TEST_ADDR);
+ saved = ctr = rdgs32(A20_TEST_ADDR);
while (loops--) {
- wrfs32(++ctr, A20_TEST_ADDR);
+ wrgs32(++ctr, A20_TEST_ADDR);
io_delay(); /* Serialize and make delay constant */
- ok = rdgs32(A20_TEST_ADDR+0x10) ^ ctr;
+ ok = rdfs32(A20_TEST_ADDR+0x10) ^ ctr;
if (ok)
break;
}
- wrfs32(saved, A20_TEST_ADDR);
+ wrgs32(saved, A20_TEST_ADDR);
return ok;
}
diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S
index 9bea5a1e2c52..bda20395658f 100644
--- a/arch/x86/boot/header.S
+++ b/arch/x86/boot/header.S
@@ -596,6 +596,9 @@ start_of_setup:
shrw $2, %cx
rep stosl
+# The C code uses %gs == 0 as invariant
+ movw %ax, %gs
+
# Jump to C code (should not return)
calll main
diff --git a/arch/x86/boot/regs.c b/arch/x86/boot/regs.c
index 55de6b3092b8..54d6dfd129c5 100644
--- a/arch/x86/boot/regs.c
+++ b/arch/x86/boot/regs.c
@@ -22,6 +22,7 @@ void initregs(struct biosregs *reg)
reg->eflags |= X86_EFLAGS_CF;
reg->ds = ds();
reg->es = ds();
+ /* The input values of %cs and %ss are ignored by intcall() */
reg->fs = fs();
- reg->gs = gs();
+ /* %gs == 0 */
}
diff --git a/arch/x86/boot/video-bios.c b/arch/x86/boot/video-bios.c
index 6eb8c06bc287..e8be64424a40 100644
--- a/arch/x86/boot/video-bios.c
+++ b/arch/x86/boot/video-bios.c
@@ -73,7 +73,6 @@ static int bios_probe(void)
if (adapter != ADAPTER_EGA && adapter != ADAPTER_VGA)
return 0;
- set_fs(0);
crtc = vga_crtc();
video_bios.modes = GET_HEAP(struct mode_info, 0);
@@ -105,8 +104,8 @@ static int bios_probe(void)
mi = GET_HEAP(struct mode_info, 1);
mi->mode = VIDEO_FIRST_BIOS+mode;
mi->depth = 0; /* text */
- mi->x = rdfs16(0x44a);
- mi->y = rdfs8(0x484)+1;
+ mi->x = rdgs16(0x44a);
+ mi->y = rdgs8(0x484)+1;
nmodes++;
}
diff --git a/arch/x86/boot/video-mode.c b/arch/x86/boot/video-mode.c
index 9ada55dc1ab7..e5b9bc96bd42 100644
--- a/arch/x86/boot/video-mode.c
+++ b/arch/x86/boot/video-mode.c
@@ -119,9 +119,8 @@ static void vga_recalc_vertical(void)
u16 crtc;
u8 pt, ov;
- set_fs(0);
- font_size = rdfs8(0x485); /* BIOS: font size (pixels) */
- rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */
+ font_size = rdgs8(0x485); /* BIOS: font size (pixels) */
+ rows = force_y ? force_y : rdgs8(0x484)+1; /* Text rows */
rows *= font_size; /* Visible scan lines */
rows--; /* ... minus one */
diff --git a/arch/x86/boot/video.c b/arch/x86/boot/video.c
index 0641c8c46aee..09b810faa5c0 100644
--- a/arch/x86/boot/video.c
+++ b/arch/x86/boot/video.c
@@ -79,12 +79,11 @@ static void store_mode_params(void)
video_segment = 0xb800;
}
- set_fs(0);
- font_size = rdfs16(0x485); /* Font size, BIOS area */
+ font_size = rdgs16(0x485); /* Font size, BIOS area */
boot_params.screen_info.orig_video_points = font_size;
- x = rdfs16(0x44a);
- y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1;
+ x = rdgs16(0x44a);
+ y = (adapter == ADAPTER_CGA) ? 25 : rdgs8(0x484)+1;
if (force_x)
x = force_x;
diff --git a/arch/x86/realmode/rm/wakeup_asm.S b/arch/x86/realmode/rm/wakeup_asm.S
index 02d0ba16ae33..a8a8580158d7 100644
--- a/arch/x86/realmode/rm/wakeup_asm.S
+++ b/arch/x86/realmode/rm/wakeup_asm.S
@@ -70,15 +70,17 @@ SYM_CODE_START(wakeup_start)
movl $rm_stack_end, %esp
movw %ax, %ds
movw %ax, %es
- movw %ax, %fs
- movw %ax, %gs
- lidtl .Lwakeup_idt
+ xorl %eax, %eax
+ movw %ax, %fs
+ movw %ax, %gs /* The real mode code requires %gs == 0 */
/* Clear the EFLAGS */
- pushl $0
+ pushl %eax
popfl
+ lidtl .Lwakeup_idt
+
/* Check header signature... */
movl signature, %eax
cmpl $WAKEUP_HEADER_SIGNATURE, %eax
--
2.52.0
On Tue, Jan 20, 2026 at 8:54 PM H. Peter Anvin <hpa@zytor.com> wrote:
>
> When accessing data that is not "near", either only one segment is
> used or one segment is always zero. Leave %gs == 0 at all times
> throughout the C code; this reduces the number of segment loads
> needed.
>
> Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
Reviewed-by: Uros Bizjak <ubizjak@gmail.com>
> ---
> arch/x86/boot/a20.c | 11 +++++------
> arch/x86/boot/header.S | 3 +++
> arch/x86/boot/regs.c | 3 ++-
> arch/x86/boot/video-bios.c | 5 ++---
> arch/x86/boot/video-mode.c | 5 ++---
> arch/x86/boot/video.c | 7 +++----
> arch/x86/realmode/rm/wakeup_asm.S | 10 ++++++----
> 7 files changed, 23 insertions(+), 21 deletions(-)
>
> diff --git a/arch/x86/boot/a20.c b/arch/x86/boot/a20.c
> index bda042933a05..3ab6cd8eaa31 100644
> --- a/arch/x86/boot/a20.c
> +++ b/arch/x86/boot/a20.c
> @@ -56,20 +56,19 @@ static int a20_test(int loops)
> int ok = 0;
> int saved, ctr;
>
> - set_fs(0x0000);
> - set_gs(0xffff);
> + set_fs(0xffff);
>
> - saved = ctr = rdfs32(A20_TEST_ADDR);
> + saved = ctr = rdgs32(A20_TEST_ADDR);
>
> while (loops--) {
> - wrfs32(++ctr, A20_TEST_ADDR);
> + wrgs32(++ctr, A20_TEST_ADDR);
> io_delay(); /* Serialize and make delay constant */
> - ok = rdgs32(A20_TEST_ADDR+0x10) ^ ctr;
> + ok = rdfs32(A20_TEST_ADDR+0x10) ^ ctr;
> if (ok)
> break;
> }
>
> - wrfs32(saved, A20_TEST_ADDR);
> + wrgs32(saved, A20_TEST_ADDR);
> return ok;
> }
>
> diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S
> index 9bea5a1e2c52..bda20395658f 100644
> --- a/arch/x86/boot/header.S
> +++ b/arch/x86/boot/header.S
> @@ -596,6 +596,9 @@ start_of_setup:
> shrw $2, %cx
> rep stosl
>
> +# The C code uses %gs == 0 as invariant
> + movw %ax, %gs
> +
> # Jump to C code (should not return)
> calll main
>
> diff --git a/arch/x86/boot/regs.c b/arch/x86/boot/regs.c
> index 55de6b3092b8..54d6dfd129c5 100644
> --- a/arch/x86/boot/regs.c
> +++ b/arch/x86/boot/regs.c
> @@ -22,6 +22,7 @@ void initregs(struct biosregs *reg)
> reg->eflags |= X86_EFLAGS_CF;
> reg->ds = ds();
> reg->es = ds();
> + /* The input values of %cs and %ss are ignored by intcall() */
> reg->fs = fs();
> - reg->gs = gs();
> + /* %gs == 0 */
> }
> diff --git a/arch/x86/boot/video-bios.c b/arch/x86/boot/video-bios.c
> index 6eb8c06bc287..e8be64424a40 100644
> --- a/arch/x86/boot/video-bios.c
> +++ b/arch/x86/boot/video-bios.c
> @@ -73,7 +73,6 @@ static int bios_probe(void)
> if (adapter != ADAPTER_EGA && adapter != ADAPTER_VGA)
> return 0;
>
> - set_fs(0);
> crtc = vga_crtc();
>
> video_bios.modes = GET_HEAP(struct mode_info, 0);
> @@ -105,8 +104,8 @@ static int bios_probe(void)
> mi = GET_HEAP(struct mode_info, 1);
> mi->mode = VIDEO_FIRST_BIOS+mode;
> mi->depth = 0; /* text */
> - mi->x = rdfs16(0x44a);
> - mi->y = rdfs8(0x484)+1;
> + mi->x = rdgs16(0x44a);
> + mi->y = rdgs8(0x484)+1;
> nmodes++;
> }
>
> diff --git a/arch/x86/boot/video-mode.c b/arch/x86/boot/video-mode.c
> index 9ada55dc1ab7..e5b9bc96bd42 100644
> --- a/arch/x86/boot/video-mode.c
> +++ b/arch/x86/boot/video-mode.c
> @@ -119,9 +119,8 @@ static void vga_recalc_vertical(void)
> u16 crtc;
> u8 pt, ov;
>
> - set_fs(0);
> - font_size = rdfs8(0x485); /* BIOS: font size (pixels) */
> - rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */
> + font_size = rdgs8(0x485); /* BIOS: font size (pixels) */
> + rows = force_y ? force_y : rdgs8(0x484)+1; /* Text rows */
>
> rows *= font_size; /* Visible scan lines */
> rows--; /* ... minus one */
> diff --git a/arch/x86/boot/video.c b/arch/x86/boot/video.c
> index 0641c8c46aee..09b810faa5c0 100644
> --- a/arch/x86/boot/video.c
> +++ b/arch/x86/boot/video.c
> @@ -79,12 +79,11 @@ static void store_mode_params(void)
> video_segment = 0xb800;
> }
>
> - set_fs(0);
> - font_size = rdfs16(0x485); /* Font size, BIOS area */
> + font_size = rdgs16(0x485); /* Font size, BIOS area */
> boot_params.screen_info.orig_video_points = font_size;
>
> - x = rdfs16(0x44a);
> - y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1;
> + x = rdgs16(0x44a);
> + y = (adapter == ADAPTER_CGA) ? 25 : rdgs8(0x484)+1;
>
> if (force_x)
> x = force_x;
> diff --git a/arch/x86/realmode/rm/wakeup_asm.S b/arch/x86/realmode/rm/wakeup_asm.S
> index 02d0ba16ae33..a8a8580158d7 100644
> --- a/arch/x86/realmode/rm/wakeup_asm.S
> +++ b/arch/x86/realmode/rm/wakeup_asm.S
> @@ -70,15 +70,17 @@ SYM_CODE_START(wakeup_start)
> movl $rm_stack_end, %esp
> movw %ax, %ds
> movw %ax, %es
> - movw %ax, %fs
> - movw %ax, %gs
>
> - lidtl .Lwakeup_idt
> + xorl %eax, %eax
> + movw %ax, %fs
> + movw %ax, %gs /* The real mode code requires %gs == 0 */
>
> /* Clear the EFLAGS */
> - pushl $0
> + pushl %eax
> popfl
>
> + lidtl .Lwakeup_idt
> +
> /* Check header signature... */
> movl signature, %eax
> cmpl $WAKEUP_HEADER_SIGNATURE, %eax
> --
> 2.52.0
>
On Tue, 20 Jan 2026 11:53:54 -0800 "H. Peter Anvin" <hpa@zytor.com> wrote: > When accessing data that is not "near", either only one segment is > used or one segment is always zero. Leave %gs == 0 at all times > throughout the C code; this reduces the number of segment loads > needed. ... > diff --git a/arch/x86/boot/a20.c b/arch/x86/boot/a20.c > index bda042933a05..3ab6cd8eaa31 100644 > --- a/arch/x86/boot/a20.c > +++ b/arch/x86/boot/a20.c > @@ -56,20 +56,19 @@ static int a20_test(int loops) > int ok = 0; > int saved, ctr; > > - set_fs(0x0000); > - set_gs(0xffff); > + set_fs(0xffff); > > - saved = ctr = rdfs32(A20_TEST_ADDR); > + saved = ctr = rdgs32(A20_TEST_ADDR); Would it be better to wrap that as (say) read_abs_32() since the objective is to read an absolute address (using the relevant zero segment register) rather than to read though either fs or gs. Is this code all running in 32bit linear mode with non-zero cs/ss/ds segment registers and 'suitable' entries in the GDT? That mode makes my brain hurt :-) Or is there a fudge to get 16bit asm from the C. David
On 2026-01-20 14:40, David Laight wrote: > > Would it be better to wrap that as (say) read_abs_32() since the > objective is to read an absolute address (using the relevant zero segment > register) rather than to read though either fs or gs. > > Is this code all running in 32bit linear mode with non-zero cs/ss/ds > segment registers and 'suitable' entries in the GDT? > That mode makes my brain hurt :-) > Or is there a fudge to get 16bit asm from the C. > To address your specific question, no I don't think that is a good idea, since it would imply that you can reach an arbitrary linear address, which is definitely not the case. At least this way the user has to look at what they are doing, which I think is a good thing for this specific code. -hpa
On 2026-01-20 14:40, David Laight wrote: > > Would it be better to wrap that as (say) read_abs_32() since the > objective is to read an absolute address (using the relevant zero segment > register) rather than to read though either fs or gs. > > Is this code all running in 32bit linear mode with non-zero cs/ss/ds > segment registers and 'suitable' entries in the GDT? > That mode makes my brain hurt :-) > Or is there a fudge to get 16bit asm from the C. > The code is running in 16-bit real mode, compiled using -m16. -hpa
© 2016 - 2026 Red Hat, Inc.