summaryrefslogtreecommitdiffstats
path: root/src/arch
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/arm/Makefile4
-rw-r--r--src/arch/arm/include/bits/acpi.h12
-rw-r--r--src/arch/arm/include/bits/hyperv.h12
-rw-r--r--src/arch/arm/include/bits/iomap.h12
-rw-r--r--src/arch/arm/include/bits/mp.h12
-rw-r--r--src/arch/arm/include/bits/nap.h10
-rw-r--r--src/arch/arm/include/bits/pci_io.h12
-rw-r--r--src/arch/arm/include/bits/sanboot.h12
-rw-r--r--src/arch/arm/include/bits/smbios.h12
-rw-r--r--src/arch/arm/include/bits/time.h12
-rw-r--r--src/arch/arm/include/bits/uaccess.h12
-rw-r--r--src/arch/arm/include/bits/uart.h12
-rw-r--r--src/arch/arm/include/bits/umalloc.h12
-rw-r--r--src/arch/arm/include/ipxe/efi/efiarm_nap.h18
-rw-r--r--src/arch/arm32/Makefile4
-rw-r--r--src/arch/arm32/Makefile.efi4
-rw-r--r--src/arch/arm32/Makefile.linux25
-rw-r--r--src/arch/arm32/core/arm32_bigint.c106
-rw-r--r--src/arch/arm32/core/pmccntr.S85
-rw-r--r--src/arch/arm32/include/bits/bigint.h107
-rw-r--r--src/arch/arm32/include/bits/profile.h19
-rw-r--r--src/arch/arm32/include/bits/setjmp.h (renamed from src/arch/arm32/include/setjmp.h)12
-rw-r--r--src/arch/arm32/include/bits/tcpip.h19
-rw-r--r--src/arch/arm32/include/gdbmach.h45
-rw-r--r--src/arch/arm64/Makefile4
-rw-r--r--src/arch/arm64/Makefile.efi4
-rw-r--r--src/arch/arm64/core/arm64_bigint.c107
-rw-r--r--src/arch/arm64/core/arm64_string.c174
-rw-r--r--src/arch/arm64/include/bits/bigint.h113
-rw-r--r--src/arch/arm64/include/bits/lkrn.h30
-rw-r--r--src/arch/arm64/include/bits/profile.h2
-rw-r--r--src/arch/arm64/include/bits/setjmp.h (renamed from src/arch/arm64/include/setjmp.h)12
-rw-r--r--src/arch/arm64/include/gdbmach.h45
-rw-r--r--src/arch/i386/Makefile4
-rw-r--r--src/arch/i386/Makefile.efi4
-rw-r--r--src/arch/i386/include/bits/gdbmach.h (renamed from src/arch/i386/include/gdbmach.h)0
-rw-r--r--src/arch/i386/include/bits/profile.h6
-rw-r--r--src/arch/i386/include/bits/setjmp.h (renamed from src/arch/i386/include/setjmp.h)12
-rw-r--r--src/arch/i386/scripts/i386-kir.lds6
-rw-r--r--src/arch/loong64/Makefile7
-rw-r--r--src/arch/loong64/Makefile.efi4
-rw-r--r--src/arch/loong64/core/loong64_bigint.c124
-rw-r--r--src/arch/loong64/include/bits/acpi.h12
-rw-r--r--src/arch/loong64/include/bits/bigint.h138
-rw-r--r--src/arch/loong64/include/bits/hyperv.h12
-rw-r--r--src/arch/loong64/include/bits/mp.h12
-rw-r--r--src/arch/loong64/include/bits/nap.h8
-rw-r--r--src/arch/loong64/include/bits/pci_io.h12
-rw-r--r--src/arch/loong64/include/bits/profile.h2
-rw-r--r--src/arch/loong64/include/bits/reboot.h12
-rw-r--r--src/arch/loong64/include/bits/sanboot.h12
-rw-r--r--src/arch/loong64/include/bits/setjmp.h23
-rw-r--r--src/arch/loong64/include/bits/smbios.h12
-rw-r--r--src/arch/loong64/include/bits/tcpip.h19
-rw-r--r--src/arch/loong64/include/bits/time.h12
-rw-r--r--src/arch/loong64/include/bits/uaccess.h12
-rw-r--r--src/arch/loong64/include/bits/uart.h12
-rw-r--r--src/arch/loong64/include/bits/umalloc.h12
-rw-r--r--src/arch/loong64/include/bits/xen.h13
-rw-r--r--src/arch/loong64/include/gdbmach.h45
-rw-r--r--src/arch/loong64/include/ipxe/efi/efiloong64_nap.h18
-rw-r--r--src/arch/loong64/include/setjmp.h31
-rw-r--r--src/arch/riscv/Makefile22
-rw-r--r--src/arch/riscv/Makefile.efi10
-rw-r--r--src/arch/riscv/Makefile.linux6
-rw-r--r--src/arch/riscv/Makefile.sbi31
-rw-r--r--src/arch/riscv/core/hart.c100
-rw-r--r--src/arch/riscv/core/riscv_dma.c190
-rw-r--r--src/arch/riscv/core/riscv_io.c (renamed from src/arch/arm/interface/efi/efiarm_nap.c)43
-rw-r--r--src/arch/riscv/core/riscv_string.c233
-rw-r--r--src/arch/riscv/core/riscv_strings.S (renamed from src/arch/x86/interface/efi/efix86_nap.c)63
-rw-r--r--src/arch/riscv/core/riscv_tcpip.S138
-rw-r--r--src/arch/riscv/core/setjmp.S105
-rw-r--r--src/arch/riscv/core/stack.S45
-rw-r--r--src/arch/riscv/core/svpage.c292
-rw-r--r--src/arch/riscv/core/xthead.c (renamed from src/arch/loong64/interface/efi/efiloong64_nap.c)56
-rw-r--r--src/arch/riscv/core/zicbom.c255
-rw-r--r--src/arch/riscv/core/zicntr.c194
-rw-r--r--src/arch/riscv/core/zkr.c110
-rw-r--r--src/arch/riscv/include/bits/bigint.h381
-rw-r--r--src/arch/riscv/include/bits/bitops.h82
-rw-r--r--src/arch/riscv/include/bits/byteswap.h48
-rw-r--r--src/arch/riscv/include/bits/compiler.h40
-rw-r--r--src/arch/riscv/include/bits/dma.h14
-rw-r--r--src/arch/riscv/include/bits/endian.h8
-rw-r--r--src/arch/riscv/include/bits/errfile.h24
-rw-r--r--src/arch/riscv/include/bits/io.h17
-rw-r--r--src/arch/riscv/include/bits/iomap.h (renamed from src/arch/loong64/include/bits/iomap.h)4
-rw-r--r--src/arch/riscv/include/bits/lkrn.h34
-rw-r--r--src/arch/riscv/include/bits/nap.h20
-rw-r--r--src/arch/riscv/include/bits/profile.h28
-rw-r--r--src/arch/riscv/include/bits/reboot.h (renamed from src/arch/arm/include/bits/reboot.h)4
-rw-r--r--src/arch/riscv/include/bits/setjmp.h16
-rw-r--r--src/arch/riscv/include/bits/stdint.h23
-rw-r--r--src/arch/riscv/include/bits/string.h82
-rw-r--r--src/arch/riscv/include/bits/strings.h91
-rw-r--r--src/arch/riscv/include/bits/tcpip.h15
-rw-r--r--src/arch/riscv/include/bits/virt_offset.h33
-rw-r--r--src/arch/riscv/include/ipxe/csr.h75
-rw-r--r--src/arch/riscv/include/ipxe/errno/sbi.h19
-rw-r--r--src/arch/riscv/include/ipxe/hart.h16
-rw-r--r--src/arch/riscv/include/ipxe/riscv_dma.h45
-rw-r--r--src/arch/riscv/include/ipxe/riscv_io.h141
-rw-r--r--src/arch/riscv/include/ipxe/sbi.h213
-rw-r--r--src/arch/riscv/include/ipxe/sbi_reboot.h18
-rw-r--r--src/arch/riscv/include/ipxe/svpage.h28
-rw-r--r--src/arch/riscv/include/ipxe/xthead.h21
-rw-r--r--src/arch/riscv/include/ipxe/zicbom.h17
-rw-r--r--src/arch/riscv/interface/sbi/sbi_console.c138
-rw-r--r--src/arch/riscv/interface/sbi/sbi_reboot.c87
-rw-r--r--src/arch/riscv/prefix/libprefix.S1529
-rw-r--r--src/arch/riscv/prefix/lkrnprefix.S129
-rw-r--r--src/arch/riscv/prefix/sbiprefix.S70
-rw-r--r--src/arch/riscv/scripts/sbi.lds133
-rw-r--r--src/arch/riscv32/Makefile24
-rw-r--r--src/arch/riscv32/Makefile.efi10
-rw-r--r--src/arch/riscv32/Makefile.linux14
-rw-r--r--src/arch/riscv32/Makefile.sbi14
-rw-r--r--src/arch/riscv32/core/riscv32_byteswap.S63
-rw-r--r--src/arch/riscv32/include/ipxe/efi/dhcparch.h20
-rw-r--r--src/arch/riscv32/include/ipxe/sbi/dhcparch.h20
-rw-r--r--src/arch/riscv32/include/limits.h61
-rw-r--r--src/arch/riscv32/libgcc/llshift.S112
-rw-r--r--src/arch/riscv64/Makefile23
-rw-r--r--src/arch/riscv64/Makefile.efi10
-rw-r--r--src/arch/riscv64/Makefile.linux10
-rw-r--r--src/arch/riscv64/Makefile.sbi14
-rw-r--r--src/arch/riscv64/core/riscv64_byteswap.S64
-rw-r--r--src/arch/riscv64/include/ipxe/efi/dhcparch.h20
-rw-r--r--src/arch/riscv64/include/ipxe/sbi/dhcparch.h20
-rw-r--r--src/arch/riscv64/include/limits.h61
-rw-r--r--src/arch/x86/Makefile4
-rw-r--r--src/arch/x86/core/cpuid.c5
-rw-r--r--src/arch/x86/core/cpuid_settings.c20
-rw-r--r--src/arch/x86/core/debugcon.c1
-rw-r--r--src/arch/x86/core/gdbmach.c1
-rw-r--r--src/arch/x86/core/pci_autoboot.c1
-rw-r--r--src/arch/x86/core/pcidirect.c4
-rw-r--r--src/arch/x86/core/rdrand.c1
-rw-r--r--src/arch/x86/core/relocate.c96
-rw-r--r--src/arch/x86/core/runtime.c17
-rw-r--r--src/arch/x86/core/video_subr.c1
-rw-r--r--src/arch/x86/core/vram_settings.c5
-rw-r--r--src/arch/x86/core/x86_bigint.c100
-rw-r--r--src/arch/x86/core/x86_io.c69
-rw-r--r--src/arch/x86/core/x86_string.c1
-rw-r--r--src/arch/x86/core/x86_tcpip.c1
-rw-r--r--src/arch/x86/core/x86_uart.c60
-rw-r--r--src/arch/x86/drivers/net/undiisr.S10
-rw-r--r--src/arch/x86/drivers/net/undinet.c53
-rw-r--r--src/arch/x86/drivers/xen/hvm.c1
-rw-r--r--src/arch/x86/hci/commands/cpuid_cmd.c6
-rw-r--r--src/arch/x86/hci/commands/pxe_cmd.c12
-rw-r--r--src/arch/x86/image/bzimage.c360
-rw-r--r--src/arch/x86/image/com32.c90
-rw-r--r--src/arch/x86/image/comboot.c93
-rw-r--r--src/arch/x86/image/elfboot.c35
-rw-r--r--src/arch/x86/image/initrd.c306
-rw-r--r--src/arch/x86/image/multiboot.c234
-rw-r--r--src/arch/x86/image/nbi.c117
-rw-r--r--src/arch/x86/image/pxe_image.c26
-rw-r--r--src/arch/x86/image/sdi.c75
-rw-r--r--src/arch/x86/image/ucode.c231
-rw-r--r--src/arch/x86/include/basemem.h5
-rw-r--r--src/arch/x86/include/bios_disks.h69
-rw-r--r--src/arch/x86/include/bits/acpi.h1
-rw-r--r--src/arch/x86/include/bits/bigint.h108
-rw-r--r--src/arch/x86/include/bits/bitops.h1
-rw-r--r--src/arch/x86/include/bits/endian.h1
-rw-r--r--src/arch/x86/include/bits/errfile.h5
-rw-r--r--src/arch/x86/include/bits/io.h1
-rw-r--r--src/arch/x86/include/bits/iomap.h1
-rw-r--r--src/arch/x86/include/bits/memmap.h15
-rw-r--r--src/arch/x86/include/bits/nap.h12
-rw-r--r--src/arch/x86/include/bits/ns16550.h60
-rw-r--r--src/arch/x86/include/bits/pci_io.h2
-rw-r--r--src/arch/x86/include/bits/reboot.h1
-rw-r--r--src/arch/x86/include/bits/sanboot.h1
-rw-r--r--src/arch/x86/include/bits/smbios.h1
-rw-r--r--src/arch/x86/include/bits/string.h1
-rw-r--r--src/arch/x86/include/bits/tcpip.h1
-rw-r--r--src/arch/x86/include/bits/time.h1
-rw-r--r--src/arch/x86/include/bits/uaccess.h14
-rw-r--r--src/arch/x86/include/bits/uart.h41
-rw-r--r--src/arch/x86/include/bits/umalloc.h14
-rw-r--r--src/arch/x86/include/bits/xen.h1
-rw-r--r--src/arch/x86/include/initrd.h23
-rw-r--r--src/arch/x86/include/ipxe/bios_nap.h1
-rw-r--r--src/arch/x86/include/ipxe/bios_reboot.h1
-rw-r--r--src/arch/x86/include/ipxe/bios_sanboot.h1
-rw-r--r--src/arch/x86/include/ipxe/bios_smbios.h1
-rw-r--r--src/arch/x86/include/ipxe/cpuid.h4
-rw-r--r--src/arch/x86/include/ipxe/efi/efix86_nap.h18
-rw-r--r--src/arch/x86/include/ipxe/int15.h22
-rw-r--r--src/arch/x86/include/ipxe/iomap_pages.h1
-rw-r--r--src/arch/x86/include/ipxe/memtop_umalloc.h18
-rw-r--r--src/arch/x86/include/ipxe/pcibios.h14
-rw-r--r--src/arch/x86/include/ipxe/pcicloud.h18
-rw-r--r--src/arch/x86/include/ipxe/pcidirect.h14
-rw-r--r--src/arch/x86/include/ipxe/rsdp.h5
-rw-r--r--src/arch/x86/include/ipxe/rtc_time.h1
-rw-r--r--src/arch/x86/include/ipxe/x86_io.h1
-rw-r--r--src/arch/x86/include/libkir.h4
-rw-r--r--src/arch/x86/include/librm.h125
-rw-r--r--src/arch/x86/include/pic8259.h47
-rw-r--r--src/arch/x86/include/pxe.h3
-rw-r--r--src/arch/x86/include/realmode.h16
-rw-r--r--src/arch/x86/interface/pcbios/acpi_timer.c9
-rw-r--r--src/arch/x86/interface/pcbios/acpipwr.c19
-rw-r--r--src/arch/x86/interface/pcbios/basemem.c2
-rw-r--r--src/arch/x86/interface/pcbios/bios_cachedhcp.c4
-rw-r--r--src/arch/x86/interface/pcbios/bios_mp.c7
-rw-r--r--src/arch/x86/interface/pcbios/bios_reboot.c11
-rw-r--r--src/arch/x86/interface/pcbios/bios_smbios.c36
-rw-r--r--src/arch/x86/interface/pcbios/biosint.c1
-rw-r--r--src/arch/x86/interface/pcbios/e820mangler.S8
-rw-r--r--src/arch/x86/interface/pcbios/hidemem.c66
-rw-r--r--src/arch/x86/interface/pcbios/int13.c31
-rw-r--r--src/arch/x86/interface/pcbios/int13con.c1
-rw-r--r--src/arch/x86/interface/pcbios/int15.c (renamed from src/arch/x86/interface/pcbios/memmap.c)137
-rw-r--r--src/arch/x86/interface/pcbios/memtop_umalloc.c235
-rw-r--r--src/arch/x86/interface/pcbios/pcibios.c4
-rw-r--r--src/arch/x86/interface/pcbios/pcicloud.c194
-rw-r--r--src/arch/x86/interface/pcbios/rsdp.c42
-rw-r--r--src/arch/x86/interface/pcbios/rtc_entropy.c33
-rw-r--r--src/arch/x86/interface/pcbios/vesafb.c41
-rw-r--r--src/arch/x86/interface/pxe/pxe_call.c37
-rw-r--r--src/arch/x86/interface/pxe/pxe_file.c71
-rw-r--r--src/arch/x86/interface/pxe/pxe_preboot.c9
-rw-r--r--src/arch/x86/interface/pxe/pxe_tftp.c16
-rw-r--r--src/arch/x86/interface/pxe/pxe_udp.c13
-rw-r--r--src/arch/x86/interface/syslinux/com32_call.c24
-rw-r--r--src/arch/x86/interface/syslinux/comboot_call.c90
-rw-r--r--src/arch/x86/interface/vmware/guestinfo.c1
-rw-r--r--src/arch/x86/interface/vmware/vmconsole.c1
-rw-r--r--src/arch/x86/prefix/lkrnprefix.S2
-rw-r--r--src/arch/x86/prefix/pxeprefix.S85
-rw-r--r--src/arch/x86/prefix/unlzma.S6
-rw-r--r--src/arch/x86/scripts/pcbios.lds6
-rw-r--r--src/arch/x86/transitions/librm_mgmt.c56
-rw-r--r--src/arch/x86_64/Makefile4
-rw-r--r--src/arch/x86_64/Makefile.efi4
-rw-r--r--src/arch/x86_64/include/bits/byteswap.h1
-rw-r--r--src/arch/x86_64/include/bits/compiler.h1
-rw-r--r--src/arch/x86_64/include/bits/gdbmach.h (renamed from src/arch/x86_64/include/gdbmach.h)0
-rw-r--r--src/arch/x86_64/include/bits/profile.h3
-rw-r--r--src/arch/x86_64/include/bits/setjmp.h (renamed from src/arch/x86_64/include/setjmp.h)12
-rw-r--r--src/arch/x86_64/include/bits/stdint.h1
-rw-r--r--src/arch/x86_64/include/bits/strings.h1
-rw-r--r--src/arch/x86_64/include/ipxe/efi/dhcparch.h1
-rw-r--r--src/arch/x86_64/include/limits.h1
251 files changed, 8158 insertions, 3673 deletions
diff --git a/src/arch/arm/Makefile b/src/arch/arm/Makefile
index b6509dda0..f827ed422 100644
--- a/src/arch/arm/Makefile
+++ b/src/arch/arm/Makefile
@@ -3,9 +3,9 @@
ASM_TCHAR := %
ASM_TCHAR_OPS := %%
-# Include common ARM headers
+# Include ARM-specific headers
#
-INCDIRS += arch/arm/include
+INCDIRS := arch/$(ARCH)/include arch/arm/include $(INCDIRS)
# ARM-specific directories containing source files
#
diff --git a/src/arch/arm/include/bits/acpi.h b/src/arch/arm/include/bits/acpi.h
deleted file mode 100644
index f9f2f00e7..000000000
--- a/src/arch/arm/include/bits/acpi.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_ACPI_H
-#define _BITS_ACPI_H
-
-/** @file
- *
- * ARM-specific ACPI API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_ACPI_H */
diff --git a/src/arch/arm/include/bits/hyperv.h b/src/arch/arm/include/bits/hyperv.h
deleted file mode 100644
index f0e0c8793..000000000
--- a/src/arch/arm/include/bits/hyperv.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_HYPERV_H
-#define _BITS_HYPERV_H
-
-/** @file
- *
- * Hyper-V interface
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_HYPERV_H */
diff --git a/src/arch/arm/include/bits/iomap.h b/src/arch/arm/include/bits/iomap.h
deleted file mode 100644
index ae953c450..000000000
--- a/src/arch/arm/include/bits/iomap.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_IOMAP_H
-#define _BITS_IOMAP_H
-
-/** @file
- *
- * ARM-specific I/O mapping API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_IOMAP_H */
diff --git a/src/arch/arm/include/bits/mp.h b/src/arch/arm/include/bits/mp.h
deleted file mode 100644
index e7d4c0c16..000000000
--- a/src/arch/arm/include/bits/mp.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_MP_H
-#define _BITS_MP_H
-
-/** @file
- *
- * ARM-specific multiprocessor API implementation
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_MP_H */
diff --git a/src/arch/arm/include/bits/nap.h b/src/arch/arm/include/bits/nap.h
index e30a7146b..dbdf37166 100644
--- a/src/arch/arm/include/bits/nap.h
+++ b/src/arch/arm/include/bits/nap.h
@@ -9,6 +9,12 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-#include <ipxe/efi/efiarm_nap.h>
+/**
+ * Sleep until next CPU interrupt
+ *
+ */
+static inline __attribute__ (( always_inline )) void cpu_halt ( void ) {
+ __asm__ __volatile__ ( "wfi" );
+}
-#endif /* _BITS_MAP_H */
+#endif /* _BITS_NAP_H */
diff --git a/src/arch/arm/include/bits/pci_io.h b/src/arch/arm/include/bits/pci_io.h
deleted file mode 100644
index 91f507a44..000000000
--- a/src/arch/arm/include/bits/pci_io.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_PCI_IO_H
-#define _BITS_PCI_IO_H
-
-/** @file
- *
- * ARM PCI I/O API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_PCI_IO_H */
diff --git a/src/arch/arm/include/bits/sanboot.h b/src/arch/arm/include/bits/sanboot.h
deleted file mode 100644
index abd4c79a5..000000000
--- a/src/arch/arm/include/bits/sanboot.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_SANBOOT_H
-#define _BITS_SANBOOT_H
-
-/** @file
- *
- * ARM-specific sanboot API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_SANBOOT_H */
diff --git a/src/arch/arm/include/bits/smbios.h b/src/arch/arm/include/bits/smbios.h
deleted file mode 100644
index d94218116..000000000
--- a/src/arch/arm/include/bits/smbios.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_SMBIOS_H
-#define _BITS_SMBIOS_H
-
-/** @file
- *
- * ARM-specific SMBIOS API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_SMBIOS_H */
diff --git a/src/arch/arm/include/bits/time.h b/src/arch/arm/include/bits/time.h
deleted file mode 100644
index 724d8b932..000000000
--- a/src/arch/arm/include/bits/time.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_TIME_H
-#define _BITS_TIME_H
-
-/** @file
- *
- * ARM-specific time API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_TIME_H */
diff --git a/src/arch/arm/include/bits/uaccess.h b/src/arch/arm/include/bits/uaccess.h
deleted file mode 100644
index 87f11509c..000000000
--- a/src/arch/arm/include/bits/uaccess.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_UACCESS_H
-#define _BITS_UACCESS_H
-
-/** @file
- *
- * ARM-specific user access API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_UACCESS_H */
diff --git a/src/arch/arm/include/bits/uart.h b/src/arch/arm/include/bits/uart.h
deleted file mode 100644
index 6f85975f7..000000000
--- a/src/arch/arm/include/bits/uart.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_UART_H
-#define _BITS_UART_H
-
-/** @file
- *
- * 16550-compatible UART
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_UART_H */
diff --git a/src/arch/arm/include/bits/umalloc.h b/src/arch/arm/include/bits/umalloc.h
deleted file mode 100644
index 27970d7b2..000000000
--- a/src/arch/arm/include/bits/umalloc.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_UMALLOC_H
-#define _BITS_UMALLOC_H
-
-/** @file
- *
- * ARM-specific user memory allocation API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_UMALLOC_H */
diff --git a/src/arch/arm/include/ipxe/efi/efiarm_nap.h b/src/arch/arm/include/ipxe/efi/efiarm_nap.h
deleted file mode 100644
index dcbdd3e20..000000000
--- a/src/arch/arm/include/ipxe/efi/efiarm_nap.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef _IPXE_EFIARM_NAP_H
-#define _IPXE_EFIARM_NAP_H
-
-/** @file
- *
- * EFI CPU sleeping
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#ifdef NAP_EFIARM
-#define NAP_PREFIX_efiarm
-#else
-#define NAP_PREFIX_efiarm __efiarm_
-#endif
-
-#endif /* _IPXE_EFIARM_NAP_H */
diff --git a/src/arch/arm32/Makefile b/src/arch/arm32/Makefile
index 0c1cf99d1..070041734 100644
--- a/src/arch/arm32/Makefile
+++ b/src/arch/arm32/Makefile
@@ -1,3 +1,7 @@
+# Specify compressor
+#
+ZBIN = $(ZBIN32)
+
# ARM32-specific directories containing source files
#
SRCDIRS += arch/arm32/core
diff --git a/src/arch/arm32/Makefile.efi b/src/arch/arm32/Makefile.efi
index d720f34f0..9bd34383d 100644
--- a/src/arch/arm32/Makefile.efi
+++ b/src/arch/arm32/Makefile.efi
@@ -8,10 +8,6 @@ CFLAGS += -mfloat-abi=soft
#
ELF2EFI = $(ELF2EFI32)
-# Specify EFI boot file
-#
-EFI_BOOT_FILE = bootarm.efi
-
# Include generic EFI Makefile
#
MAKEDEPS += arch/arm/Makefile.efi
diff --git a/src/arch/arm32/Makefile.linux b/src/arch/arm32/Makefile.linux
new file mode 100644
index 000000000..289118f29
--- /dev/null
+++ b/src/arch/arm32/Makefile.linux
@@ -0,0 +1,25 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# The number of different ABIs for 32-bit ARM is insane. It is
+# unclear whether or not unaligned accesses ought to work in a 32-bit
+# Linux userspace binary. When running in QEMU, unaligned accesses
+# result in a SIGBUS. Since this is likely to be the most common use
+# case (for running self-tests on an x86 build machine), and since we
+# don't particularly care about performance for Linux userspace
+# binaries, force the compiler to never generate an unaligned access.
+#
+CFLAGS += -mno-unaligned-access
+
+# Inhibit the harmless warning about wchar_t size mismatch between the
+# linux_api.o helper object and the rest of iPXE.
+#
+LINUX_CFLAGS += -Wl,--no-wchar-size-warning
+
+# Starting virtual address
+#
+LDFLAGS += -Ttext=0x10000
+
+# Include generic Linux Makefile
+#
+MAKEDEPS += arch/arm/Makefile.linux
+include arch/arm/Makefile.linux
diff --git a/src/arch/arm32/core/arm32_bigint.c b/src/arch/arm32/core/arm32_bigint.c
deleted file mode 100644
index 29fb40a7c..000000000
--- a/src/arch/arm32/core/arm32_bigint.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <stdint.h>
-#include <string.h>
-#include <ipxe/bigint.h>
-
-/** @file
- *
- * Big integer support
- */
-
-/**
- * Multiply big integers
- *
- * @v multiplicand0 Element 0 of big integer to be multiplied
- * @v multiplicand_size Number of elements in multiplicand
- * @v multiplier0 Element 0 of big integer to be multiplied
- * @v multiplier_size Number of elements in multiplier
- * @v result0 Element 0 of big integer to hold result
- */
-void bigint_multiply_raw ( const uint32_t *multiplicand0,
- unsigned int multiplicand_size,
- const uint32_t *multiplier0,
- unsigned int multiplier_size,
- uint32_t *result0 ) {
- unsigned int result_size = ( multiplicand_size + multiplier_size );
- const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
- *multiplicand = ( ( const void * ) multiplicand0 );
- const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
- *multiplier = ( ( const void * ) multiplier0 );
- bigint_t ( result_size ) __attribute__ (( may_alias ))
- *result = ( ( void * ) result0 );
- unsigned int i;
- unsigned int j;
- uint32_t multiplicand_element;
- uint32_t multiplier_element;
- uint32_t *result_elements;
- uint32_t discard_low;
- uint32_t discard_high;
- uint32_t discard_temp;
-
- /* Zero result */
- memset ( result, 0, sizeof ( *result ) );
-
- /* Multiply integers one element at a time */
- for ( i = 0 ; i < multiplicand_size ; i++ ) {
- multiplicand_element = multiplicand->element[i];
- for ( j = 0 ; j < multiplier_size ; j++ ) {
- multiplier_element = multiplier->element[j];
- result_elements = &result->element[ i + j ];
- /* Perform a single multiply, and add the
- * resulting double-element into the result,
- * carrying as necessary. The carry can
- * never overflow beyond the end of the
- * result, since:
- *
- * a < 2^{n}, b < 2^{m} => ab < 2^{n+m}
- */
- __asm__ __volatile__ ( "umull %1, %2, %5, %6\n\t"
- "ldr %3, [%0]\n\t"
- "adds %3, %1\n\t"
- "stmia %0!, {%3}\n\t"
- "ldr %3, [%0]\n\t"
- "adcs %3, %2\n\t"
- "stmia %0!, {%3}\n\t"
- "bcc 2f\n\t"
- "\n1:\n\t"
- "ldr %3, [%0]\n\t"
- "adcs %3, #0\n\t"
- "stmia %0!, {%3}\n\t"
- "bcs 1b\n\t"
- "\n2:\n\t"
- : "+l" ( result_elements ),
- "=l" ( discard_low ),
- "=l" ( discard_high ),
- "=l" ( discard_temp ),
- "+m" ( *result )
- : "l" ( multiplicand_element ),
- "l" ( multiplier_element )
- : "cc" );
- }
- }
-}
diff --git a/src/arch/arm32/core/pmccntr.S b/src/arch/arm32/core/pmccntr.S
new file mode 100644
index 000000000..c2fbeff4f
--- /dev/null
+++ b/src/arch/arm32/core/pmccntr.S
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * Performance Monitor Cycle Counter (PMCCNTR)
+ *
+ */
+
+ .section ".note.GNU-stack", "", %progbits
+ .text
+ .arm
+
+/*
+ * PMCCNTR status
+ *
+ * bit 31 set if PMCCNTR availability is not yet determined
+ * bit 0 set if PMCCNTR is available
+ *
+ */
+ .section ".data.pmccntr_status", "aw", %progbits
+ .globl pmccntr_status
+pmccntr_status:
+ .word 0x80000000
+
+/*
+ * Check PMCCNTR availability
+ *
+ * Must preserve all registers, and return with either PMCCNTR enabled
+ * or the Z flag set to indicate unavailability.
+ *
+ */
+ .section ".text.pmccntr_check", "ax", %progbits
+ .globl pmccntr_check
+ .type pmccntr_check, %function
+pmccntr_check:
+ /* Save registers */
+ stmfd sp!, { r0, r1 }
+ /* Read CPSR.M (bits 3:0, always permitted in PL0) */
+ mrs r0, cpsr
+ and r0, r0, #0x0000000f
+ /* Read PMUSERENR.EN (bit 0, always permitted in PL0) */
+ mrc p15, 0, r1, c9, c14, 0
+ and r1, r1, #0x00000001
+ /* Check if we are in PL1+ or in PL0 with PMUSERENR.EN set */
+ orrs r0, r0, r1
+ /* If PMCCNTR is unavailable, exit with status=0 and ZF set */
+ beq 1f
+ /* Set PMCR.E (bit 0), set exit status=1 and ZF clear */
+ movs r0, #0x00000001
+ mcr p15, 0, r0, c9, c12, 0
+ /* Set PMCNTENSET.C (bit 31) */
+ mov r1, #0x80000000
+ mcr p15, 0, r1, c9, c12, 1
+1: /* Store PMCCNTR status */
+ ldr r1, pmccntr_status_ptr
+ str r0, [r1]
+ /* Restore registers and return */
+ ldmfd sp!, { r0, r1 }
+ bx lr
+pmccntr_status_ptr:
+ .word pmccntr_status
+ .size pmccntr_check, . - pmccntr_check
diff --git a/src/arch/arm32/include/bits/bigint.h b/src/arch/arm32/include/bits/bigint.h
index e4b511da7..988bef5ff 100644
--- a/src/arch/arm32/include/bits/bigint.h
+++ b/src/arch/arm32/include/bits/bigint.h
@@ -43,8 +43,9 @@ bigint_init_raw ( uint32_t *value0, unsigned int size,
* @v addend0 Element 0 of big integer to add
* @v value0 Element 0 of big integer to be added to
* @v size Number of elements
+ * @ret carry Carry out
*/
-static inline __attribute__ (( always_inline )) void
+static inline __attribute__ (( always_inline )) int
bigint_add_raw ( const uint32_t *addend0, uint32_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
@@ -54,8 +55,9 @@ bigint_add_raw ( const uint32_t *addend0, uint32_t *value0,
uint32_t *discard_end;
uint32_t discard_addend_i;
uint32_t discard_value_i;
+ int carry;
- __asm__ __volatile__ ( "adds %2, %0, %8, lsl #2\n\t" /* clear CF */
+ __asm__ __volatile__ ( "adds %2, %0, %9, lsl #2\n\t" /* clear CF */
"\n1:\n\t"
"ldmia %0!, {%3}\n\t"
"ldr %4, [%1]\n\t"
@@ -68,9 +70,11 @@ bigint_add_raw ( const uint32_t *addend0, uint32_t *value0,
"=l" ( discard_end ),
"=l" ( discard_addend_i ),
"=l" ( discard_value_i ),
+ "=@cccs" ( carry ),
"+m" ( *value )
- : "0" ( addend0 ), "1" ( value0 ), "l" ( size )
- : "cc" );
+ : "0" ( addend0 ), "1" ( value0 ),
+ "l" ( size ) );
+ return carry;
}
/**
@@ -79,8 +83,9 @@ bigint_add_raw ( const uint32_t *addend0, uint32_t *value0,
* @v subtrahend0 Element 0 of big integer to subtract
* @v value0 Element 0 of big integer to be subtracted from
* @v size Number of elements
+ * @ret borrow Borrow out
*/
-static inline __attribute__ (( always_inline )) void
+static inline __attribute__ (( always_inline )) int
bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
@@ -90,8 +95,9 @@ bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0,
uint32_t *discard_end;
uint32_t discard_subtrahend_i;
uint32_t discard_value_i;
+ int borrow;
- __asm__ __volatile__ ( "add %2, %0, %8, lsl #2\n\t"
+ __asm__ __volatile__ ( "add %2, %0, %9, lsl #2\n\t"
"cmp %2, %0\n\t" /* set CF */
"\n1:\n\t"
"ldmia %0!, {%3}\n\t"
@@ -105,27 +111,30 @@ bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0,
"=l" ( discard_end ),
"=l" ( discard_subtrahend_i ),
"=l" ( discard_value_i ),
+ "=@cccc" ( borrow ),
"+m" ( *value )
: "0" ( subtrahend0 ), "1" ( value0 ),
- "l" ( size )
- : "cc" );
+ "l" ( size ) );
+ return borrow;
}
/**
- * Rotate big integer left
+ * Shift big integer left
*
* @v value0 Element 0 of big integer
* @v size Number of elements
+ * @ret out Bit shifted out
*/
-static inline __attribute__ (( always_inline )) void
-bigint_rol_raw ( uint32_t *value0, unsigned int size ) {
+static inline __attribute__ (( always_inline )) int
+bigint_shl_raw ( uint32_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint32_t *discard_value;
uint32_t *discard_end;
uint32_t discard_value_i;
+ int carry;
- __asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */
+ __asm__ __volatile__ ( "adds %1, %0, %1, lsl #2\n\t" /* clear CF */
"\n1:\n\t"
"ldr %2, [%0]\n\t"
"adcs %2, %2\n\t"
@@ -135,26 +144,29 @@ bigint_rol_raw ( uint32_t *value0, unsigned int size ) {
: "=l" ( discard_value ),
"=l" ( discard_end ),
"=l" ( discard_value_i ),
+ "=@cccs" ( carry ),
"+m" ( *value )
- : "0" ( value0 ), "1" ( size )
- : "cc" );
+ : "0" ( value0 ), "1" ( size ) );
+ return carry;
}
/**
- * Rotate big integer right
+ * Shift big integer right
*
* @v value0 Element 0 of big integer
* @v size Number of elements
+ * @ret out Bit shifted out
*/
-static inline __attribute__ (( always_inline )) void
-bigint_ror_raw ( uint32_t *value0, unsigned int size ) {
+static inline __attribute__ (( always_inline )) int
+bigint_shr_raw ( uint32_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint32_t *discard_value;
uint32_t *discard_end;
uint32_t discard_value_i;
+ int carry;
- __asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */
+ __asm__ __volatile__ ( "adds %1, %0, %1, lsl #2\n\t" /* clear CF */
"\n1:\n\t"
"ldmdb %1!, {%2}\n\t"
"rrxs %2, %2\n\t"
@@ -164,9 +176,10 @@ bigint_ror_raw ( uint32_t *value0, unsigned int size ) {
: "=l" ( discard_value ),
"=l" ( discard_end ),
"=l" ( discard_value_i ),
+ "=@cccs" ( carry ),
"+m" ( *value )
- : "0" ( value0 ), "1" ( size )
- : "cc" );
+ : "0" ( value0 ), "1" ( size ) );
+ return carry;
}
/**
@@ -217,25 +230,6 @@ bigint_is_geq_raw ( const uint32_t *value0, const uint32_t *reference0,
}
/**
- * Test if bit is set in big integer
- *
- * @v value0 Element 0 of big integer
- * @v size Number of elements
- * @v bit Bit to test
- * @ret is_set Bit is set
- */
-static inline __attribute__ (( always_inline )) int
-bigint_bit_is_set_raw ( const uint32_t *value0, unsigned int size,
- unsigned int bit ) {
- const bigint_t ( size ) __attribute__ (( may_alias )) *value =
- ( ( const void * ) value0 );
- unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) );
- unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) );
-
- return ( value->element[index] & ( 1 << subindex ) );
-}
-
-/**
* Find highest bit set in big integer
*
* @v value0 Element 0 of big integer
@@ -309,10 +303,35 @@ bigint_done_raw ( const uint32_t *value0, unsigned int size __unused,
*(--out_byte) = *(value_byte++);
}
-extern void bigint_multiply_raw ( const uint32_t *multiplicand0,
- unsigned int multiplicand_size,
- const uint32_t *multiplier0,
- unsigned int multiplier_size,
- uint32_t *value0 );
+/**
+ * Multiply big integer elements
+ *
+ * @v multiplicand Multiplicand element
+ * @v multiplier Multiplier element
+ * @v result Result element
+ * @v carry Carry element
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_multiply_one ( const uint32_t multiplicand, const uint32_t multiplier,
+ uint32_t *result, uint32_t *carry ) {
+ uint32_t discard_low;
+ uint32_t discard_high;
+
+ __asm__ __volatile__ ( /* Perform multiplication */
+ "umull %0, %1, %4, %5\n\t"
+ /* Accumulate result */
+ "adds %2, %0\n\t"
+ "adc %1, #0\n\t"
+ /* Accumulate carry (cannot overflow) */
+ "adds %2, %3\n\t"
+ "adc %3, %1, #0\n\t"
+ : "=r" ( discard_low ),
+ "=r" ( discard_high ),
+ "+r" ( *result ),
+ "+r" ( *carry )
+ : "r" ( multiplicand ),
+ "r" ( multiplier )
+ : "cc" );
+}
#endif /* _BITS_BIGINT_H */
diff --git a/src/arch/arm32/include/bits/profile.h b/src/arch/arm32/include/bits/profile.h
index 2b15d1604..31c321884 100644
--- a/src/arch/arm32/include/bits/profile.h
+++ b/src/arch/arm32/include/bits/profile.h
@@ -11,19 +11,30 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
+extern uint32_t pmccntr_status;
+
/**
* Get profiling timestamp
*
* @ret timestamp Timestamp
*/
-static inline __attribute__ (( always_inline )) uint64_t
+static inline __attribute__ (( always_inline )) unsigned long
profile_timestamp ( void ) {
uint32_t cycles;
/* Read cycle counter */
- __asm__ __volatile__ ( "mcr p15, 0, %1, c9, c12, 0\n\t"
- "mrc p15, 0, %0, c9, c13, 0\n\t"
- : "=r" ( cycles ) : "r" ( 1 ) );
+ __asm__ __volatile__ ( /* Check PMCCNTR status */
+ "tst %0, %0\n\t"
+ /* Check availability if not yet known */
+ "it mi\n\t"
+ "blxmi pmccntr_check\n\t"
+ /* Read PMCCNTR if available */
+ "it ne\n\t"
+ "mrcne p15, 0, %0, c9, c13, 0\n\t"
+ "\n1:\n\t"
+ : "=r" ( cycles )
+ : "0" ( pmccntr_status )
+ : "cc", "lr" );
return cycles;
}
diff --git a/src/arch/arm32/include/setjmp.h b/src/arch/arm32/include/bits/setjmp.h
index 4828b47a2..9ee264ecd 100644
--- a/src/arch/arm32/include/setjmp.h
+++ b/src/arch/arm32/include/bits/setjmp.h
@@ -1,5 +1,5 @@
-#ifndef _SETJMP_H
-#define _SETJMP_H
+#ifndef _BITS_SETJMP_H
+#define _BITS_SETJMP_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
@@ -29,10 +29,4 @@ typedef struct {
uint32_t lr;
} jmp_buf[1];
-extern int __asmcall __attribute__ (( returns_twice ))
-setjmp ( jmp_buf env );
-
-extern void __asmcall __attribute__ (( noreturn ))
-longjmp ( jmp_buf env, int val );
-
-#endif /* _SETJMP_H */
+#endif /* _BITS_SETJMP_H */
diff --git a/src/arch/arm32/include/bits/tcpip.h b/src/arch/arm32/include/bits/tcpip.h
deleted file mode 100644
index fc3c5b3ff..000000000
--- a/src/arch/arm32/include/bits/tcpip.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef _BITS_TCPIP_H
-#define _BITS_TCPIP_H
-
-/** @file
- *
- * Transport-network layer interface
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-static inline __attribute__ (( always_inline )) uint16_t
-tcpip_continue_chksum ( uint16_t partial, const void *data, size_t len ) {
-
- /* Not yet optimised */
- return generic_tcpip_continue_chksum ( partial, data, len );
-}
-
-#endif /* _BITS_TCPIP_H */
diff --git a/src/arch/arm32/include/gdbmach.h b/src/arch/arm32/include/gdbmach.h
deleted file mode 100644
index cd152eedd..000000000
--- a/src/arch/arm32/include/gdbmach.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef GDBMACH_H
-#define GDBMACH_H
-
-/** @file
- *
- * GDB architecture specifics
- *
- * This file declares functions for manipulating the machine state and
- * debugging context.
- *
- */
-
-#include <stdint.h>
-
-typedef unsigned long gdbreg_t;
-
-/* Register snapshot */
-enum {
- /* Not yet implemented */
- GDBMACH_NREGS,
-};
-
-#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) )
-
-static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) {
- /* Not yet implemented */
- ( void ) regs;
- ( void ) pc;
-}
-
-static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) {
- /* Not yet implemented */
- ( void ) regs;
- ( void ) step;
-}
-
-static inline void gdbmach_breakpoint ( void ) {
- /* Not yet implemented */
-}
-
-extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len,
- int enable );
-extern void gdbmach_init ( void );
-
-#endif /* GDBMACH_H */
diff --git a/src/arch/arm64/Makefile b/src/arch/arm64/Makefile
index 9b9dd5ec8..719a1e61c 100644
--- a/src/arch/arm64/Makefile
+++ b/src/arch/arm64/Makefile
@@ -1,3 +1,7 @@
+# Specify compressor
+#
+ZBIN = $(ZBIN64)
+
# ARM64-specific directories containing source files
#
SRCDIRS += arch/arm64/core
diff --git a/src/arch/arm64/Makefile.efi b/src/arch/arm64/Makefile.efi
index 998a64d03..96f2953f4 100644
--- a/src/arch/arm64/Makefile.efi
+++ b/src/arch/arm64/Makefile.efi
@@ -4,10 +4,6 @@
#
ELF2EFI = $(ELF2EFI64)
-# Specify EFI boot file
-#
-EFI_BOOT_FILE = bootaa64.efi
-
# Include generic EFI Makefile
#
MAKEDEPS += arch/arm/Makefile.efi
diff --git a/src/arch/arm64/core/arm64_bigint.c b/src/arch/arm64/core/arm64_bigint.c
deleted file mode 100644
index 7740f1aef..000000000
--- a/src/arch/arm64/core/arm64_bigint.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <stdint.h>
-#include <string.h>
-#include <ipxe/bigint.h>
-
-/** @file
- *
- * Big integer support
- */
-
-/**
- * Multiply big integers
- *
- * @v multiplicand0 Element 0 of big integer to be multiplied
- * @v multiplicand_size Number of elements in multiplicand
- * @v multiplier0 Element 0 of big integer to be multiplied
- * @v multiplier_size Number of elements in multiplier
- * @v result0 Element 0 of big integer to hold result
- */
-void bigint_multiply_raw ( const uint64_t *multiplicand0,
- unsigned int multiplicand_size,
- const uint64_t *multiplier0,
- unsigned int multiplier_size,
- uint64_t *result0 ) {
- unsigned int result_size = ( multiplicand_size + multiplier_size );
- const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
- *multiplicand = ( ( const void * ) multiplicand0 );
- const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
- *multiplier = ( ( const void * ) multiplier0 );
- bigint_t ( result_size ) __attribute__ (( may_alias ))
- *result = ( ( void * ) result0 );
- unsigned int i;
- unsigned int j;
- uint64_t multiplicand_element;
- uint64_t multiplier_element;
- uint64_t *result_elements;
- uint64_t discard_low;
- uint64_t discard_high;
- uint64_t discard_temp_low;
- uint64_t discard_temp_high;
-
- /* Zero result */
- memset ( result, 0, sizeof ( *result ) );
-
- /* Multiply integers one element at a time */
- for ( i = 0 ; i < multiplicand_size ; i++ ) {
- multiplicand_element = multiplicand->element[i];
- for ( j = 0 ; j < multiplier_size ; j++ ) {
- multiplier_element = multiplier->element[j];
- result_elements = &result->element[ i + j ];
- /* Perform a single multiply, and add the
- * resulting double-element into the result,
- * carrying as necessary. The carry can
- * never overflow beyond the end of the
- * result, since:
- *
- * a < 2^{n}, b < 2^{m} => ab < 2^{n+m}
- */
- __asm__ __volatile__ ( "mul %1, %6, %7\n\t"
- "umulh %2, %6, %7\n\t"
- "ldp %3, %4, [%0]\n\t"
- "adds %3, %3, %1\n\t"
- "adcs %4, %4, %2\n\t"
- "stp %3, %4, [%0], #16\n\t"
- "bcc 2f\n\t"
- "\n1:\n\t"
- "ldr %3, [%0]\n\t"
- "adcs %3, %3, xzr\n\t"
- "str %3, [%0], #8\n\t"
- "bcs 1b\n\t"
- "\n2:\n\t"
- : "+r" ( result_elements ),
- "=&r" ( discard_low ),
- "=&r" ( discard_high ),
- "=r" ( discard_temp_low ),
- "=r" ( discard_temp_high ),
- "+m" ( *result )
- : "r" ( multiplicand_element ),
- "r" ( multiplier_element )
- : "cc" );
- }
- }
-}
diff --git a/src/arch/arm64/core/arm64_string.c b/src/arch/arm64/core/arm64_string.c
index 28a2b73bc..07a7eefdf 100644
--- a/src/arch/arm64/core/arm64_string.c
+++ b/src/arch/arm64/core/arm64_string.c
@@ -31,6 +31,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
+/** Block size (for "ldp"/"stp") */
+#define ARM64_STRING_BLKSZ 16
+
/**
* Copy memory area
*
@@ -40,59 +43,70 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @ret dest Destination address
*/
void arm64_memcpy ( void *dest, const void *src, size_t len ) {
- void *discard_dest;
- void *discard_end;
- const void *discard_src;
- size_t discard_offset;
+ size_t len_pre;
+ size_t len_mid;
+ size_t len_post;
unsigned long discard_data;
unsigned long discard_low;
unsigned long discard_high;
+ unsigned long discard_len;
- /* If length is too short for an "ldp"/"stp" instruction pair,
- * then just copy individual bytes.
+ /* Calculate pre-aligned, aligned, and post-aligned lengths.
+ * (Align on the destination address, on the assumption that
+ * misaligned stores are likely to be more expensive than
+ * misaligned loads.)
*/
- if ( len < 16 ) {
- __asm__ __volatile__ ( "cbz %0, 2f\n\t"
- "\n1:\n\t"
- "sub %0, %0, #1\n\t"
- "ldrb %w1, [%3, %0]\n\t"
- "strb %w1, [%2, %0]\n\t"
- "cbnz %0, 1b\n\t"
- "\n2:\n\t"
- : "=&r" ( discard_offset ),
- "=&r" ( discard_data )
- : "r" ( dest ), "r" ( src ), "0" ( len )
- : "memory" );
- return;
- }
+ len_pre = ( ( ARM64_STRING_BLKSZ - ( ( intptr_t ) dest ) ) &
+ ( ARM64_STRING_BLKSZ - 1 ) );
+ if ( len_pre > len )
+ len_pre = len;
+ len -= len_pre;
+ len_mid = ( len & ~( ARM64_STRING_BLKSZ - 1 ) );
+ len -= len_mid;
+ len_post = len;
- /* Use "ldp"/"stp" to copy 16 bytes at a time: one initial
- * potentially unaligned access, multiple destination-aligned
- * accesses, one final potentially unaligned access.
- */
- __asm__ __volatile__ ( "ldp %3, %4, [%1], #16\n\t"
- "stp %3, %4, [%0], #16\n\t"
- "and %3, %0, #15\n\t"
- "sub %0, %0, %3\n\t"
- "sub %1, %1, %3\n\t"
- "bic %2, %5, #15\n\t"
- "b 2f\n\t"
+ /* Copy pre-aligned section */
+ __asm__ __volatile__ ( "cbz %2, 2f\n\t"
+ "\n1:\n\t"
+ "ldrb %w3, [%1], #1\n\t"
+ "strb %w3, [%0], #1\n\t"
+ "sub %2, %2, #1\n\t"
+ "cbnz %2, 1b\n\t"
+ "\n2:\n\t"
+ : "+r" ( dest ), "+r" ( src ),
+ "=&r" ( discard_len ),
+ "=&r" ( discard_data )
+ : "2" ( len_pre )
+ : "memory" );
+
+ /* Copy aligned section */
+ __asm__ __volatile__ ( "cbz %2, 2f\n\t"
"\n1:\n\t"
"ldp %3, %4, [%1], #16\n\t"
"stp %3, %4, [%0], #16\n\t"
+ "sub %2, %2, #16\n\t"
+ "cbnz %2, 1b\n\t"
"\n2:\n\t"
- "cmp %0, %2\n\t"
- "bne 1b\n\t"
- "ldp %3, %4, [%6, #-16]\n\t"
- "stp %3, %4, [%5, #-16]\n\t"
- : "=&r" ( discard_dest ),
- "=&r" ( discard_src ),
- "=&r" ( discard_end ),
+ : "+r" ( dest ), "+r" ( src ),
+ "=&r" ( discard_len ),
"=&r" ( discard_low ),
"=&r" ( discard_high )
- : "r" ( dest + len ), "r" ( src + len ),
- "0" ( dest ), "1" ( src )
- : "memory", "cc" );
+ : "2" ( len_mid )
+ : "memory" );
+
+ /* Copy post-aligned section */
+ __asm__ __volatile__ ( "cbz %2, 2f\n\t"
+ "\n1:\n\t"
+ "ldrb %w3, [%1], #1\n\t"
+ "strb %w3, [%0], #1\n\t"
+ "sub %2, %2, #1\n\t"
+ "cbnz %2, 1b\n\t"
+ "\n2:\n\t"
+ : "+r" ( dest ), "+r" ( src ),
+ "=&r" ( discard_len ),
+ "=&r" ( discard_data )
+ : "2" ( len_post )
+ : "memory" );
}
/**
@@ -102,44 +116,56 @@ void arm64_memcpy ( void *dest, const void *src, size_t len ) {
* @v len Length
*/
void arm64_bzero ( void *dest, size_t len ) {
- size_t discard_offset;
- void *discard_dest;
- void *discard_end;
+ size_t len_pre;
+ size_t len_mid;
+ size_t len_post;
+ unsigned long discard_len;
- /* If length is too short for an "stp" instruction, then just
- * zero individual bytes.
- */
- if ( len < 16 ) {
- __asm__ __volatile__ ( "cbz %0, 2f\n\t"
- "\n1:\n\t"
- "sub %0, %0, #1\n\t"
- "strb wzr, [%1, %0]\n\t"
- "cbnz %0, 1b\n\t"
- "\n2:\n\t"
- : "=&r" ( discard_offset )
- : "r" ( dest ), "0" ( len )
- : "memory" );
- return;
- }
+ /* Calculate pre-aligned, aligned, and post-aligned lengths */
+ len_pre = ( ( ARM64_STRING_BLKSZ - ( ( intptr_t ) dest ) ) &
+ ( ARM64_STRING_BLKSZ - 1 ) );
+ if ( len_pre > len )
+ len_pre = len;
+ len -= len_pre;
+ len_mid = ( len & ~( ARM64_STRING_BLKSZ - 1 ) );
+ len -= len_mid;
+ len_post = len;
- /* Use "stp" to zero 16 bytes at a time: one initial
- * potentially unaligned access, multiple aligned accesses,
- * one final potentially unaligned access.
- */
- __asm__ __volatile__ ( "stp xzr, xzr, [%0], #16\n\t"
- "bic %0, %0, #15\n\t"
- "bic %1, %2, #15\n\t"
- "b 2f\n\t"
+ /* Zero pre-aligned section */
+ __asm__ __volatile__ ( "cbz %1, 2f\n\t"
+ "\n1:\n\t"
+ "strb wzr, [%0], #1\n\t"
+ "sub %1, %1, #1\n\t"
+ "cbnz %1, 1b\n\t"
+ "\n2:\n\t"
+ : "+r" ( dest ),
+ "=&r" ( discard_len )
+ : "1" ( len_pre )
+ : "memory" );
+
+ /* Zero aligned section */
+ __asm__ __volatile__ ( "cbz %1, 2f\n\t"
"\n1:\n\t"
"stp xzr, xzr, [%0], #16\n\t"
+ "sub %1, %1, #16\n\t"
+ "cbnz %1, 1b\n\t"
"\n2:\n\t"
- "cmp %0, %1\n\t"
- "bne 1b\n\t"
- "stp xzr, xzr, [%2, #-16]\n\t"
- : "=&r" ( discard_dest ),
- "=&r" ( discard_end )
- : "r" ( dest + len ), "0" ( dest )
- : "memory", "cc" );
+ : "+r" ( dest ),
+ "=&r" ( discard_len )
+ : "1" ( len_mid )
+ : "memory" );
+
+ /* Zero post-aligned section */
+ __asm__ __volatile__ ( "cbz %1, 2f\n\t"
+ "\n1:\n\t"
+ "strb wzr, [%0], #1\n\t"
+ "sub %1, %1, #1\n\t"
+ "cbnz %1, 1b\n\t"
+ "\n2:\n\t"
+ : "+r" ( dest ),
+ "=&r" ( discard_len )
+ : "1" ( len_post )
+ : "memory" );
}
/**
diff --git a/src/arch/arm64/include/bits/bigint.h b/src/arch/arm64/include/bits/bigint.h
index 0d08bbd65..f4032e335 100644
--- a/src/arch/arm64/include/bits/bigint.h
+++ b/src/arch/arm64/include/bits/bigint.h
@@ -43,8 +43,9 @@ bigint_init_raw ( uint64_t *value0, unsigned int size,
* @v addend0 Element 0 of big integer to add
* @v value0 Element 0 of big integer to be added to
* @v size Number of elements
+ * @ret carry Carry out
*/
-static inline __attribute__ (( always_inline )) void
+static inline __attribute__ (( always_inline )) int
bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
@@ -54,6 +55,7 @@ bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
uint64_t discard_addend_i;
uint64_t discard_value_i;
unsigned int discard_size;
+ int carry;
__asm__ __volatile__ ( "cmn xzr, xzr\n\t" /* clear CF */
"\n1:\n\t"
@@ -68,9 +70,11 @@ bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
"=r" ( discard_size ),
"=r" ( discard_addend_i ),
"=r" ( discard_value_i ),
+ "=@cccs" ( carry ),
"+m" ( *value )
- : "0" ( addend0 ), "1" ( value0 ), "2" ( size )
- : "cc" );
+ : "0" ( addend0 ), "1" ( value0 ),
+ "2" ( size ) );
+ return carry;
}
/**
@@ -79,8 +83,9 @@ bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
* @v subtrahend0 Element 0 of big integer to subtract
* @v value0 Element 0 of big integer to be subtracted from
* @v size Number of elements
+ * @ret borrow Borrow out
*/
-static inline __attribute__ (( always_inline )) void
+static inline __attribute__ (( always_inline )) int
bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
@@ -90,6 +95,7 @@ bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0,
uint64_t discard_subtrahend_i;
uint64_t discard_value_i;
unsigned int discard_size;
+ int borrow;
__asm__ __volatile__ ( "cmp xzr, xzr\n\t" /* set CF */
"\n1:\n\t"
@@ -104,25 +110,28 @@ bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0,
"=r" ( discard_size ),
"=r" ( discard_subtrahend_i ),
"=r" ( discard_value_i ),
+ "=@cccc" ( borrow ),
"+m" ( *value )
: "0" ( subtrahend0 ), "1" ( value0 ),
- "2" ( size )
- : "cc" );
+ "2" ( size ) );
+ return borrow;
}
/**
- * Rotate big integer left
+ * Shift big integer left
*
* @v value0 Element 0 of big integer
* @v size Number of elements
+ * @ret out Bit shifted out
*/
-static inline __attribute__ (( always_inline )) void
-bigint_rol_raw ( uint64_t *value0, unsigned int size ) {
+static inline __attribute__ (( always_inline )) int
+bigint_shl_raw ( uint64_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_value;
uint64_t discard_value_i;
unsigned int discard_size;
+ int carry;
__asm__ __volatile__ ( "cmn xzr, xzr\n\t" /* clear CF */
"\n1:\n\t"
@@ -134,40 +143,43 @@ bigint_rol_raw ( uint64_t *value0, unsigned int size ) {
: "=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_value_i ),
+ "=@cccs" ( carry ),
"+m" ( *value )
- : "0" ( value0 ), "1" ( size )
- : "cc" );
+ : "0" ( value0 ), "1" ( size ) );
+ return carry;
}
/**
- * Rotate big integer right
+ * Shift big integer right
*
* @v value0 Element 0 of big integer
* @v size Number of elements
+ * @ret out Bit shifted out
*/
-static inline __attribute__ (( always_inline )) void
-bigint_ror_raw ( uint64_t *value0, unsigned int size ) {
+static inline __attribute__ (( always_inline )) int
+bigint_shr_raw ( uint64_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_value;
- uint64_t discard_value_i;
- uint64_t discard_value_j;
+ uint64_t discard_high;
unsigned int discard_size;
+ uint64_t low;
- __asm__ __volatile__ ( "mov %3, #0\n\t"
+ __asm__ __volatile__ ( "mov %2, #0\n\t"
"\n1:\n\t"
"sub %w1, %w1, #1\n\t"
- "ldr %2, [%0, %1, lsl #3]\n\t"
- "extr %3, %3, %2, #1\n\t"
- "str %3, [%0, %1, lsl #3]\n\t"
- "mov %3, %2\n\t"
+ "ldr %3, [%0, %1, lsl #3]\n\t"
+ "extr %2, %2, %3, #1\n\t"
+ "str %2, [%0, %1, lsl #3]\n\t"
+ "mov %2, %3\n\t"
"cbnz %w1, 1b\n\t"
: "=r" ( discard_value ),
"=r" ( discard_size ),
- "=r" ( discard_value_i ),
- "=r" ( discard_value_j ),
+ "=r" ( discard_high ),
+ "=r" ( low ),
"+m" ( *value )
: "0" ( value0 ), "1" ( size ) );
+ return ( low & 1 );
}
/**
@@ -218,25 +230,6 @@ bigint_is_geq_raw ( const uint64_t *value0, const uint64_t *reference0,
}
/**
- * Test if bit is set in big integer
- *
- * @v value0 Element 0 of big integer
- * @v size Number of elements
- * @v bit Bit to test
- * @ret is_set Bit is set
- */
-static inline __attribute__ (( always_inline )) int
-bigint_bit_is_set_raw ( const uint64_t *value0, unsigned int size,
- unsigned int bit ) {
- const bigint_t ( size ) __attribute__ (( may_alias )) *value =
- ( ( const void * ) value0 );
- unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) );
- unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) );
-
- return ( !! ( value->element[index] & ( 1UL << subindex ) ) );
-}
-
-/**
* Find highest bit set in big integer
*
* @v value0 Element 0 of big integer
@@ -310,10 +303,36 @@ bigint_done_raw ( const uint64_t *value0, unsigned int size __unused,
*(--out_byte) = *(value_byte++);
}
-extern void bigint_multiply_raw ( const uint64_t *multiplicand0,
- unsigned int multiplicand_size,
- const uint64_t *multiplier0,
- unsigned int multiplier_size,
- uint64_t *value0 );
+/**
+ * Multiply big integer elements
+ *
+ * @v multiplicand Multiplicand element
+ * @v multiplier Multiplier element
+ * @v result Result element
+ * @v carry Carry element
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_multiply_one ( const uint64_t multiplicand, const uint64_t multiplier,
+ uint64_t *result, uint64_t *carry ) {
+ uint64_t discard_low;
+ uint64_t discard_high;
+
+ __asm__ __volatile__ ( /* Perform multiplication */
+ "mul %0, %4, %5\n\t"
+ "umulh %1, %4, %5\n\t"
+ /* Accumulate result */
+ "adds %2, %2, %0\n\t"
+ "adc %1, %1, xzr\n\t"
+ /* Accumulate carry (cannot overflow) */
+ "adds %2, %2, %3\n\t"
+ "adc %3, %1, xzr\n\t"
+ : "=&r" ( discard_low ),
+ "=r" ( discard_high ),
+ "+r" ( *result ),
+ "+r" ( *carry )
+ : "r" ( multiplicand ),
+ "r" ( multiplier )
+ : "cc" );
+}
#endif /* _BITS_BIGINT_H */
diff --git a/src/arch/arm64/include/bits/lkrn.h b/src/arch/arm64/include/bits/lkrn.h
new file mode 100644
index 000000000..943464e9b
--- /dev/null
+++ b/src/arch/arm64/include/bits/lkrn.h
@@ -0,0 +1,30 @@
+#ifndef _BITS_LKRN_H
+#define _BITS_LKRN_H
+
+/** @file
+ *
+ * Linux kernel image invocation
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** Header magic value */
+#define LKRN_MAGIC_ARCH LKRN_MAGIC_AARCH64
+
+/**
+ * Jump to kernel entry point
+ *
+ * @v entry Kernel entry point
+ * @v fdt Device tree
+ */
+static inline __attribute__ (( noreturn )) void
+lkrn_jump ( physaddr_t entry, physaddr_t fdt ) {
+ register unsigned long x0 asm ( "x0" ) = fdt;
+
+ __asm__ __volatile__ ( "br %1"
+ : : "r" ( x0 ), "r" ( entry ) );
+ __builtin_unreachable();
+}
+
+#endif /* _BITS_LKRN_H */
diff --git a/src/arch/arm64/include/bits/profile.h b/src/arch/arm64/include/bits/profile.h
index 62ffa3772..4a5b3f7a1 100644
--- a/src/arch/arm64/include/bits/profile.h
+++ b/src/arch/arm64/include/bits/profile.h
@@ -16,7 +16,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
* @ret timestamp Timestamp
*/
-static inline __attribute__ (( always_inline )) uint64_t
+static inline __attribute__ (( always_inline )) unsigned long
profile_timestamp ( void ) {
uint64_t cycles;
diff --git a/src/arch/arm64/include/setjmp.h b/src/arch/arm64/include/bits/setjmp.h
index 85a7a9cad..6ffd2fb0a 100644
--- a/src/arch/arm64/include/setjmp.h
+++ b/src/arch/arm64/include/bits/setjmp.h
@@ -1,5 +1,5 @@
-#ifndef _SETJMP_H
-#define _SETJMP_H
+#ifndef _BITS_SETJMP_H
+#define _BITS_SETJMP_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
@@ -35,10 +35,4 @@ typedef struct {
uint64_t sp;
} jmp_buf[1];
-extern int __asmcall __attribute__ (( returns_twice ))
-setjmp ( jmp_buf env );
-
-extern void __asmcall __attribute__ (( noreturn ))
-longjmp ( jmp_buf env, int val );
-
-#endif /* _SETJMP_H */
+#endif /* _BITS_SETJMP_H */
diff --git a/src/arch/arm64/include/gdbmach.h b/src/arch/arm64/include/gdbmach.h
deleted file mode 100644
index cd152eedd..000000000
--- a/src/arch/arm64/include/gdbmach.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef GDBMACH_H
-#define GDBMACH_H
-
-/** @file
- *
- * GDB architecture specifics
- *
- * This file declares functions for manipulating the machine state and
- * debugging context.
- *
- */
-
-#include <stdint.h>
-
-typedef unsigned long gdbreg_t;
-
-/* Register snapshot */
-enum {
- /* Not yet implemented */
- GDBMACH_NREGS,
-};
-
-#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) )
-
-static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) {
- /* Not yet implemented */
- ( void ) regs;
- ( void ) pc;
-}
-
-static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) {
- /* Not yet implemented */
- ( void ) regs;
- ( void ) step;
-}
-
-static inline void gdbmach_breakpoint ( void ) {
- /* Not yet implemented */
-}
-
-extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len,
- int enable );
-extern void gdbmach_init ( void );
-
-#endif /* GDBMACH_H */
diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile
index e59f05fc8..44ccfccc8 100644
--- a/src/arch/i386/Makefile
+++ b/src/arch/i386/Makefile
@@ -1,3 +1,7 @@
+# Specify compressor
+#
+ZBIN = $(ZBIN32)
+
# Force i386-only instructions
#
CFLAGS += -march=i386
diff --git a/src/arch/i386/Makefile.efi b/src/arch/i386/Makefile.efi
index 37ede65ac..aa809eb5d 100644
--- a/src/arch/i386/Makefile.efi
+++ b/src/arch/i386/Makefile.efi
@@ -8,10 +8,6 @@ ELF2EFI = $(ELF2EFI32)
#
CFLAGS += -malign-double
-# Specify EFI boot file
-#
-EFI_BOOT_FILE = bootia32.efi
-
# Include generic EFI Makefile
#
MAKEDEPS += arch/x86/Makefile.efi
diff --git a/src/arch/i386/include/gdbmach.h b/src/arch/i386/include/bits/gdbmach.h
index 52cce7833..52cce7833 100644
--- a/src/arch/i386/include/gdbmach.h
+++ b/src/arch/i386/include/bits/gdbmach.h
diff --git a/src/arch/i386/include/bits/profile.h b/src/arch/i386/include/bits/profile.h
index e184d7b51..21c216a81 100644
--- a/src/arch/i386/include/bits/profile.h
+++ b/src/arch/i386/include/bits/profile.h
@@ -16,12 +16,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
* @ret timestamp Timestamp
*/
-static inline __attribute__ (( always_inline )) uint64_t
+static inline __attribute__ (( always_inline )) unsigned long
profile_timestamp ( void ) {
- uint64_t tsc;
+ uint32_t tsc;
/* Read timestamp counter */
- __asm__ __volatile__ ( "rdtsc" : "=A" ( tsc ) );
+ __asm__ __volatile__ ( "rdtsc" : "=a" ( tsc ) : : "edx" );
return tsc;
}
diff --git a/src/arch/i386/include/setjmp.h b/src/arch/i386/include/bits/setjmp.h
index 98566696a..6b2ec9613 100644
--- a/src/arch/i386/include/setjmp.h
+++ b/src/arch/i386/include/bits/setjmp.h
@@ -1,5 +1,5 @@
-#ifndef _SETJMP_H
-#define _SETJMP_H
+#ifndef _BITS_SETJMP_H
+#define _BITS_SETJMP_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
@@ -21,10 +21,4 @@ typedef struct {
uint32_t ebp;
} jmp_buf[1];
-extern int __asmcall __attribute__ (( returns_twice ))
-setjmp ( jmp_buf env );
-
-extern void __asmcall __attribute__ (( noreturn ))
-longjmp ( jmp_buf env, int val );
-
-#endif /* _SETJMP_H */
+#endif /* _BITS_SETJMP_H */
diff --git a/src/arch/i386/scripts/i386-kir.lds b/src/arch/i386/scripts/i386-kir.lds
index 13c36f2bf..4cb656000 100644
--- a/src/arch/i386/scripts/i386-kir.lds
+++ b/src/arch/i386/scripts/i386-kir.lds
@@ -88,6 +88,8 @@ SECTIONS {
__rodata16 = .;
*(.rodata16)
*(.rodata16.*)
+ *(.srodata)
+ *(.srodata.*)
*(.rodata)
*(.rodata.*)
}
@@ -95,6 +97,8 @@ SECTIONS {
__data16 = .;
*(.data16)
*(.data16.*)
+ *(.sdata)
+ *(.sdata.*)
*(.data)
*(.data.*)
KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */
@@ -107,6 +111,8 @@ SECTIONS {
_bss16 = .;
*(.bss16)
*(.bss16.*)
+ *(.sbss)
+ *(.sbss.*)
*(.bss)
*(.bss.*)
*(COMMON)
diff --git a/src/arch/loong64/Makefile b/src/arch/loong64/Makefile
index fd0bf137f..809af07eb 100644
--- a/src/arch/loong64/Makefile
+++ b/src/arch/loong64/Makefile
@@ -1,3 +1,7 @@
+# Specify compressor
+#
+ZBIN = $(ZBIN64)
+
# Assembler section type character
#
ASM_TCHAR := @
@@ -18,6 +22,9 @@ endif
# EFI requires -fshort-wchar, and nothing else currently uses wchar_t
CFLAGS += -fshort-wchar
+# Include LoongArch64-specific headers
+INCDIRS := arch/$(ARCH)/include $(INCDIRS)
+
# LoongArch64-specific directories containing source files
SRCDIRS += arch/loong64/core
SRCDIRS += arch/loong64/interface/efi
diff --git a/src/arch/loong64/Makefile.efi b/src/arch/loong64/Makefile.efi
index 1c51bcd67..611c910f2 100644
--- a/src/arch/loong64/Makefile.efi
+++ b/src/arch/loong64/Makefile.efi
@@ -4,10 +4,6 @@
#
ELF2EFI = $(ELF2EFI64)
-# Specify EFI boot file
-#
-EFI_BOOT_FILE = bootloongarch64.efi
-
# Include generic EFI Makefile
#
MAKEDEPS += Makefile.efi
diff --git a/src/arch/loong64/core/loong64_bigint.c b/src/arch/loong64/core/loong64_bigint.c
deleted file mode 100644
index b428e22c3..000000000
--- a/src/arch/loong64/core/loong64_bigint.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
- * Copyright (c) 2023, Xiaotian Wu <wuxiaotian@loongson.cn>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <stdint.h>
-#include <string.h>
-#include <ipxe/bigint.h>
-
-/** @file
- *
- * Big integer support
- */
-
-/**
- * Multiply big integers
- *
- * @v multiplicand0 Element 0 of big integer to be multiplied
- * @v multiplicand_size Number of elements in multiplicand
- * @v multiplier0 Element 0 of big integer to be multiplied
- * @v multiplier_size Number of elements in multiplier
- * @v result0 Element 0 of big integer to hold result
- */
-void bigint_multiply_raw ( const uint64_t *multiplicand0,
- unsigned int multiplicand_size,
- const uint64_t *multiplier0,
- unsigned int multiplier_size,
- uint64_t *result0 ) {
- unsigned int result_size = ( multiplicand_size + multiplier_size );
- const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
- *multiplicand = ( ( const void * ) multiplicand0 );
- const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
- *multiplier = ( ( const void * ) multiplier0 );
- bigint_t ( result_size ) __attribute__ (( may_alias ))
- *result = ( ( void * ) result0 );
- unsigned int i;
- unsigned int j;
- uint64_t multiplicand_element;
- uint64_t multiplier_element;
- uint64_t *result_elements;
- uint64_t discard_low;
- uint64_t discard_high;
- uint64_t discard_temp_low;
- uint64_t discard_temp_high;
-
- /* Zero result */
- memset ( result, 0, sizeof ( *result ) );
-
- /* Multiply integers one element at a time */
- for ( i = 0 ; i < multiplicand_size ; i++ ) {
- multiplicand_element = multiplicand->element[i];
- for ( j = 0 ; j < multiplier_size ; j++ ) {
- multiplier_element = multiplier->element[j];
- result_elements = &result->element[ i + j ];
- /* Perform a single multiply, and add the
- * resulting double-element into the result,
- * carrying as necessary. The carry can
- * never overflow beyond the end of the
- * result, since:
- *
- * a < 2^{n}, b < 2^{m} => ab < 2^{n+m}
- */
- __asm__ __volatile__ ( "mul.d %1, %6, %7\n\t"
- "mulh.du %2, %6, %7\n\t"
-
- "ld.d %3, %0, 0\n\t"
- "ld.d %4, %0, 8\n\t"
-
- "add.d %3, %3, %1\n\t"
- "sltu $t0, %3, %1\n\t"
-
- "add.d %4, %4, %2\n\t"
- "sltu $t1, %4, %2\n\t"
-
- "add.d %4, %4, $t0\n\t"
- "sltu $t0, %4, $t0\n\t"
- "or $t0, $t0, $t1\n\t"
-
- "st.d %3, %0, 0\n\t"
- "st.d %4, %0, 8\n\t"
-
- "addi.d %0, %0, 16\n\t"
- "beqz $t0, 2f\n"
- "1:\n\t"
- "ld.d %3, %0, 0\n\t"
- "add.d %3, %3, $t0\n\t"
- "sltu $t0, %3, $t0\n\t"
- "st.d %3, %0, 0\n\t"
- "addi.d %0, %0, 8\n\t"
- "bnez $t0, 1b\n"
- "2:"
- : "+r" ( result_elements ),
- "=&r" ( discard_low ),
- "=&r" ( discard_high ),
- "=r" ( discard_temp_low ),
- "=r" ( discard_temp_high ),
- "+m" ( *result )
- : "r" ( multiplicand_element ),
- "r" ( multiplier_element )
- : "t0", "t1" );
- }
- }
-}
diff --git a/src/arch/loong64/include/bits/acpi.h b/src/arch/loong64/include/bits/acpi.h
deleted file mode 100644
index 83dd1df9a..000000000
--- a/src/arch/loong64/include/bits/acpi.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_ACPI_H
-#define _BITS_ACPI_H
-
-/** @file
- *
- * LoongArch64-specific ACPI API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_ACPI_H */
diff --git a/src/arch/loong64/include/bits/bigint.h b/src/arch/loong64/include/bits/bigint.h
index bec748beb..6a879503a 100644
--- a/src/arch/loong64/include/bits/bigint.h
+++ b/src/arch/loong64/include/bits/bigint.h
@@ -43,8 +43,9 @@ bigint_init_raw ( uint64_t *value0, unsigned int size,
* @v addend0 Element 0 of big integer to add
* @v value0 Element 0 of big integer to be added to
* @v size Number of elements
+ * @ret carry Carry out
*/
-static inline __attribute__ (( always_inline )) void
+static inline __attribute__ (( always_inline )) int
bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
@@ -53,20 +54,20 @@ bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
uint64_t *discard_value;
uint64_t discard_addend_i;
uint64_t discard_value_i;
- uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size;
+ uint64_t carry;
__asm__ __volatile__ ( "\n1:\n\t"
/* Load addend[i] and value[i] */
"ld.d %3, %0, 0\n\t"
"ld.d %4, %1, 0\n\t"
/* Add carry flag and addend */
- "add.d %4, %4, %5\n\t"
- "sltu %6, %4, %5\n\t"
+ "add.d %4, %4, %6\n\t"
+ "sltu %5, %4, %6\n\t"
"add.d %4, %4, %3\n\t"
- "sltu %5, %4, %3\n\t"
- "or %5, %5, %6\n\t"
+ "sltu %6, %4, %3\n\t"
+ "or %6, %5, %6\n\t"
/* Store value[i] */
"st.d %4, %1, 0\n\t"
/* Loop */
@@ -79,11 +80,12 @@ bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
"=r" ( discard_size ),
"=r" ( discard_addend_i ),
"=r" ( discard_value_i ),
- "=r" ( discard_carry ),
"=r" ( discard_temp ),
+ "=r" ( carry ),
"+m" ( *value )
: "0" ( addend0 ), "1" ( value0 ),
- "2" ( size ), "5" ( 0 ) );
+ "2" ( size ), "6" ( 0 ) );
+ return carry;
}
/**
@@ -92,8 +94,9 @@ bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
* @v subtrahend0 Element 0 of big integer to subtract
* @v value0 Element 0 of big integer to be subtracted from
* @v size Number of elements
+ * @ret borrow Borrow out
*/
-static inline __attribute__ (( always_inline )) void
+static inline __attribute__ (( always_inline )) int
bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
@@ -102,20 +105,20 @@ bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0,
uint64_t *discard_value;
uint64_t discard_subtrahend_i;
uint64_t discard_value_i;
- uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size;
+ uint64_t borrow;
__asm__ __volatile__ ( "\n1:\n\t"
/* Load subtrahend[i] and value[i] */
"ld.d %3, %0, 0\n\t"
"ld.d %4, %1, 0\n\t"
/* Subtract carry flag and subtrahend */
- "sltu %6, %4, %5\n\t"
- "sub.d %4, %4, %5\n\t"
- "sltu %5, %4, %3\n\t"
+ "sltu %5, %4, %6\n\t"
+ "sub.d %4, %4, %6\n\t"
+ "sltu %6, %4, %3\n\t"
"sub.d %4, %4, %3\n\t"
- "or %5, %5, %6\n\t"
+ "or %6, %5, %6\n\t"
/* Store value[i] */
"st.d %4, %1, 0\n\t"
/* Loop */
@@ -128,38 +131,40 @@ bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0,
"=r" ( discard_size ),
"=r" ( discard_subtrahend_i ),
"=r" ( discard_value_i ),
- "=r" ( discard_carry ),
"=r" ( discard_temp ),
+ "=r" ( borrow ),
"+m" ( *value )
: "0" ( subtrahend0 ), "1" ( value0 ),
- "2" ( size ), "5" ( 0 ) );
+ "2" ( size ), "6" ( 0 ) );
+ return borrow;
}
/**
- * Rotate big integer left
+ * Shift big integer left
*
* @v value0 Element 0 of big integer
* @v size Number of elements
+ * @ret out Bit shifted out
*/
-static inline __attribute__ (( always_inline )) void
-bigint_rol_raw ( uint64_t *value0, unsigned int size ) {
+static inline __attribute__ (( always_inline )) int
+bigint_shl_raw ( uint64_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_value;
uint64_t discard_value_i;
- uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size;
+ uint64_t carry;
__asm__ __volatile__ ( "\n1:\n\t"
/* Load value[i] */
"ld.d %2, %0, 0\n\t"
/* Shift left */
"rotri.d %2, %2, 63\n\t"
- "andi %4, %2, 1\n\t"
- "xor %2, %2, %4\n\t"
- "or %2, %2, %3\n\t"
- "move %3, %4\n\t"
+ "andi %3, %2, 1\n\t"
+ "xor %2, %2, %3\n\t"
+ "or %2, %2, %4\n\t"
+ "move %4, %3\n\t"
/* Store value[i] */
"st.d %2, %0, 0\n\t"
/* Loop */
@@ -169,37 +174,39 @@ bigint_rol_raw ( uint64_t *value0, unsigned int size ) {
: "=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_value_i ),
- "=r" ( discard_carry ),
"=r" ( discard_temp ),
+ "=r" ( carry ),
"+m" ( *value )
- : "0" ( value0 ), "1" ( size ), "3" ( 0 )
+ : "0" ( value0 ), "1" ( size ), "4" ( 0 )
: "cc" );
+ return ( carry & 1 );
}
/**
- * Rotate big integer right
+ * Shift big integer right
*
* @v value0 Element 0 of big integer
* @v size Number of elements
+ * @ret out Bit shifted out
*/
-static inline __attribute__ (( always_inline )) void
-bigint_ror_raw ( uint64_t *value0, unsigned int size ) {
+static inline __attribute__ (( always_inline )) int
+bigint_shr_raw ( uint64_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_value;
uint64_t discard_value_i;
- uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size;
+ uint64_t carry;
__asm__ __volatile__ ( "\n1:\n\t"
/* Load value[i] */
"ld.d %2, %0, -8\n\t"
/* Shift right */
- "andi %4, %2, 1\n\t"
- "xor %2, %2, %4\n\t"
- "or %2, %2, %3\n\t"
- "move %3, %4\n\t"
+ "andi %3, %2, 1\n\t"
+ "xor %2, %2, %3\n\t"
+ "or %2, %2, %4\n\t"
+ "move %4, %3\n\t"
"rotri.d %2, %2, 1\n\t"
/* Store value[i] */
"st.d %2, %0, -8\n\t"
@@ -210,11 +217,12 @@ bigint_ror_raw ( uint64_t *value0, unsigned int size ) {
: "=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_value_i ),
- "=r" ( discard_carry ),
"=r" ( discard_temp ),
+ "=r" ( carry ),
"+m" ( *value )
- : "0" ( value0 + size ), "1" ( size ), "3" ( 0 )
+ : "0" ( value0 + size ), "1" ( size ), "4" ( 0 )
: "cc" );
+ return ( carry & 1 );
}
/**
@@ -265,25 +273,6 @@ bigint_is_geq_raw ( const uint64_t *value0, const uint64_t *reference0,
}
/**
- * Test if bit is set in big integer
- *
- * @v value0 Element 0 of big integer
- * @v size Number of elements
- * @v bit Bit to test
- * @ret is_set Bit is set
- */
-static inline __attribute__ (( always_inline )) int
-bigint_bit_is_set_raw ( const uint64_t *value0, unsigned int size,
- unsigned int bit ) {
- const bigint_t ( size ) __attribute__ (( may_alias )) *value =
- ( ( const void * ) value0 );
- unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) );
- unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) );
-
- return ( !! ( value->element[index] & ( 1UL << subindex ) ) );
-}
-
-/**
* Find highest bit set in big integer
*
* @v value0 Element 0 of big integer
@@ -357,10 +346,39 @@ bigint_done_raw ( const uint64_t *value0, unsigned int size __unused,
*(--out_byte) = *(value_byte++);
}
-extern void bigint_multiply_raw ( const uint64_t *multiplicand0,
- unsigned int multiplicand_size,
- const uint64_t *multiplier0,
- unsigned int multiplier_size,
- uint64_t *value0 );
+/**
+ * Multiply big integer elements
+ *
+ * @v multiplicand Multiplicand element
+ * @v multiplier Multiplier element
+ * @v result Result element
+ * @v carry Carry element
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_multiply_one ( const uint64_t multiplicand, const uint64_t multiplier,
+ uint64_t *result, uint64_t *carry ) {
+ uint64_t discard_low;
+ uint64_t discard_high;
+ uint64_t discard_carry;
+
+ __asm__ __volatile__ ( /* Perform multiplication */
+ "mul.d %0, %5, %6\n\t"
+ "mulh.du %1, %5, %6\n\t"
+ /* Accumulate low half */
+ "add.d %3, %3, %0\n\t"
+ "sltu %2, %3, %0\n\t"
+ "add.d %1, %1, %2\n\t"
+ /* Accumulate carry (cannot overflow) */
+ "add.d %3, %3, %4\n\t"
+ "sltu %2, %3, %4\n\t"
+ "add.d %4, %1, %2\n\t"
+ : "=&r" ( discard_low ),
+ "=r" ( discard_high ),
+ "=r" ( discard_carry ),
+ "+r" ( *result ),
+ "+r" ( *carry )
+ : "r" ( multiplicand ),
+ "r" ( multiplier ) );
+}
#endif /* _BITS_BIGINT_H */
diff --git a/src/arch/loong64/include/bits/hyperv.h b/src/arch/loong64/include/bits/hyperv.h
deleted file mode 100644
index f0e0c8793..000000000
--- a/src/arch/loong64/include/bits/hyperv.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_HYPERV_H
-#define _BITS_HYPERV_H
-
-/** @file
- *
- * Hyper-V interface
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_HYPERV_H */
diff --git a/src/arch/loong64/include/bits/mp.h b/src/arch/loong64/include/bits/mp.h
deleted file mode 100644
index fef2fd59a..000000000
--- a/src/arch/loong64/include/bits/mp.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_MP_H
-#define _BITS_MP_H
-
-/** @file
- *
- * LoongArch64-specific multiprocessor API implementation
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_MP_H */
diff --git a/src/arch/loong64/include/bits/nap.h b/src/arch/loong64/include/bits/nap.h
index 2deba3558..d904db537 100644
--- a/src/arch/loong64/include/bits/nap.h
+++ b/src/arch/loong64/include/bits/nap.h
@@ -9,6 +9,12 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-#include <ipxe/efi/efiloong64_nap.h>
+/**
+ * Sleep until next CPU interrupt
+ *
+ */
+static inline __attribute__ (( always_inline )) void cpu_halt ( void ) {
+ __asm__ __volatile__ ( "idle 0" );
+}
#endif /* _BITS_NAP_H */
diff --git a/src/arch/loong64/include/bits/pci_io.h b/src/arch/loong64/include/bits/pci_io.h
deleted file mode 100644
index fdc5141cf..000000000
--- a/src/arch/loong64/include/bits/pci_io.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_PCI_IO_H
-#define _BITS_PCI_IO_H
-
-/** @file
- *
- * LoongArch64-specific PCI I/O API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_PCI_IO_H */
diff --git a/src/arch/loong64/include/bits/profile.h b/src/arch/loong64/include/bits/profile.h
index 9f597ce2d..02f8d4b7c 100644
--- a/src/arch/loong64/include/bits/profile.h
+++ b/src/arch/loong64/include/bits/profile.h
@@ -16,7 +16,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
* @ret timestamp Timestamp
*/
-static inline __attribute__ (( always_inline )) uint64_t
+static inline __attribute__ (( always_inline )) unsigned long
profile_timestamp ( void ) {
uint64_t cycles;
diff --git a/src/arch/loong64/include/bits/reboot.h b/src/arch/loong64/include/bits/reboot.h
deleted file mode 100644
index 96a1eb1ce..000000000
--- a/src/arch/loong64/include/bits/reboot.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_REBOOT_H
-#define _BITS_REBOOT_H
-
-/** @file
- *
- * LoongArch64-specific reboot API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_REBOOT_H */
diff --git a/src/arch/loong64/include/bits/sanboot.h b/src/arch/loong64/include/bits/sanboot.h
deleted file mode 100644
index f9205e2ad..000000000
--- a/src/arch/loong64/include/bits/sanboot.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_SANBOOT_H
-#define _BITS_SANBOOT_H
-
-/** @file
- *
- * LoongArch64-specific sanboot API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_SANBOOT_H */
diff --git a/src/arch/loong64/include/bits/setjmp.h b/src/arch/loong64/include/bits/setjmp.h
new file mode 100644
index 000000000..c8d7cef0e
--- /dev/null
+++ b/src/arch/loong64/include/bits/setjmp.h
@@ -0,0 +1,23 @@
+#ifndef _BITS_SETJMP_H
+#define _BITS_SETJMP_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** A jump buffer */
+typedef struct {
+ uint64_t s0;
+ uint64_t s1;
+ uint64_t s2;
+ uint64_t s3;
+ uint64_t s4;
+ uint64_t s5;
+ uint64_t s6;
+ uint64_t s7;
+ uint64_t s8;
+
+ uint64_t fp;
+ uint64_t sp;
+ uint64_t ra;
+} jmp_buf[1];
+
+#endif /* _BITS_SETJMP_H */
diff --git a/src/arch/loong64/include/bits/smbios.h b/src/arch/loong64/include/bits/smbios.h
deleted file mode 100644
index 6c87db430..000000000
--- a/src/arch/loong64/include/bits/smbios.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_SMBIOS_H
-#define _BITS_SMBIOS_H
-
-/** @file
- *
- * LoongArch64-specific SMBIOS API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_SMBIOS_H */
diff --git a/src/arch/loong64/include/bits/tcpip.h b/src/arch/loong64/include/bits/tcpip.h
deleted file mode 100644
index fc3c5b3ff..000000000
--- a/src/arch/loong64/include/bits/tcpip.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef _BITS_TCPIP_H
-#define _BITS_TCPIP_H
-
-/** @file
- *
- * Transport-network layer interface
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-static inline __attribute__ (( always_inline )) uint16_t
-tcpip_continue_chksum ( uint16_t partial, const void *data, size_t len ) {
-
- /* Not yet optimised */
- return generic_tcpip_continue_chksum ( partial, data, len );
-}
-
-#endif /* _BITS_TCPIP_H */
diff --git a/src/arch/loong64/include/bits/time.h b/src/arch/loong64/include/bits/time.h
deleted file mode 100644
index 4cd7485cf..000000000
--- a/src/arch/loong64/include/bits/time.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_TIME_H
-#define _BITS_TIME_H
-
-/** @file
- *
- * LoongArch64-specific time API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_TIME_H */
diff --git a/src/arch/loong64/include/bits/uaccess.h b/src/arch/loong64/include/bits/uaccess.h
deleted file mode 100644
index dddd9be04..000000000
--- a/src/arch/loong64/include/bits/uaccess.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_UACCESS_H
-#define _BITS_UACCESS_H
-
-/** @file
- *
- * LoongArch64-specific user access API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_UACCESS_H */
diff --git a/src/arch/loong64/include/bits/uart.h b/src/arch/loong64/include/bits/uart.h
deleted file mode 100644
index 6f85975f7..000000000
--- a/src/arch/loong64/include/bits/uart.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_UART_H
-#define _BITS_UART_H
-
-/** @file
- *
- * 16550-compatible UART
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_UART_H */
diff --git a/src/arch/loong64/include/bits/umalloc.h b/src/arch/loong64/include/bits/umalloc.h
deleted file mode 100644
index f6978b8bd..000000000
--- a/src/arch/loong64/include/bits/umalloc.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_UMALLOC_H
-#define _BITS_UMALLOC_H
-
-/** @file
- *
- * LoongArch64-specific user memory allocation API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_UMALLOC_H */
diff --git a/src/arch/loong64/include/bits/xen.h b/src/arch/loong64/include/bits/xen.h
deleted file mode 100644
index 2a3d7741c..000000000
--- a/src/arch/loong64/include/bits/xen.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef _BITS_XEN_H
-#define _BITS_XEN_H
-
-/** @file
- *
- * Xen interface
- *
- */
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <ipxe/nonxen.h>
-
-#endif /* _BITS_XEN_H */
diff --git a/src/arch/loong64/include/gdbmach.h b/src/arch/loong64/include/gdbmach.h
deleted file mode 100644
index cd152eedd..000000000
--- a/src/arch/loong64/include/gdbmach.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef GDBMACH_H
-#define GDBMACH_H
-
-/** @file
- *
- * GDB architecture specifics
- *
- * This file declares functions for manipulating the machine state and
- * debugging context.
- *
- */
-
-#include <stdint.h>
-
-typedef unsigned long gdbreg_t;
-
-/* Register snapshot */
-enum {
- /* Not yet implemented */
- GDBMACH_NREGS,
-};
-
-#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) )
-
-static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) {
- /* Not yet implemented */
- ( void ) regs;
- ( void ) pc;
-}
-
-static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) {
- /* Not yet implemented */
- ( void ) regs;
- ( void ) step;
-}
-
-static inline void gdbmach_breakpoint ( void ) {
- /* Not yet implemented */
-}
-
-extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len,
- int enable );
-extern void gdbmach_init ( void );
-
-#endif /* GDBMACH_H */
diff --git a/src/arch/loong64/include/ipxe/efi/efiloong64_nap.h b/src/arch/loong64/include/ipxe/efi/efiloong64_nap.h
deleted file mode 100644
index 5c0d38636..000000000
--- a/src/arch/loong64/include/ipxe/efi/efiloong64_nap.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef _IPXE_EFILOONG64_NAP_H
-#define _IPXE_EFILOONG64_NAP_H
-
-/** @file
- *
- * EFI CPU sleeping
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#ifdef NAP_EFILOONG64
-#define NAP_PREFIX_efiloong64
-#else
-#define NAP_PREFIX_efiloong64 __efiloong64_
-#endif
-
-#endif /* _IPXE_EFILOONG64_NAP_H */
diff --git a/src/arch/loong64/include/setjmp.h b/src/arch/loong64/include/setjmp.h
deleted file mode 100644
index 1e5168338..000000000
--- a/src/arch/loong64/include/setjmp.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef _SETJMP_H
-#define _SETJMP_H
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <stdint.h>
-
-/** jump buffer env*/
-typedef struct {
- uint64_t s0;
- uint64_t s1;
- uint64_t s2;
- uint64_t s3;
- uint64_t s4;
- uint64_t s5;
- uint64_t s6;
- uint64_t s7;
- uint64_t s8;
-
- uint64_t fp;
- uint64_t sp;
- uint64_t ra;
-} jmp_buf[1];
-
-extern int __asmcall __attribute__ (( returns_twice ))
-setjmp ( jmp_buf env );
-
-extern void __asmcall __attribute__ (( noreturn ))
-longjmp ( jmp_buf env, int val );
-
-#endif /* _SETJMP_H */
diff --git a/src/arch/riscv/Makefile b/src/arch/riscv/Makefile
new file mode 100644
index 000000000..324e1403e
--- /dev/null
+++ b/src/arch/riscv/Makefile
@@ -0,0 +1,22 @@
+# Assembler section type character
+#
+ASM_TCHAR := @
+ASM_TCHAR_OPS := @
+
+# Include RISCV-specific headers
+#
+INCDIRS := arch/$(ARCH)/include arch/riscv/include $(INCDIRS)
+
+# RISCV-specific directories containing source files
+#
+SRCDIRS += arch/riscv/core
+SRCDIRS += arch/riscv/interface/sbi
+SRCDIRS += arch/riscv/prefix
+
+# RISCV-specific flags
+#
+CFLAGS += -mno-strict-align -mno-plt
+
+# EFI requires -fshort-wchar, and nothing else currently uses wchar_t
+#
+CFLAGS += -fshort-wchar
diff --git a/src/arch/riscv/Makefile.efi b/src/arch/riscv/Makefile.efi
new file mode 100644
index 000000000..957e8b884
--- /dev/null
+++ b/src/arch/riscv/Makefile.efi
@@ -0,0 +1,10 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# RISCV-specific flags
+#
+CFLAGS += -mcmodel=medany
+
+# Include generic EFI Makefile
+#
+MAKEDEPS += Makefile.efi
+include Makefile.efi
diff --git a/src/arch/riscv/Makefile.linux b/src/arch/riscv/Makefile.linux
new file mode 100644
index 000000000..42590441e
--- /dev/null
+++ b/src/arch/riscv/Makefile.linux
@@ -0,0 +1,6 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# Include generic Linux Makefile
+#
+MAKEDEPS += Makefile.linux
+include Makefile.linux
diff --git a/src/arch/riscv/Makefile.sbi b/src/arch/riscv/Makefile.sbi
new file mode 100644
index 000000000..5e5dc9a6b
--- /dev/null
+++ b/src/arch/riscv/Makefile.sbi
@@ -0,0 +1,31 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# Build a position-independent executable, with relocations required
+# only for data values. Runtime relocations are applied by the
+# prefix code.
+#
+CFLAGS += -mcmodel=medany -fpie
+LDFLAGS += -pie --no-dynamic-linker -z combreloc
+
+# Place explicitly zero-initialised variables in the .data section
+# rather than in .bss, so that we can rely on their values even during
+# parsing of the system memory map prior to relocation (and therefore
+# prior to explicit zeroing of the .bss section).
+#
+CFLAGS += -fno-zero-initialized-in-bss
+
+# Linker script
+#
+LDSCRIPT = arch/riscv/scripts/sbi.lds
+
+# Media types
+#
+MEDIA += sbi
+MEDIA += lkrn
+
+# Padded flash device images (e.g. for QEMU's -pflash option)
+#
+NON_AUTO_MEDIA += pf32
+%.pf32 : %.sbi $(MAKEDEPS)
+ $(Q)$(CP) $< $@
+ $(Q)$(TRUNCATE) -s 32M $@
diff --git a/src/arch/riscv/core/hart.c b/src/arch/riscv/core/hart.c
new file mode 100644
index 000000000..d9cfb4e5d
--- /dev/null
+++ b/src/arch/riscv/core/hart.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Hardware threads (harts)
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ipxe/fdt.h>
+#include <ipxe/hart.h>
+
+/** Boot hart ID */
+unsigned long boot_hart;
+
+/** Colour for debug messages */
+#define colour &boot_hart
+
+/**
+ * Find boot hart node
+ *
+ * @v offset Boot hart node offset
+ * @ret rc Return status code
+ */
+static int hart_node ( unsigned int *offset ) {
+ char path[27 /* "/cpus/cpu@XXXXXXXXXXXXXXXX" + NUL */ ];
+ int rc;
+
+ /* Construct node path */
+ snprintf ( path, sizeof ( path ), "/cpus/cpu@%lx", boot_hart );
+
+ /* Find node */
+ if ( ( rc = fdt_path ( &sysfdt, path, offset ) ) != 0 ) {
+ DBGC ( colour, "HART could not find %s: %s\n",
+ path, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Check for supported extension
+ *
+ * @v ext Extension name (including leading underscore)
+ * @ret rc Return status code
+ */
+int hart_supported ( const char *ext ) {
+ unsigned int offset;
+ const char *isa;
+ const char *tmp;
+ int rc;
+
+ /* Find boot hart node */
+ if ( ( rc = hart_node ( &offset ) ) != 0 )
+ return rc;
+
+ /* Get ISA description */
+ isa = fdt_string ( &sysfdt, offset, "riscv,isa" );
+ if ( ! isa ) {
+ DBGC ( colour, "HART could not identify ISA\n" );
+ return -ENOENT;
+ }
+ DBGC ( colour, "HART supports %s\n", isa );
+
+ /* Check for presence of extension */
+ tmp = isa;
+ while ( ( tmp = strstr ( tmp, ext ) ) != NULL ) {
+ tmp += strlen ( ext );
+ if ( ( *tmp == '\0' ) || ( *tmp == '_' ) )
+ return 0;
+ }
+
+ return -ENOTSUP;
+}
diff --git a/src/arch/riscv/core/riscv_dma.c b/src/arch/riscv/core/riscv_dma.c
new file mode 100644
index 000000000..7b48b1bdd
--- /dev/null
+++ b/src/arch/riscv/core/riscv_dma.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <assert.h>
+#include <ipxe/zicbom.h>
+#include <ipxe/iomap.h>
+#include <ipxe/dma.h>
+
+/** @file
+ *
+ * iPXE DMA API for RISC-V
+ *
+ */
+
+/** Minimum alignment for coherent DMA allocations
+ *
+ * We set this sufficiently high to ensure that we do not end up with
+ * both cached and uncached uses in the same cacheline.
+ */
+#define RISCV_DMA_ALIGN 256
+
+/**
+ * Map buffer for DMA
+ *
+ * @v dma DMA device
+ * @v map DMA mapping to fill in
+ * @v addr Buffer address
+ * @v len Length of buffer
+ * @v flags Mapping flags
+ * @ret rc Return status code
+ */
+static int riscv_dma_map ( struct dma_device *dma,
+ struct dma_mapping *map,
+ void *addr, size_t len, int flags ) {
+
+ /* Sanity check: we cannot support bidirectional mappings */
+ assert ( ! ( ( flags & DMA_TX ) & ( flags & DMA_RX ) ) );
+
+ /* Populate mapping */
+ map->dma = dma;
+ map->offset = 0;
+ map->token = NULL;
+
+ /* Flush cached data to transmit buffers */
+ if ( flags & DMA_TX )
+ cache_clean ( addr, len );
+
+ /* Invalidate cached data in receive buffers and record address */
+ if ( flags & DMA_RX ) {
+ cache_invalidate ( addr, len );
+ map->token = addr;
+ }
+
+ /* Increment mapping count (for debugging) */
+ if ( DBG_LOG )
+ dma->mapped++;
+
+ return 0;
+}
+
+/**
+ * Unmap buffer
+ *
+ * @v map DMA mapping
+ * @v len Used length
+ */
+static void riscv_dma_unmap ( struct dma_mapping *map, size_t len ) {
+ struct dma_device *dma = map->dma;
+ void *addr = map->token;
+
+ /* Invalidate cached data in receive buffers */
+ if ( addr )
+ cache_invalidate ( addr, len );
+
+ /* Clear mapping */
+ map->dma = NULL;
+
+ /* Decrement mapping count (for debugging) */
+ if ( DBG_LOG )
+ dma->mapped--;
+}
+
+/**
+ * Allocate and map DMA-coherent buffer
+ *
+ * @v dma DMA device
+ * @v map DMA mapping to fill in
+ * @v len Length of buffer
+ * @v align Physical alignment
+ * @ret addr Buffer address, or NULL on error
+ */
+static void * riscv_dma_alloc ( struct dma_device *dma,
+ struct dma_mapping *map,
+ size_t len, size_t align ) {
+ physaddr_t phys;
+ void *addr;
+ void *caddr;
+
+ /* Round up length and alignment */
+ len = ( ( len + RISCV_DMA_ALIGN - 1 ) & ~( RISCV_DMA_ALIGN - 1 ) );
+ if ( align < RISCV_DMA_ALIGN )
+ align = RISCV_DMA_ALIGN;
+
+ /* Allocate from heap */
+ addr = malloc_phys ( len, align );
+ if ( ! addr )
+ return NULL;
+
+ /* Invalidate any existing cached data */
+ cache_invalidate ( addr, len );
+
+ /* Record mapping */
+ map->dma = dma;
+ map->token = addr;
+
+ /* Calculate coherently-mapped virtual address */
+ phys = virt_to_phys ( addr );
+ assert ( phys == ( ( uint32_t ) phys ) );
+ caddr = ( ( void * ) ( intptr_t ) ( phys + svpage_dma32() ) );
+ assert ( phys == virt_to_phys ( caddr ) );
+ DBGC ( dma, "DMA allocated [%#08lx,%#08lx) via %p\n",
+ phys, ( phys + len ), caddr );
+
+ /* Increment allocation count (for debugging) */
+ if ( DBG_LOG )
+ dma->allocated++;
+
+ return caddr;
+}
+
+/**
+ * Unmap and free DMA-coherent buffer
+ *
+ * @v dma DMA device
+ * @v map DMA mapping
+ * @v addr Buffer address
+ * @v len Length of buffer
+ */
+static void riscv_dma_free ( struct dma_mapping *map,
+ void *addr, size_t len ) {
+ struct dma_device *dma = map->dma;
+
+ /* Sanity check */
+ assert ( virt_to_phys ( addr ) == virt_to_phys ( map->token ) );
+
+ /* Round up length to match allocation */
+ len = ( ( len + RISCV_DMA_ALIGN - 1 ) & ~( RISCV_DMA_ALIGN - 1 ) );
+
+ /* Free original allocation */
+ free_phys ( map->token, len );
+
+ /* Clear mapping */
+ map->dma = NULL;
+ map->token = NULL;
+
+ /* Decrement allocation count (for debugging) */
+ if ( DBG_LOG )
+ dma->allocated--;
+}
+
+PROVIDE_DMAAPI ( riscv, dma_map, riscv_dma_map );
+PROVIDE_DMAAPI ( riscv, dma_unmap, riscv_dma_unmap );
+PROVIDE_DMAAPI ( riscv, dma_alloc, riscv_dma_alloc );
+PROVIDE_DMAAPI ( riscv, dma_free, riscv_dma_free );
+PROVIDE_DMAAPI ( riscv, dma_umalloc, riscv_dma_alloc );
+PROVIDE_DMAAPI ( riscv, dma_ufree, riscv_dma_free );
+PROVIDE_DMAAPI_INLINE ( riscv, dma_set_mask );
+PROVIDE_DMAAPI_INLINE ( riscv, dma );
diff --git a/src/arch/arm/interface/efi/efiarm_nap.c b/src/arch/riscv/core/riscv_io.c
index fba7a5d82..756b39752 100644
--- a/src/arch/arm/interface/efi/efiarm_nap.c
+++ b/src/arch/riscv/core/riscv_io.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -23,35 +23,24 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-#include <ipxe/nap.h>
-#include <ipxe/efi/efi.h>
+#include <ipxe/io.h>
+#include <ipxe/riscv_io.h>
/** @file
*
- * iPXE CPU sleeping API for EFI
+ * iPXE I/O API for RISC-V
*
*/
-/**
- * Sleep until next interrupt
- *
- */
-static void efiarm_cpu_nap ( void ) {
- /*
- * I can't find any EFI API that allows us to put the CPU to
- * sleep. The CpuSleep() function is defined in CpuLib.h, but
- * isn't part of any exposed protocol so we have no way to
- * call it.
- *
- * The EFI shell doesn't seem to bother sleeping the CPU; it
- * just sits there idly burning power.
- *
- * If a shutdown is in progess, there may be nothing to
- * generate an interrupt since the timer is disabled in the
- * first step of ExitBootServices().
- */
- if ( ! efi_shutdown_in_progress )
- __asm__ __volatile__ ( "wfi" );
-}
-
-PROVIDE_NAP ( efiarm, cpu_nap, efiarm_cpu_nap );
+PROVIDE_IOAPI_INLINE ( riscv, phys_to_bus );
+PROVIDE_IOAPI_INLINE ( riscv, bus_to_phys );
+PROVIDE_IOAPI_INLINE ( riscv, readb );
+PROVIDE_IOAPI_INLINE ( riscv, readw );
+PROVIDE_IOAPI_INLINE ( riscv, readl );
+PROVIDE_IOAPI_INLINE ( riscv, writeb );
+PROVIDE_IOAPI_INLINE ( riscv, writew );
+PROVIDE_IOAPI_INLINE ( riscv, writel );
+PROVIDE_IOAPI_INLINE ( riscv, readq );
+PROVIDE_IOAPI_INLINE ( riscv, writeq );
+PROVIDE_IOAPI_INLINE ( riscv, mb );
+PROVIDE_DUMMY_PIO ( riscv );
diff --git a/src/arch/riscv/core/riscv_string.c b/src/arch/riscv/core/riscv_string.c
new file mode 100644
index 000000000..e28dc8951
--- /dev/null
+++ b/src/arch/riscv/core/riscv_string.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+/** @file
+ *
+ * Optimised string operations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+
+/**
+ * Copy memory area
+ *
+ * @v dest Destination address
+ * @v src Source address
+ * @v len Length
+ * @ret dest Destination address
+ */
+void riscv_memcpy ( void *dest, const void *src, size_t len ) {
+ size_t len_pre;
+ size_t len_mid;
+ size_t len_post;
+ unsigned long discard_data;
+
+ /* Calculate pre-aligned, aligned, and post-aligned lengths.
+ * (Align on the destination address, on the assumption that
+ * misaligned stores are likely to be more expensive than
+ * misaligned loads.)
+ */
+ len_pre = ( ( sizeof ( unsigned long ) - ( ( intptr_t ) dest ) ) &
+ ( sizeof ( unsigned long ) - 1 ) );
+ if ( len_pre > len )
+ len_pre = len;
+ len -= len_pre;
+ len_mid = ( len & ~( sizeof ( unsigned long ) - 1 ) );
+ len -= len_mid;
+ len_post = len;
+
+ /* Copy pre-aligned section */
+ __asm__ __volatile__ ( "j 2f\n\t"
+ "\n1:\n\t"
+ "lb %2, (%1)\n\t"
+ "sb %2, (%0)\n\t"
+ "addi %0, %0, 1\n\t"
+ "addi %1, %1, 1\n\t"
+ "\n2:\n\t"
+ "bne %0, %3, 1b\n\t"
+ : "+r" ( dest ), "+r" ( src ),
+ "=&r" ( discard_data )
+ : "r" ( dest + len_pre )
+ : "memory" );
+
+ /* Copy aligned section */
+ __asm__ __volatile__ ( "j 2f\n\t"
+ "\n1:\n\t"
+ LOADN " %2, (%1)\n\t"
+ STOREN " %2, (%0)\n\t"
+ "addi %0, %0, %4\n\t"
+ "addi %1, %1, %4\n\t"
+ "\n2:\n\t"
+ "bne %0, %3, 1b\n\t"
+ : "+r" ( dest ), "+r" ( src ),
+ "=&r" ( discard_data )
+ : "r" ( dest + len_mid ),
+ "i" ( sizeof ( unsigned long ) )
+ : "memory" );
+
+ /* Copy post-aligned section */
+ __asm__ __volatile__ ( "j 2f\n\t"
+ "\n1:\n\t"
+ "lb %2, (%1)\n\t"
+ "sb %2, (%0)\n\t"
+ "addi %0, %0, 1\n\t"
+ "addi %1, %1, 1\n\t"
+ "\n2:\n\t"
+ "bne %0, %3, 1b\n\t"
+ : "+r" ( dest ), "+r" ( src ),
+ "=&r" ( discard_data )
+ : "r" ( dest + len_post )
+ : "memory" );
+}
+
+/**
+ * Zero memory region
+ *
+ * @v dest Destination region
+ * @v len Length
+ */
+void riscv_bzero ( void *dest, size_t len ) {
+ size_t len_pre;
+ size_t len_mid;
+ size_t len_post;
+
+ /* Calculate pre-aligned, aligned, and post-aligned lengths */
+ len_pre = ( ( sizeof ( unsigned long ) - ( ( intptr_t ) dest ) ) &
+ ( sizeof ( unsigned long ) - 1 ) );
+ if ( len_pre > len )
+ len_pre = len;
+ len -= len_pre;
+ len_mid = ( len & ~( sizeof ( unsigned long ) - 1 ) );
+ len -= len_mid;
+ len_post = len;
+
+ /* Zero pre-aligned section */
+ __asm__ __volatile__ ( "j 2f\n\t"
+ "\n1:\n\t"
+ "sb zero, (%0)\n\t"
+ "addi %0, %0, 1\n\t"
+ "\n2:\n\t"
+ "bne %0, %1, 1b\n\t"
+ : "+r" ( dest )
+ : "r" ( dest + len_pre )
+ : "memory" );
+
+ /* Zero aligned section */
+ __asm__ __volatile__ ( "j 2f\n\t"
+ "\n1:\n\t"
+ STOREN " zero, (%0)\n\t"
+ "addi %0, %0, %2\n\t"
+ "\n2:\n\t"
+ "bne %0, %1, 1b\n\t"
+ : "+r" ( dest )
+ : "r" ( dest + len_mid ),
+ "i" ( sizeof ( unsigned long ) )
+ : "memory" );
+
+ /* Zero post-aligned section */
+ __asm__ __volatile__ ( "j 2f\n\t"
+ "\n1:\n\t"
+ "sb zero, (%0)\n\t"
+ "addi %0, %0, 1\n\t"
+ "\n2:\n\t"
+ "bne %0, %1, 1b\n\t"
+ : "+r" ( dest )
+ : "r" ( dest + len_post )
+ : "memory" );
+}
+
+/**
+ * Fill memory region
+ *
+ * @v dest Destination region
+ * @v len Length
+ * @v character Fill character
+ *
+ * The unusual parameter order is to allow for more efficient
+ * tail-calling to riscv_bzero() when zeroing a region.
+ */
+void riscv_memset ( void *dest, size_t len, int character ) {
+
+ /* Do nothing if length is zero */
+ if ( ! len )
+ return;
+
+ /* Use optimised zeroing code if applicable */
+ if ( character == 0 ) {
+ riscv_bzero ( dest, len );
+ return;
+ }
+
+ /* Fill one byte at a time. Calling memset() with a non-zero
+ * value is relatively rare and unlikely to be
+ * performance-critical.
+ */
+ __asm__ __volatile__ ( "\n1:\n\t"
+ "sb %2, (%0)\n\t"
+ "addi %0, %0, 1\n\t"
+ "bne %0, %1, 1b\n\t"
+ : "+r" ( dest )
+ : "r" ( dest + len ), "r" ( character )
+ : "memory" );
+}
+
+/**
+ * Copy (possibly overlapping) memory region
+ *
+ * @v dest Destination region
+ * @v src Source region
+ * @v len Length
+ */
+void riscv_memmove ( void *dest, const void *src, size_t len ) {
+ void *orig_dest = dest;
+ unsigned long discard_data;
+
+ /* Do nothing if length is zero */
+ if ( ! len )
+ return;
+
+ /* Use memcpy() if copy direction is forwards */
+ if ( dest <= src ) {
+ memcpy ( dest, src, len );
+ return;
+ }
+
+ /* Assume memmove() is not performance-critical, and perform a
+ * bytewise copy backwards for simplicity.
+ */
+ dest += len;
+ src += len;
+ __asm__ __volatile__ ( "\n1:\n\t"
+ "addi %1, %1, -1\n\t"
+ "addi %0, %0, -1\n\t"
+ "lb %2, (%1)\n\t"
+ "sb %2, (%0)\n\t"
+ "bne %0, %3, 1b\n\t"
+ : "+r" ( dest ), "+r" ( src ),
+ "=&r" ( discard_data )
+ : "r" ( orig_dest )
+ : "memory" );
+}
diff --git a/src/arch/x86/interface/efi/efix86_nap.c b/src/arch/riscv/core/riscv_strings.S
index 296876b85..eb1b397b9 100644
--- a/src/arch/x86/interface/efi/efix86_nap.c
+++ b/src/arch/riscv/core/riscv_strings.S
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -21,37 +21,50 @@
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <ipxe/nap.h>
-#include <ipxe/efi/efi.h>
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
/** @file
*
- * iPXE CPU sleeping API for EFI
+ * Byte swapping
*
*/
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
/**
- * Sleep until next interrupt
+ * Find first (i.e. least significant) set bit
*
+ * @v value Value
+ * @ret lsb Least significant bit set in value (LSB=1), or zero
*/
-static void efix86_cpu_nap ( void ) {
- /*
- * I can't find any EFI API that allows us to put the CPU to
- * sleep. The CpuSleep() function is defined in CpuLib.h, but
- * isn't part of any exposed protocol so we have no way to
- * call it.
- *
- * The EFI shell doesn't seem to bother sleeping the CPU; it
- * just sits there idly burning power.
- *
- * If a shutdown is in progess, there may be nothing to
- * generate an interrupt since the timer is disabled in the
- * first step of ExitBootServices().
- */
- if ( ! efi_shutdown_in_progress )
- __asm__ __volatile__ ( "hlt" );
-}
+ .section ".text.riscv_ffs"
+ .globl riscv_ffs
+riscv_ffs:
+ beqz a0, 2f
+ mv t0, a0
+ li a0, ( __riscv_xlen + 1 )
+1: slli t0, t0, 1
+ addi a0, a0, -1
+ bnez t0, 1b
+2: ret
+ .size riscv_ffs, . - riscv_ffs
-PROVIDE_NAP ( efix86, cpu_nap, efix86_cpu_nap );
+/**
+ * Find last (i.e. most significant) set bit
+ *
+ * @v value Value
+ * @ret msb Most significant bit set in value (LSB=1), or zero
+ */
+ .section ".text.riscv_fls"
+ .globl riscv_fls
+riscv_fls:
+ beqz a0, 2f
+ mv t0, a0
+ li a0, __riscv_xlen
+ bltz t0, 2f
+1: slli t0, t0, 1
+ addi a0, a0, -1
+ bgez t0, 1b
+2: ret
+ .size riscv_fls, . - riscv_fls
diff --git a/src/arch/riscv/core/riscv_tcpip.S b/src/arch/riscv/core/riscv_tcpip.S
new file mode 100644
index 000000000..8c3e90ba6
--- /dev/null
+++ b/src/arch/riscv/core/riscv_tcpip.S
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * TCP/IP checksum
+ *
+ */
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+/**
+ * Calculate continued TCP/IP checkum
+ *
+ * @v partial Checksum of already-summed data, in network byte order
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret cksum Updated checksum, in network byte order
+ *
+ * In practice, this routine will only ever be called with a data
+ * pointer aligned to a 16-bit boundary. We optimise for this case,
+ * ensuring that the code would still give correct output if called
+ * with a misaligned pointer.
+ */
+ .section ".text.tcpip_continue_chksum", "ax", @progbits
+ .globl tcpip_continue_chksum
+tcpip_continue_chksum:
+
+ /* Set up register usage:
+ *
+ * a0: checksum low xlen bits
+ * a1: data pointer
+ * a2: end of data pointer
+ * a3: end of data pointer minus a constant offset of interest
+ * a4: checksum high bits (guaranteed to never carry) / constant 0xffff
+ * a5: temporary register
+ */
+ not a0, a0
+ add a2, a2, a1
+ addi a3, a2, -( __riscv_xlen / 8 )
+ mv a4, zero
+
+ /* Skip aligned checksumming if data is too short */
+ bgtu a1, a3, post_aligned
+
+ /* Checksum 16-bit words until we reach xlen-bit alignment (or
+ * one byte past xlen-bit alignment).
+ */
+ j 2f
+1: lhu a5, (a1)
+ addi a1, a1, 2
+ add a4, a4, a5
+2: andi a5, a1, ( ( ( __riscv_xlen / 8 ) - 1 ) & ~1 )
+ bnez a5, 1b
+
+ /* Checksum aligned xlen-bit words */
+ j 2f
+1: LOADN a5, (a1)
+ addi a1, a1, ( __riscv_xlen / 8 )
+ add a0, a0, a5
+ sltu a5, a0, a5
+ add a4, a4, a5
+2: bleu a1, a3, 1b
+
+post_aligned:
+ /* Checksum remaining 16-bit words */
+ addi a3, a2, -2
+ j 2f
+1: lhu a5, (a1)
+ addi a1, a1, 2
+ add a4, a4, a5
+2: bleu a1, a3, 1b
+
+ /* Checksum final byte if present */
+ beq a1, a2, 1f
+ lbu a5, (a1)
+ add a4, a4, a5
+1:
+ /* Fold down to xlen+1 bits */
+ add a0, a0, a4
+ sltu a4, a0, a4
+
+ /* Fold down to (xlen/2)+2 bits */
+ slli a5, a0, ( __riscv_xlen / 2 )
+ srli a0, a0, ( __riscv_xlen / 2 )
+ srli a5, a5, ( __riscv_xlen / 2 )
+ add a0, a0, a4
+ add a0, a0, a5
+
+ /* Load constant 0xffff for use in subsequent folding */
+ li a4, 0xffff
+
+#if __riscv_xlen >= 64
+ /* Fold down to (xlen/4)+3 bits (if xlen >= 64) */
+ and a5, a0, a4
+ srli a0, a0, ( __riscv_xlen / 4 )
+ add a0, a0, a5
+#endif
+
+ /* Fold down to 16+1 bits */
+ and a5, a0, a4
+ srli a0, a0, 16
+ add a0, a0, a5
+
+ /* Fold down to 16 bits */
+ srli a5, a0, 16
+ add a0, a0, a5
+ srli a5, a0, 17
+ add a0, a0, a5
+ and a0, a0, a4
+
+ /* Negate and return */
+ xor a0, a0, a4
+ ret
+ .size tcpip_continue_chksum, . - tcpip_continue_chksum
diff --git a/src/arch/riscv/core/setjmp.S b/src/arch/riscv/core/setjmp.S
new file mode 100644
index 000000000..69d844ac9
--- /dev/null
+++ b/src/arch/riscv/core/setjmp.S
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * Long jumps
+ *
+ */
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+ /* Must match jmp_buf structure layout */
+ .struct 0
+env_ra: .space ( __riscv_xlen / 8 )
+env_sp: .space ( __riscv_xlen / 8 )
+env_s0: .space ( __riscv_xlen / 8 )
+env_s1: .space ( __riscv_xlen / 8 )
+env_s2: .space ( __riscv_xlen / 8 )
+env_s3: .space ( __riscv_xlen / 8 )
+env_s4: .space ( __riscv_xlen / 8 )
+env_s5: .space ( __riscv_xlen / 8 )
+env_s6: .space ( __riscv_xlen / 8 )
+env_s7: .space ( __riscv_xlen / 8 )
+env_s8: .space ( __riscv_xlen / 8 )
+env_s9: .space ( __riscv_xlen / 8 )
+env_s10: .space ( __riscv_xlen / 8 )
+env_s11: .space ( __riscv_xlen / 8 )
+ .previous
+
+/*
+ * Save stack context for non-local goto
+ */
+ .section ".text.setjmp", "ax", @progbits
+ .globl setjmp
+setjmp:
+ /* Save registers */
+ STOREN ra, env_ra(a0)
+ STOREN sp, env_sp(a0)
+ STOREN s0, env_s0(a0)
+ STOREN s1, env_s1(a0)
+ STOREN s2, env_s2(a0)
+ STOREN s3, env_s3(a0)
+ STOREN s4, env_s4(a0)
+ STOREN s5, env_s5(a0)
+ STOREN s6, env_s6(a0)
+ STOREN s7, env_s7(a0)
+ STOREN s8, env_s8(a0)
+ STOREN s9, env_s9(a0)
+ STOREN s10, env_s10(a0)
+ STOREN s11, env_s11(a0)
+ /* Return zero when returning as setjmp() */
+ mv a0, zero
+ ret
+ .size setjmp, . - setjmp
+
+/*
+ * Non-local jump to a saved stack context
+ */
+ .section ".text.longjmp", "ax", @progbits
+ .globl longjmp
+longjmp:
+ /* Restore registers */
+ LOADN s11, env_s11(a0)
+ LOADN s10, env_s10(a0)
+ LOADN s9, env_s9(a0)
+ LOADN s8, env_s8(a0)
+ LOADN s7, env_s7(a0)
+ LOADN s6, env_s6(a0)
+ LOADN s5, env_s5(a0)
+ LOADN s4, env_s4(a0)
+ LOADN s3, env_s3(a0)
+ LOADN s2, env_s2(a0)
+ LOADN s1, env_s1(a0)
+ LOADN s0, env_s0(a0)
+ LOADN sp, env_sp(a0)
+ LOADN ra, env_ra(a0)
+ /* Force result to non-zero */
+ seqz a0, a1
+ or a0, a0, a1
+ /* Return to setjmp() caller */
+ ret
+ .size longjmp, . - longjmp
diff --git a/src/arch/riscv/core/stack.S b/src/arch/riscv/core/stack.S
new file mode 100644
index 000000000..1cd1da7c5
--- /dev/null
+++ b/src/arch/riscv/core/stack.S
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * Internal stack
+ *
+ */
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+#define STACK_ALIGN 16
+
+#define STACK_SIZE 8192
+
+ .section ".stack", "aw", @nobits
+ .balign STACK_ALIGN
+ .globl _stack
+_stack:
+ .space STACK_SIZE
+ .globl _estack
+_estack:
diff --git a/src/arch/riscv/core/svpage.c b/src/arch/riscv/core/svpage.c
new file mode 100644
index 000000000..e25e3920a
--- /dev/null
+++ b/src/arch/riscv/core/svpage.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <strings.h>
+#include <assert.h>
+#include <ipxe/hart.h>
+#include <ipxe/iomap.h>
+
+/** @file
+ *
+ * Supervisor page table management
+ *
+ * With the 64-bit paging schemes (Sv39, Sv48, and Sv57) we choose to
+ * identity-map as much as possible of the physical address space via
+ * PTEs 0-255, and place a recursive page table entry in PTE 511 which
+ * allows PTEs 256-510 to be used to map 1GB "gigapages" within the
+ * top 256GB of the 64-bit address space. At least one of these PTEs
+ * will already be in use to map iPXE itself. The remaining PTEs may
+ * be used to map I/O devices.
+ */
+
+/** A page table */
+struct page_table {
+ /** Page table entry */
+ uint64_t pte[512];
+};
+
+/** Page table entry flags */
+enum pte_flags {
+ /** Page table entry is valid */
+ PTE_V = 0x01,
+ /** Page is readable */
+ PTE_R = 0x02,
+ /** Page is writable */
+ PTE_W = 0x04,
+ /** Page has been accessed */
+ PTE_A = 0x40,
+ /** Page is dirty */
+ PTE_D = 0x80,
+ /** Page is the last page in an allocation
+ *
+ * This bit is ignored by the hardware. We use it to track
+ * the size of allocations made by ioremap().
+ */
+ PTE_LAST = 0x100,
+};
+
+/** Page-based memory type (Svpbmt) */
+#define PTE_SVPBMT( x ) ( ( ( unsigned long long ) (x) ) << 61 )
+
+/** Page is non-cacheable memory (Svpbmt) */
+#define PTE_SVPBMT_NC PTE_SVPBMT ( 1 )
+
+/** Page maps I/O addresses (Svpbmt) */
+#define PTE_SVPBMT_IO PTE_SVPBMT ( 2 )
+
+/** Page table entry address */
+#define PTE_PPN( addr ) ( (addr) >> 2 )
+
+/** The page table */
+extern struct page_table page_table;
+
+/** Maximum number of I/O pages */
+#define MAP_PAGE_COUNT \
+ ( sizeof ( page_table.pte ) / sizeof ( page_table.pte[0] ) )
+
+/** I/O page size
+ *
+ * We choose to use 1GB "gigapages", since these are supported by all
+ * paging levels.
+ */
+#define MAP_PAGE_SIZE 0x40000000UL
+
+/** I/O page base address
+ *
+ * The recursive page table entry maps the high 512GB of the 64-bit
+ * address space as 1GB "gigapages".
+ */
+#define MAP_BASE ( ( void * ) ( intptr_t ) ( -1ULL << 39 ) )
+
+/** Coherent DMA mapping of the 32-bit address space */
+static void *svpage_dma32_base;
+
+/** Size of the coherent DMA mapping */
+#define DMA32_LEN ( ( size_t ) 0x100000000ULL )
+
+/**
+ * Map pages
+ *
+ * @v phys Physical address
+ * @v len Length
+ * @v attrs Page attributes
+ * @ret virt Mapped virtual address, or NULL on error
+ */
+static void * svpage_map ( physaddr_t phys, size_t len, unsigned long attrs ) {
+ unsigned long satp;
+ unsigned long start;
+ unsigned int count;
+ unsigned int stride;
+ unsigned int first;
+ unsigned int i;
+ size_t offset;
+ void *virt;
+
+ DBGC ( &page_table, "SVPAGE mapping %#08lx+%#zx attrs %#016lx\n",
+ phys, len, attrs );
+
+ /* Sanity checks */
+ if ( ! len )
+ return NULL;
+ assert ( attrs & PTE_V );
+
+ /* Use physical address directly if paging is disabled */
+ __asm__ ( "csrr %0, satp" : "=r" ( satp ) );
+ if ( ! satp ) {
+ virt = phys_to_virt ( phys );
+ DBGC ( &page_table, "SVPAGE mapped %#08lx+%#zx to %p (no "
+ "paging)\n", phys, len, virt );
+ return virt;
+ }
+
+ /* Round down start address to a page boundary */
+ start = ( phys & ~( MAP_PAGE_SIZE - 1 ) );
+ offset = ( phys - start );
+ assert ( offset < MAP_PAGE_SIZE );
+
+ /* Calculate number of pages required */
+ count = ( ( offset + len + MAP_PAGE_SIZE - 1 ) / MAP_PAGE_SIZE );
+ assert ( count != 0 );
+ assert ( count <= MAP_PAGE_COUNT );
+
+ /* Round up number of pages to a power of two */
+ stride = ( 1 << fls ( count - 1 ) );
+ assert ( count <= stride );
+
+ /* Allocate pages */
+ for ( first = 0 ; first < MAP_PAGE_COUNT ; first += stride ) {
+
+ /* Calculate virtual address */
+ virt = ( MAP_BASE + ( first * MAP_PAGE_SIZE ) + offset );
+
+ /* Check that page table entries are available */
+ for ( i = first ; i < ( first + count ) ; i++ ) {
+ if ( page_table.pte[i] & PTE_V ) {
+ virt = NULL;
+ break;
+ }
+ }
+ if ( ! virt )
+ continue;
+
+ /* Create page table entries */
+ for ( i = first ; i < ( first + count ) ; i++ ) {
+ page_table.pte[i] = ( PTE_PPN ( start ) | attrs );
+ start += MAP_PAGE_SIZE;
+ }
+
+ /* Mark last page as being the last in this allocation */
+ page_table.pte[ i - 1 ] |= PTE_LAST;
+
+ /* Synchronise page table updates */
+ __asm__ __volatile__ ( "sfence.vma" );
+
+ /* Return virtual address */
+ DBGC ( &page_table, "SVPAGE mapped %#08lx+%#zx to %p using "
+ "PTEs [%d-%d]\n", phys, len, virt, first,
+ ( first + count - 1 ) );
+ return virt;
+ }
+
+ DBGC ( &page_table, "SVPAGE could not map %#08lx+%#zx\n",
+ phys, len );
+ return NULL;
+}
+
+/**
+ * Unmap pages
+ *
+ * @v virt Virtual address
+ */
+static void svpage_unmap ( const volatile void *virt ) {
+ unsigned long satp;
+ unsigned int first;
+ unsigned int i;
+ int is_last;
+
+ DBGC ( &page_table, "SVPAGE unmapping %p\n", virt );
+
+ /* Do nothing if paging is disabled */
+ __asm__ ( "csrr %0, satp" : "=r" ( satp ) );
+ if ( ! satp )
+ return;
+
+ /* Calculate first page table entry */
+ first = ( ( virt - MAP_BASE ) / MAP_PAGE_SIZE );
+
+ /* Ignore unmappings outside of the I/O range */
+ if ( first >= MAP_PAGE_COUNT )
+ return;
+
+ /* Clear page table entries */
+ for ( i = first ; ; i++ ) {
+
+ /* Sanity check */
+ assert ( page_table.pte[i] & PTE_V );
+
+ /* Check if this is the last page in this allocation */
+ is_last = ( page_table.pte[i] & PTE_LAST );
+
+ /* Clear page table entry */
+ page_table.pte[i] = 0;
+
+ /* Terminate if this was the last page */
+ if ( is_last )
+ break;
+ }
+
+ /* Synchronise page table updates */
+ __asm__ __volatile__ ( "sfence.vma" );
+
+ DBGC ( &page_table, "SVPAGE unmapped %p using PTEs [%d-%d]\n",
+ virt, first, i );
+}
+
+/**
+ * Map pages for I/O
+ *
+ * @v bus_addr Bus address
+ * @v len Length of region
+ * @ret io_addr I/O address
+ */
+static void * svpage_ioremap ( unsigned long bus_addr, size_t len ) {
+ unsigned long attrs = ( PTE_V | PTE_R | PTE_W | PTE_A | PTE_D );
+ int rc;
+
+ /* Add Svpbmt attributes if applicable */
+ if ( ( rc = hart_supported ( "_svpbmt" ) ) == 0 )
+ attrs |= PTE_SVPBMT_IO;
+
+ /* Map pages for I/O */
+ return svpage_map ( bus_addr, len, attrs );
+}
+
+/**
+ * Get 32-bit address space coherent DMA mapping address
+ *
+ * @ret base Coherent DMA mapping base address
+ */
+void * svpage_dma32 ( void ) {
+ unsigned long attrs = ( PTE_V | PTE_R | PTE_W | PTE_A | PTE_D );
+ int rc;
+
+ /* Add Svpbmt attributes if applicable */
+ if ( ( rc = hart_supported ( "_svpbmt" ) ) == 0 )
+ attrs |= PTE_SVPBMT_NC;
+
+ /* Create mapping, if necessary */
+ if ( ! svpage_dma32_base )
+ svpage_dma32_base = svpage_map ( 0, DMA32_LEN, attrs );
+
+ /* Sanity check */
+ assert ( virt_to_phys ( svpage_dma32_base ) == 0 );
+
+ return svpage_dma32_base;
+}
+
+PROVIDE_IOMAP_INLINE ( svpage, io_to_bus );
+PROVIDE_IOMAP ( svpage, ioremap, svpage_ioremap );
+PROVIDE_IOMAP ( svpage, iounmap, svpage_unmap );
diff --git a/src/arch/loong64/interface/efi/efiloong64_nap.c b/src/arch/riscv/core/xthead.c
index 0a7609783..c947c9dc8 100644
--- a/src/arch/loong64/interface/efi/efiloong64_nap.c
+++ b/src/arch/riscv/core/xthead.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, Xiaotian Wu <wuxiaotian@loongson.cn>
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -23,35 +23,43 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-#include <ipxe/nap.h>
-#include <ipxe/efi/efi.h>
-
/** @file
*
- * iPXE CPU sleeping API for EFI
+ * T-Head vendor extensions
*
*/
+#include <ipxe/sbi.h>
+#include <ipxe/xthead.h>
+
+/** Colour for debug messages */
+#define colour THEAD_MVENDORID
+
/**
- * Sleep until next interrupt
+ * Check for a T-Head feature via SXSTATUS register
*
+ * @v feature Feature bit
+ * @ret supported Feature is supported
*/
-static void efiloong64_cpu_nap ( void ) {
- /*
- * I can't find any EFI API that allows us to put the CPU to
- * sleep. The CpuSleep() function is defined in CpuLib.h, but
- * isn't part of any exposed protocol so we have no way to
- * call it.
- *
- * The EFI shell doesn't seem to bother sleeping the CPU; it
- * just sits there idly burning power.
- *
- * If a shutdown is in progess, there may be nothing to
- * generate an interrupt since the timer is disabled in the
- * first step of ExitBootServices().
- */
- if ( ! efi_shutdown_in_progress )
- __asm__ __volatile__ ( "idle 0" );
-}
+int xthead_supported ( unsigned long feature ) {
+ struct sbi_return ret;
+ unsigned long sxstatus;
-PROVIDE_NAP ( efiloong64, cpu_nap, efiloong64_cpu_nap );
+ /* Check for a T-Head CPU */
+ ret = sbi_ecall_0 ( SBI_BASE, SBI_BASE_MVENDORID );
+ if ( ret.error )
+ return 0;
+ if ( ret.value != THEAD_MVENDORID ) {
+ DBGC ( colour, "THEAD vendor ID mismatch: %#08lx\n",
+ ret.value );
+ return 0;
+ }
+
+ /* Read SXSTATUS CSR */
+ __asm__ ( "csrr %0, %1"
+ : "=r" ( sxstatus ) : "i" ( THEAD_SXSTATUS ) );
+ DBGC ( colour, "THEAD sxstatus %#08lx\n", sxstatus );
+
+ /* Check feature bit */
+ return ( !! ( sxstatus & feature ) );
+}
diff --git a/src/arch/riscv/core/zicbom.c b/src/arch/riscv/core/zicbom.c
new file mode 100644
index 000000000..28ff62c22
--- /dev/null
+++ b/src/arch/riscv/core/zicbom.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Cache-block management operations (Zicbom)
+ *
+ * We support explicit cache management operations on I/O buffers.
+ * These are guaranteed to be aligned on their own size and at least
+ * as large as a (reasonable) cacheline, and therefore cannot cross a
+ * cacheline boundary.
+ */
+
+#include <stdint.h>
+#include <ipxe/hart.h>
+#include <ipxe/xthead.h>
+#include <ipxe/zicbom.h>
+
+/** Minimum supported cacheline size
+ *
+ * We assume that cache management operations will ignore the least
+ * significant address bits, and so we are safe to assume a cacheline
+ * size that is smaller than the size actually used by the CPU.
+ *
+ * Cache clean and invalidate loops could be made faster by detecting
+ * the actual cacheline size.
+ */
+#define CACHE_STRIDE 32
+
+/** A cache management extension */
+struct cache_extension {
+ /**
+ * Clean data cache (i.e. write cached content back to memory)
+ *
+ * @v first First byte
+ * @v last Last byte
+ */
+ void ( * clean ) ( const void *first, const void *last );
+ /**
+ * Invalidate data cache (i.e. discard any cached content)
+ *
+ * @v first First byte
+ * @v last Last byte
+ */
+ void ( * invalidate ) ( void *first, void *last );
+};
+
+/** Define an operation to clean the data cache */
+#define CACHE_CLEAN( extension, insn ) \
+ static void extension ## _clean ( const void *first, \
+ const void *last ) { \
+ \
+ __asm__ __volatile__ ( ".option arch, +" #extension "\n\t" \
+ "\n1:\n\t" \
+ insn "\n\t" \
+ "addi %0, %0, %2\n\t" \
+ "bltu %0, %1, 1b\n\t" \
+ : "+r" ( first ) \
+ : "r" ( last ), "i" ( CACHE_STRIDE ) ); \
+ }
+
+/** Define an operation to invalidate the data cache */
+#define CACHE_INVALIDATE( extension, insn ) \
+ static void extension ## _invalidate ( void *first, \
+ void *last ) { \
+ \
+ __asm__ __volatile__ ( ".option arch, +" #extension "\n\t" \
+ "\n1:\n\t" \
+ insn "\n\t" \
+ "addi %0, %0, %2\n\t" \
+ "bltu %0, %1, 1b\n\t" \
+ : "+r" ( first ) \
+ : "r" ( last ), "i" ( CACHE_STRIDE ) \
+ : "memory" ); \
+ }
+
+/** Define a cache management extension */
+#define CACHE_EXTENSION( extension, clean_insn, invalidate_insn ) \
+ CACHE_CLEAN ( extension, clean_insn ); \
+ CACHE_INVALIDATE ( extension, invalidate_insn ); \
+ static struct cache_extension extension = { \
+ .clean = extension ## _clean, \
+ .invalidate = extension ## _invalidate, \
+ };
+
+/** The standard Zicbom extension */
+CACHE_EXTENSION ( zicbom, "cbo.clean (%0)", "cbo.inval (%0)" );
+
+/** The T-Head cache management extension */
+CACHE_EXTENSION ( xtheadcmo, "th.dcache.cva %0", "th.dcache.iva %0" );
+
+/**
+ * Clean data cache (with fully coherent memory)
+ *
+ * @v first First byte
+ * @v last Last byte
+ */
+static void cache_coherent_clean ( const void *first __unused,
+ const void *last __unused ) {
+ /* Nothing to do */
+}
+
+/**
+ * Invalidate data cache (with fully coherent memory)
+ *
+ * @v first First byte
+ * @v last Last byte
+ */
+static void cache_coherent_invalidate ( void *first __unused,
+ void *last __unused ) {
+ /* Nothing to do */
+}
+
+/** Dummy cache management extension for fully coherent memory */
+static struct cache_extension cache_coherent = {
+ .clean = cache_coherent_clean,
+ .invalidate = cache_coherent_invalidate,
+};
+
+static void cache_auto_detect ( void );
+static void cache_auto_clean ( const void *first, const void *last );
+static void cache_auto_invalidate ( void *first, void *last );
+
+/** The autodetect cache management extension */
+static struct cache_extension cache_auto = {
+ .clean = cache_auto_clean,
+ .invalidate = cache_auto_invalidate,
+};
+
+/** Active cache management extension */
+static struct cache_extension *cache_extension = &cache_auto;
+
+/**
+ * Clean data cache (i.e. write cached content back to memory)
+ *
+ * @v start Start address
+ * @v len Length
+ */
+void cache_clean ( const void *start, size_t len ) {
+ const void *first;
+ const void *last;
+
+ /* Do nothing for zero-length buffers */
+ if ( ! len )
+ return;
+
+ /* Construct address range */
+ first = ( ( const void * )
+ ( ( ( intptr_t ) start ) & ~( CACHE_STRIDE - 1 ) ) );
+ last = ( start + len - 1 );
+
+ /* Clean cache lines */
+ cache_extension->clean ( first, last );
+}
+
+/**
+ * Invalidate data cache (i.e. discard any cached content)
+ *
+ * @v start Start address
+ * @v len Length
+ */
+void cache_invalidate ( void *start, size_t len ) {
+ void *first;
+ void *last;
+
+ /* Do nothing for zero-length buffers */
+ if ( ! len )
+ return;
+
+ /* Construct address range */
+ first = ( ( void * )
+ ( ( ( intptr_t ) start ) & ~( CACHE_STRIDE - 1 ) ) );
+ last = ( start + len - 1 );
+
+ /* Invalidate cache lines */
+ cache_extension->invalidate ( first, last );
+}
+
+/**
+ * Autodetect and clean data cache
+ *
+ * @v first First byte
+ * @v last Last byte
+ */
+static void cache_auto_clean ( const void *first, const void *last ) {
+
+ /* Detect cache extension */
+ cache_auto_detect();
+
+ /* Clean data cache */
+ cache_extension->clean ( first, last );
+}
+
+/**
+ * Autodetect and invalidate data cache
+ *
+ * @v first First byte
+ * @v last Last byte
+ */
+static void cache_auto_invalidate ( void *first, void *last ) {
+
+ /* Detect cache extension */
+ cache_auto_detect();
+
+ /* Clean data cache */
+ cache_extension->invalidate ( first, last );
+}
+
+/**
+ * Autodetect cache
+ *
+ */
+static void cache_auto_detect ( void ) {
+ int rc;
+
+ /* Check for standard Zicbom extension */
+ if ( ( rc = hart_supported ( "_zicbom" ) ) == 0 ) {
+ DBGC ( &cache_extension, "CACHE detected Zicbom\n" );
+ cache_extension = &zicbom;
+ return;
+ }
+
+ /* Check for T-Head cache management extension */
+ if ( xthead_supported ( THEAD_SXSTATUS_THEADISAEE ) ) {
+ DBGC ( &cache_extension, "CACHE detected XTheadCmo\n" );
+ cache_extension = &xtheadcmo;
+ return;
+ }
+
+ /* Assume coherent memory if no supported extension detected */
+ DBGC ( &cache_extension, "CACHE assuming coherent memory\n" );
+ cache_extension = &cache_coherent;
+}
diff --git a/src/arch/riscv/core/zicntr.c b/src/arch/riscv/core/zicntr.c
new file mode 100644
index 000000000..fb632b8c0
--- /dev/null
+++ b/src/arch/riscv/core/zicntr.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Base counters and timers extension (Zicntr)
+ *
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <ipxe/fdt.h>
+#include <ipxe/csr.h>
+#include <ipxe/timer.h>
+
+/** Timer increment per microsecond */
+static unsigned long zicntr_mhz;
+
+/** Minimum resolution for scaled timer */
+#define ZICNTR_SCALED_HZ 32
+
+/**
+ * Timer scale (expressed as a bit shift)
+ *
+ * We use this to avoid the need for 64-bit divsion on 32-bit systems.
+ */
+static unsigned int zicntr_scale;
+
+/** Number of timer ticks per scaled timer increment */
+static unsigned long zicntr_ticks;
+
+/** Colour for debug messages */
+#define colour &zicntr_mhz
+
+/**
+ * Get low XLEN bits of current time
+ *
+ * @ret time Current time
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+rdtime_low ( void ) {
+ unsigned long time;
+
+ /* Read low XLEN bits of current time */
+ __asm__ __volatile__ ( "rdtime %0" : "=r" ( time ) );
+ return time;
+}
+
+/**
+ * Get current time, scaled to avoid rollover within a realistic timescale
+ *
+ * @ret time Scaled current time
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+rdtime_scaled ( void ) {
+ union {
+ uint64_t time;
+ struct {
+ uint32_t low;
+ uint32_t high;
+ };
+ } u;
+ unsigned long tmp __attribute__ (( unused ));
+
+ /* Read full current time */
+#if __riscv_xlen >= 64
+ __asm__ __volatile__ ( "rdtime %0" : "=r" ( u.time ) );
+#else
+ __asm__ __volatile__ ( "1:\n\t"
+ "rdtimeh %1\n\t"
+ "rdtime %0\n\t"
+ "rdtimeh %2\n\t"
+ "bne %1, %2, 1b\n\t"
+ : "=r" ( u.low ), "=r" ( u.high ),
+ "=r" ( tmp ) );
+#endif
+
+ /* Scale time to avoid XLEN-bit rollover */
+ return ( u.time >> zicntr_scale );
+}
+
+/**
+ * Get current system time in ticks
+ *
+ * @ret ticks Current time, in ticks
+ */
+static unsigned long zicntr_currticks ( void ) {
+ unsigned long scaled;
+
+ /* Get scaled time and convert to ticks */
+ scaled = rdtime_scaled();
+ return ( scaled * zicntr_ticks );
+}
+
+/**
+ * Delay for a fixed number of microseconds
+ *
+ * @v usecs Number of microseconds for which to delay
+ */
+static void zicntr_udelay ( unsigned long usecs ) {
+ unsigned long start;
+ unsigned long elapsed;
+ unsigned long threshold;
+
+ /* Delay until sufficient time has elapsed */
+ start = rdtime_low();
+ threshold = ( usecs * zicntr_mhz );
+ do {
+ elapsed = ( rdtime_low() - start );
+ } while ( elapsed < threshold );
+}
+
+/**
+ * Probe timer
+ *
+ * @ret rc Return status code
+ */
+static int zicntr_probe ( void ) {
+ unsigned int offset;
+ union {
+ uint64_t freq;
+ int64_t sfreq;
+ } u;
+ int rc;
+
+ /* Check if time CSR can be read */
+ if ( ! csr_can_read ( "time" ) ) {
+ DBGC ( colour, "ZICNTR cannot read TIME CSR\n" );
+ return -ENOTSUP;
+ }
+
+ /* Get timer frequency */
+ if ( ( ( rc = fdt_path ( &sysfdt, "/cpus", &offset ) ) != 0 ) ||
+ ( ( rc = fdt_u64 ( &sysfdt, offset, "timebase-frequency",
+ &u.freq ) ) != 0 ) ) {
+ DBGC ( colour, "ZICNTR could not determine frequency: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Convert to MHz (without 64-bit division) */
+ do {
+ zicntr_mhz++;
+ u.sfreq -= 1000000;
+ } while ( u.sfreq > 0 );
+
+ /* Calibrate currticks() scaling factor */
+ zicntr_scale = 31;
+ zicntr_ticks = ( ( 1UL << zicntr_scale ) /
+ ( zicntr_mhz * ( 1000000 / TICKS_PER_SEC ) ) );
+ while ( zicntr_ticks > ( TICKS_PER_SEC / ZICNTR_SCALED_HZ ) ) {
+ zicntr_scale--;
+ zicntr_ticks >>= 1;
+ }
+ DBGC ( colour, "ZICNTR at %ld MHz, %ld ticks per 2^%d increments\n",
+ zicntr_mhz, zicntr_ticks, zicntr_scale );
+ if ( ! zicntr_ticks ) {
+ DBGC ( colour, "ZICNTR has zero ticks per 2^%d increments\n",
+ zicntr_scale );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/** Zicntr timer */
+struct timer zicntr_timer __timer ( TIMER_PREFERRED ) = {
+ .name = "zicntr",
+ .probe = zicntr_probe,
+ .currticks = zicntr_currticks,
+ .udelay = zicntr_udelay,
+};
diff --git a/src/arch/riscv/core/zkr.c b/src/arch/riscv/core/zkr.c
new file mode 100644
index 000000000..b27a7f076
--- /dev/null
+++ b/src/arch/riscv/core/zkr.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Entropy source extension (Zkr)
+ *
+ */
+
+#include <errno.h>
+#include <ipxe/csr.h>
+#include <ipxe/entropy.h>
+#include <ipxe/drbg.h>
+
+struct entropy_source zkr_entropy __entropy_source ( ENTROPY_PREFERRED );
+
+/** Seed CSR operational state */
+#define ZKR_SEED_OPST_MASK 0xc0000000UL
+#define ZKR_SEED_OPST_ES16 0x80000000UL /**< 16 bits of entropy available */
+
+/** Number of times to retry reading from seed CSR */
+#define ZKR_SEED_MAX_RETRY 1024
+
+/** Colour for debug messages */
+#define colour &zkr_entropy
+
+/**
+ * Enable entropy gathering
+ *
+ * @ret rc Return status code
+ */
+static int zkr_entropy_enable ( void ) {
+
+ /* Check if seed CSR is accessible in S-mode */
+ if ( ! csr_can_write ( "seed", 0 ) ) {
+ DBGC ( colour, "ZKR cannot access seed CSR\n" );
+ return -ENOTSUP;
+ }
+
+ /* RISC-V ISA mandates that 128 bits of full entropy shall be
+ * obtained from 256 entropy bits read from the seed CSR.
+ *
+ * Each 16-bit sample therefore contains 8 bits of
+ * min-entropy.
+ */
+ entropy_init ( &zkr_entropy, MIN_ENTROPY ( 8.0 ) );
+
+ return 0;
+}
+
+/**
+ * Get noise sample
+ *
+ * @ret noise Noise sample
+ * @ret rc Return status code
+ */
+static int zkr_get_noise ( noise_sample_t *noise ) {
+ unsigned long seed;
+ unsigned int i;
+
+ /* Read entropy from seed CSR */
+ for ( i = 0 ; i < ZKR_SEED_MAX_RETRY ; i++ ) {
+
+ /* Read seed CSR */
+ __asm__ __volatile__ ( "csrrw %0, seed, zero" :
+ "=r" ( seed ) );
+
+ /* Check operationsl state */
+ if ( ( seed & ZKR_SEED_OPST_MASK ) == ZKR_SEED_OPST_ES16 ) {
+
+ /* Return entropy from both halves of the
+ * 16-bit entropy source value.
+ */
+ *noise = ( seed ^ ( seed >> 8 ) );
+ return 0;
+ }
+ }
+
+ DBGC ( colour, "ZKR could not source entropy (seed %#08lx)\n", seed );
+ return -EBUSY;
+}
+
+/** Hardware entropy source */
+struct entropy_source zkr_entropy __entropy_source ( ENTROPY_PREFERRED ) = {
+ .name = "zkr",
+ .enable = zkr_entropy_enable,
+ .get_noise = zkr_get_noise,
+};
diff --git a/src/arch/riscv/include/bits/bigint.h b/src/arch/riscv/include/bits/bigint.h
new file mode 100644
index 000000000..7f87d9748
--- /dev/null
+++ b/src/arch/riscv/include/bits/bigint.h
@@ -0,0 +1,381 @@
+#ifndef _BITS_BIGINT_H
+#define _BITS_BIGINT_H
+
+/** @file
+ *
+ * Big integer support
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+
+/** Element of a big integer */
+typedef unsigned long bigint_element_t;
+
+/**
+ * Initialise big integer
+ *
+ * @v value0 Element 0 of big integer to initialise
+ * @v size Number of elements
+ * @v data Raw data
+ * @v len Length of raw data
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_init_raw ( unsigned long *value0, unsigned int size,
+ const void *data, size_t len ) {
+ size_t pad_len = ( sizeof ( bigint_t ( size ) ) - len );
+ uint8_t *value_byte = ( ( void * ) value0 );
+ const uint8_t *data_byte = ( data + len );
+
+ /* Copy raw data in reverse order, padding with zeros */
+ while ( len-- )
+ *(value_byte++) = *(--data_byte);
+ while ( pad_len-- )
+ *(value_byte++) = 0;
+}
+
+/**
+ * Add big integers
+ *
+ * @v addend0 Element 0 of big integer to add
+ * @v value0 Element 0 of big integer to be added to
+ * @v size Number of elements
+ * @ret carry Carry out
+ */
+static inline __attribute__ (( always_inline )) int
+bigint_add_raw ( const unsigned long *addend0, unsigned long *value0,
+ unsigned int size ) {
+ bigint_t ( size ) __attribute__ (( may_alias )) *value =
+ ( ( void * ) value0 );
+ unsigned long *valueN = ( value0 + size );
+ unsigned long *discard_addend;
+ unsigned long *discard_value;
+ unsigned long discard_addend_i;
+ unsigned long discard_value_i;
+ unsigned long discard_temp;
+ unsigned long carry;
+
+ __asm__ __volatile__ ( "\n1:\n\t"
+ /* Load addend[i] and value[i] */
+ LOADN " %2, (%0)\n\t"
+ LOADN " %3, (%1)\n\t"
+ /* Add carry flag and addend */
+ "add %3, %3, %5\n\t"
+ "sltu %4, %3, %5\n\t"
+ "add %3, %3, %2\n\t"
+ "sltu %5, %3, %2\n\t"
+ "or %5, %4, %5\n\t"
+ /* Store value[i] */
+ STOREN " %3, (%1)\n\t"
+ /* Loop */
+ "addi %0, %0, %8\n\t"
+ "addi %1, %1, %8\n\t"
+ "bne %1, %7, 1b\n\t"
+ : "=&r" ( discard_addend ),
+ "=&r" ( discard_value ),
+ "=&r" ( discard_addend_i ),
+ "=&r" ( discard_value_i ),
+ "=&r" ( discard_temp ),
+ "=&r" ( carry ),
+ "+m" ( *value )
+ : "r" ( valueN ),
+ "i" ( sizeof ( unsigned long ) ),
+ "0" ( addend0 ), "1" ( value0 ), "5" ( 0 ) );
+ return carry;
+}
+
+/**
+ * Subtract big integers
+ *
+ * @v subtrahend0 Element 0 of big integer to subtract
+ * @v value0 Element 0 of big integer to be subtracted from
+ * @v size Number of elements
+ * @ret borrow Borrow out
+ */
+static inline __attribute__ (( always_inline )) int
+bigint_subtract_raw ( const unsigned long *subtrahend0, unsigned long *value0,
+ unsigned int size ) {
+ bigint_t ( size ) __attribute__ (( may_alias )) *value =
+ ( ( void * ) value0 );
+ unsigned long *valueN = ( value0 + size );
+ unsigned long *discard_subtrahend;
+ unsigned long *discard_value;
+ unsigned long discard_subtrahend_i;
+ unsigned long discard_value_i;
+ unsigned long discard_temp;
+ unsigned long borrow;
+
+ __asm__ __volatile__ ( "\n1:\n\t"
+ /* Load subtrahend[i] and value[i] */
+ LOADN " %2, (%0)\n\t"
+ LOADN " %3, (%1)\n\t"
+ /* Subtract carry flag and subtrahend */
+ "sltu %4, %3, %5\n\t"
+ "sub %3, %3, %5\n\t"
+ "sltu %5, %3, %2\n\t"
+ "sub %3, %3, %2\n\t"
+ "or %5, %5, %4\n\t"
+ /* Store value[i] */
+ STOREN " %3, (%1)\n\t"
+ /* Loop */
+ "addi %0, %0, %8\n\t"
+ "addi %1, %1, %8\n\t"
+ "bne %1, %7, 1b\n\t"
+ : "=&r" ( discard_subtrahend ),
+ "=&r" ( discard_value ),
+ "=&r" ( discard_subtrahend_i ),
+ "=&r" ( discard_value_i ),
+ "=&r" ( discard_temp ),
+ "=&r" ( borrow ),
+ "+m" ( *value )
+ : "r" ( valueN ),
+ "i" ( sizeof ( unsigned long ) ),
+ "0" ( subtrahend0 ), "1" ( value0 ),
+ "5" ( 0 ) );
+ return borrow;
+}
+
+/**
+ * Shift big integer left
+ *
+ * @v value0 Element 0 of big integer
+ * @v size Number of elements
+ * @ret out Bit shifted out
+ */
+static inline __attribute__ (( always_inline )) int
+bigint_shl_raw ( unsigned long *value0, unsigned int size ) {
+ bigint_t ( size ) __attribute__ (( may_alias )) *value =
+ ( ( void * ) value0 );
+ unsigned long *valueN = ( value0 + size );
+ unsigned long *discard_value;
+ unsigned long discard_value_i;
+ unsigned long discard_temp;
+ unsigned long carry;
+
+ __asm__ __volatile__ ( "\n1:\n\t"
+ /* Load value[i] */
+ LOADN " %1, (%0)\n\t"
+ /* Shift left */
+ "slli %2, %1, 1\n\t"
+ "or %2, %2, %3\n\t"
+ "srli %3, %1, %7\n\t"
+ /* Store value[i] */
+ STOREN " %2, (%0)\n\t"
+ /* Loop */
+ "addi %0, %0, %6\n\t"
+ "bne %0, %5, 1b\n\t"
+ : "=&r" ( discard_value ),
+ "=&r" ( discard_value_i ),
+ "=&r" ( discard_temp ),
+ "=&r" ( carry ),
+ "+m" ( *value )
+ : "r" ( valueN ),
+ "i" ( sizeof ( unsigned long ) ),
+ "i" ( ( 8 * sizeof ( unsigned long ) - 1 ) ),
+ "0" ( value0 ), "3" ( 0 ) );
+ return carry;
+}
+
+/**
+ * Shift big integer right
+ *
+ * @v value0 Element 0 of big integer
+ * @v size Number of elements
+ * @ret out Bit shifted out
+ */
+static inline __attribute__ (( always_inline )) int
+bigint_shr_raw ( unsigned long *value0, unsigned int size ) {
+ bigint_t ( size ) __attribute__ (( may_alias )) *value =
+ ( ( void * ) value0 );
+ unsigned long *valueN = ( value0 + size );
+ unsigned long *discard_value;
+ unsigned long discard_value_i;
+ unsigned long discard_temp;
+ unsigned long carry;
+
+ __asm__ __volatile__ ( "\n1:\n\t"
+ /* Load value[i] */
+ LOADN " %1, %6(%0)\n\t"
+ /* Shift right */
+ "srli %2, %1, 1\n\t"
+ "or %2, %2, %3\n\t"
+ "slli %3, %1, %7\n\t"
+ /* Store value[i] */
+ STOREN " %2, %6(%0)\n\t"
+ /* Loop */
+ "addi %0, %0, %6\n\t"
+ "bne %0, %5, 1b\n\t"
+ : "=&r" ( discard_value ),
+ "=&r" ( discard_value_i ),
+ "=&r" ( discard_temp ),
+ "=&r" ( carry ),
+ "+m" ( *value )
+ : "r" ( value0 ),
+ "i" ( -( sizeof ( unsigned long ) ) ),
+ "i" ( ( 8 * sizeof ( unsigned long ) - 1 ) ),
+ "0" ( valueN ), "3" ( 0 ) );
+ return ( !! carry );
+}
+
+/**
+ * Test if big integer is equal to zero
+ *
+ * @v value0 Element 0 of big integer
+ * @v size Number of elements
+ * @ret is_zero Big integer is equal to zero
+ */
+static inline __attribute__ (( always_inline, pure )) int
+bigint_is_zero_raw ( const unsigned long *value0, unsigned int size ) {
+ const unsigned long *value = value0;
+ unsigned long value_i;
+
+ do {
+ value_i = *(value++);
+ if ( value_i )
+ break;
+ } while ( --size );
+
+ return ( value_i == 0 );
+}
+
+/**
+ * Compare big integers
+ *
+ * @v value0 Element 0 of big integer
+ * @v reference0 Element 0 of reference big integer
+ * @v size Number of elements
+ * @ret geq Big integer is greater than or equal to the reference
+ */
+static inline __attribute__ (( always_inline, pure )) int
+bigint_is_geq_raw ( const unsigned long *value0,
+ const unsigned long *reference0, unsigned int size ) {
+ const unsigned long *value = ( value0 + size );
+ const unsigned long *reference = ( reference0 + size );
+ unsigned long value_i;
+ unsigned long reference_i;
+
+ do {
+ value_i = *(--value);
+ reference_i = *(--reference);
+ if ( value_i != reference_i )
+ break;
+ } while ( --size );
+
+ return ( value_i >= reference_i );
+}
+
+/**
+ * Find highest bit set in big integer
+ *
+ * @v value0 Element 0 of big integer
+ * @v size Number of elements
+ * @ret max_bit Highest bit set + 1 (or 0 if no bits set)
+ */
+static inline __attribute__ (( always_inline )) int
+bigint_max_set_bit_raw ( const unsigned long *value0, unsigned int size ) {
+ const unsigned long *value = ( value0 + size );
+ int max_bit = ( 8 * sizeof ( bigint_t ( size ) ) );
+ unsigned long value_i;
+
+ do {
+ value_i = *(--value);
+ max_bit -= ( ( 8 * sizeof ( *value0 ) ) - fls ( value_i ) );
+ if ( value_i )
+ break;
+ } while ( --size );
+
+ return max_bit;
+}
+
+/**
+ * Grow big integer
+ *
+ * @v source0 Element 0 of source big integer
+ * @v source_size Number of elements in source big integer
+ * @v dest0 Element 0 of destination big integer
+ * @v dest_size Number of elements in destination big integer
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_grow_raw ( const unsigned long *source0, unsigned int source_size,
+ unsigned long *dest0, unsigned int dest_size ) {
+ unsigned int pad_size = ( dest_size - source_size );
+
+ memcpy ( dest0, source0, sizeof ( bigint_t ( source_size ) ) );
+ memset ( ( dest0 + source_size ), 0, sizeof ( bigint_t ( pad_size ) ) );
+}
+
+/**
+ * Shrink big integer
+ *
+ * @v source0 Element 0 of source big integer
+ * @v source_size Number of elements in source big integer
+ * @v dest0 Element 0 of destination big integer
+ * @v dest_size Number of elements in destination big integer
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_shrink_raw ( const unsigned long *source0,
+ unsigned int source_size __unused,
+ unsigned long *dest0, unsigned int dest_size ) {
+
+ memcpy ( dest0, source0, sizeof ( bigint_t ( dest_size ) ) );
+}
+
+/**
+ * Finalise big integer
+ *
+ * @v value0 Element 0 of big integer to finalise
+ * @v size Number of elements
+ * @v out Output buffer
+ * @v len Length of output buffer
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_done_raw ( const unsigned long *value0, unsigned int size __unused,
+ void *out, size_t len ) {
+ const uint8_t *value_byte = ( ( const void * ) value0 );
+ uint8_t *out_byte = ( out + len );
+
+ /* Copy raw data in reverse order */
+ while ( len-- )
+ *(--out_byte) = *(value_byte++);
+}
+
+/**
+ * Multiply big integer elements
+ *
+ * @v multiplicand Multiplicand element
+ * @v multiplier Multiplier element
+ * @v result Result element
+ * @v carry Carry element
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_multiply_one ( const unsigned long multiplicand,
+ const unsigned long multiplier,
+ unsigned long *result, unsigned long *carry ) {
+ unsigned long discard_low;
+ unsigned long discard_high;
+ unsigned long discard_carry;
+
+ __asm__ __volatile__ ( /* Perform multiplication */
+ "mulhu %1, %5, %6\n\t"
+ "mul %0, %5, %6\n\t"
+ /* Accumulate low half */
+ "add %3, %3, %0\n\t"
+ "sltu %2, %3, %0\n\t"
+ "add %1, %1, %2\n\t"
+ /* Accumulate carry (cannot overflow) */
+ "add %3, %3, %4\n\t"
+ "sltu %2, %3, %4\n\t"
+ "add %4, %1, %2\n\t"
+ : "=r" ( discard_low ),
+ "=&r" ( discard_high ),
+ "=r" ( discard_carry ),
+ "+r" ( *result ),
+ "+r" ( *carry )
+ : "r" ( multiplicand ),
+ "r" ( multiplier ) );
+}
+
+#endif /* _BITS_BIGINT_H */
diff --git a/src/arch/riscv/include/bits/bitops.h b/src/arch/riscv/include/bits/bitops.h
new file mode 100644
index 000000000..2019db99a
--- /dev/null
+++ b/src/arch/riscv/include/bits/bitops.h
@@ -0,0 +1,82 @@
+#ifndef _BITS_BITOPS_H
+#define _BITS_BITOPS_H
+
+/** @file
+ *
+ * RISC-V bit operations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+/**
+ * Test and set bit atomically
+ *
+ * @v bit Bit to set
+ * @v bits Bit field
+ * @ret old Old value of bit (zero or non-zero)
+ */
+static inline __attribute__ (( always_inline )) int
+test_and_set_bit ( unsigned int bit, volatile void *bits ) {
+ unsigned int index = ( bit / 32 );
+ unsigned int offset = ( bit % 32 );
+ volatile uint32_t *word = ( ( ( volatile uint32_t * ) bits ) + index );
+ uint32_t mask = ( 1U << offset );
+ uint32_t old;
+
+ __asm__ __volatile__ ( "amoor.w %0, %2, %1"
+ : "=r" ( old ), "+A" ( *word )
+ : "r" ( mask ) );
+
+ return ( !! ( old & mask ) );
+}
+
+/**
+ * Test and clear bit atomically
+ *
+ * @v bit Bit to set
+ * @v bits Bit field
+ * @ret old Old value of bit (zero or non-zero)
+ */
+static inline __attribute__ (( always_inline )) int
+test_and_clear_bit ( unsigned int bit, volatile void *bits ) {
+ unsigned int index = ( bit / 32 );
+ unsigned int offset = ( bit % 32 );
+ volatile uint32_t *word = ( ( ( volatile uint32_t * ) bits ) + index );
+ uint32_t mask = ( 1U << offset );
+ uint32_t old;
+
+ __asm__ __volatile__ ( "amoand.w %0, %2, %1"
+ : "=r" ( old ), "+A" ( *word )
+ : "r" ( ~mask ) );
+
+ return ( !! ( old & mask ) );
+}
+
+/**
+ * Set bit atomically
+ *
+ * @v bit Bit to set
+ * @v bits Bit field
+ */
+static inline __attribute__ (( always_inline )) void
+set_bit ( unsigned int bit, volatile void *bits ) {
+
+ test_and_set_bit ( bit, bits );
+}
+
+/**
+ * Clear bit atomically
+ *
+ * @v bit Bit to set
+ * @v bits Bit field
+ */
+static inline __attribute__ (( always_inline )) void
+clear_bit ( unsigned int bit, volatile void *bits ) {
+
+ test_and_clear_bit ( bit, bits );
+}
+
+#endif /* _BITS_BITOPS_H */
diff --git a/src/arch/riscv/include/bits/byteswap.h b/src/arch/riscv/include/bits/byteswap.h
new file mode 100644
index 000000000..56d03f64e
--- /dev/null
+++ b/src/arch/riscv/include/bits/byteswap.h
@@ -0,0 +1,48 @@
+#ifndef _BITS_BYTESWAP_H
+#define _BITS_BYTESWAP_H
+
+/** @file
+ *
+ * Byte-order swapping functions
+ *
+ */
+
+#include <stdint.h>
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern __asmcall uint64_t riscv_swap_word ( uint64_t x );
+extern __asmcall unsigned long riscv_swap_half ( unsigned long x );
+extern __asmcall unsigned long riscv_swap_byte ( unsigned long x );
+
+static inline __attribute__ (( always_inline, const )) uint16_t
+__bswap_variable_16 ( uint16_t x ) {
+ return riscv_swap_byte ( x );
+}
+
+static inline __attribute__ (( always_inline )) void
+__bswap_16s ( uint16_t *x ) {
+ *x = riscv_swap_byte ( *x );
+}
+
+static inline __attribute__ (( always_inline, const )) uint32_t
+__bswap_variable_32 ( uint32_t x ) {
+ return riscv_swap_half ( x );
+}
+
+static inline __attribute__ (( always_inline )) void
+__bswap_32s ( uint32_t *x ) {
+ *x = riscv_swap_half ( *x );
+}
+
+static inline __attribute__ (( always_inline, const )) uint64_t
+__bswap_variable_64 ( uint64_t x ) {
+ return riscv_swap_word ( x );
+}
+
+static inline __attribute__ (( always_inline )) void
+__bswap_64s ( uint64_t *x ) {
+ *x = riscv_swap_word ( *x );
+}
+
+#endif /* _BITS_BYTESWAP_H */
diff --git a/src/arch/riscv/include/bits/compiler.h b/src/arch/riscv/include/bits/compiler.h
new file mode 100644
index 000000000..624a16108
--- /dev/null
+++ b/src/arch/riscv/include/bits/compiler.h
@@ -0,0 +1,40 @@
+#ifndef _BITS_COMPILER_H
+#define _BITS_COMPILER_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** Dummy relocation type */
+#define RELOC_TYPE_NONE R_RISCV_NONE
+
+/* Determine load/store instructions for natural bit width */
+#if __riscv_xlen == 128
+#define NATURAL_SUFFIX q
+#elif __riscv_xlen == 64
+#define NATURAL_SUFFIX d
+#elif __riscv_xlen == 32
+#define NATURAL_SUFFIX w
+#else
+#error "Unsupported bit width"
+#endif
+#ifdef ASSEMBLY
+#define LOADN _C2 ( L, NATURAL_SUFFIX )
+#define STOREN _C2 ( S, NATURAL_SUFFIX )
+#else
+#define LOADN "L" _S2 ( NATURAL_SUFFIX )
+#define STOREN "S" _S2 ( NATURAL_SUFFIX )
+#endif
+
+#ifndef ASSEMBLY
+
+/** Unprefixed constant operand modifier */
+#define ASM_NO_PREFIX ""
+
+/** Declare a function with standard calling conventions */
+#define __asmcall
+
+/** Declare a function with libgcc implicit linkage */
+#define __libgcc
+
+#endif /* ASSEMBLY */
+
+#endif /* _BITS_COMPILER_H */
diff --git a/src/arch/riscv/include/bits/dma.h b/src/arch/riscv/include/bits/dma.h
new file mode 100644
index 000000000..f7decd14c
--- /dev/null
+++ b/src/arch/riscv/include/bits/dma.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_DMA_H
+#define _BITS_DMA_H
+
+/** @file
+ *
+ * RISCV-specific DMA API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/riscv_dma.h>
+
+#endif /* _BITS_DMA_H */
diff --git a/src/arch/riscv/include/bits/endian.h b/src/arch/riscv/include/bits/endian.h
new file mode 100644
index 000000000..85718cfdd
--- /dev/null
+++ b/src/arch/riscv/include/bits/endian.h
@@ -0,0 +1,8 @@
+#ifndef _BITS_ENDIAN_H
+#define _BITS_ENDIAN_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#endif /* _BITS_ENDIAN_H */
diff --git a/src/arch/riscv/include/bits/errfile.h b/src/arch/riscv/include/bits/errfile.h
new file mode 100644
index 000000000..bdd2927a4
--- /dev/null
+++ b/src/arch/riscv/include/bits/errfile.h
@@ -0,0 +1,24 @@
+#ifndef _BITS_ERRFILE_H
+#define _BITS_ERRFILE_H
+
+/** @file
+ *
+ * RISC-V error file identifiers
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @addtogroup errfile Error file identifiers
+ * @{
+ */
+
+#define ERRFILE_sbi_reboot ( ERRFILE_ARCH | ERRFILE_CORE | 0x00000000 )
+#define ERRFILE_hart ( ERRFILE_ARCH | ERRFILE_CORE | 0x00010000 )
+#define ERRFILE_zicntr ( ERRFILE_ARCH | ERRFILE_CORE | 0x00020000 )
+#define ERRFILE_zkr ( ERRFILE_ARCH | ERRFILE_CORE | 0x00030000 )
+
+/** @} */
+
+#endif /* _BITS_ERRFILE_H */
diff --git a/src/arch/riscv/include/bits/io.h b/src/arch/riscv/include/bits/io.h
new file mode 100644
index 000000000..4296e318a
--- /dev/null
+++ b/src/arch/riscv/include/bits/io.h
@@ -0,0 +1,17 @@
+#ifndef _BITS_IO_H
+#define _BITS_IO_H
+
+/** @file
+ *
+ * RISCV-specific I/O API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** Page shift */
+#define PAGE_SHIFT 12
+
+#include <ipxe/riscv_io.h>
+
+#endif /* _BITS_IO_H */
diff --git a/src/arch/loong64/include/bits/iomap.h b/src/arch/riscv/include/bits/iomap.h
index 041171d22..fd8e37825 100644
--- a/src/arch/loong64/include/bits/iomap.h
+++ b/src/arch/riscv/include/bits/iomap.h
@@ -3,10 +3,12 @@
/** @file
*
- * LoongArch64-specific I/O mapping API implementations
+ * RISCV-specific I/O mapping API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+#include <ipxe/svpage.h>
+
#endif /* _BITS_IOMAP_H */
diff --git a/src/arch/riscv/include/bits/lkrn.h b/src/arch/riscv/include/bits/lkrn.h
new file mode 100644
index 000000000..d26108647
--- /dev/null
+++ b/src/arch/riscv/include/bits/lkrn.h
@@ -0,0 +1,34 @@
+#ifndef _BITS_LKRN_H
+#define _BITS_LKRN_H
+
+/** @file
+ *
+ * Linux kernel image invocation
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/hart.h>
+
+/** Header magic value */
+#define LKRN_MAGIC_ARCH LKRN_MAGIC_RISCV
+
+/**
+ * Jump to kernel entry point
+ *
+ * @v entry Kernel entry point
+ * @v fdt Device tree
+ */
+static inline __attribute__ (( noreturn )) void
+lkrn_jump ( physaddr_t entry, physaddr_t fdt ) {
+ register unsigned long a0 asm ( "a0" ) = boot_hart;
+ register unsigned long a1 asm ( "a1" ) = fdt;
+
+ __asm__ __volatile__ ( "call disable_paging\n\t"
+ "jr %2\n\t"
+ : : "r" ( a0 ), "r" ( a1 ), "r" ( entry ) );
+ __builtin_unreachable();
+}
+
+#endif /* _BITS_LKRN_H */
diff --git a/src/arch/riscv/include/bits/nap.h b/src/arch/riscv/include/bits/nap.h
new file mode 100644
index 000000000..331399f46
--- /dev/null
+++ b/src/arch/riscv/include/bits/nap.h
@@ -0,0 +1,20 @@
+#ifndef _BITS_NAP_H
+#define _BITS_NAP_H
+
+/** @file
+ *
+ * RISCV-specific CPU sleeping API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * Sleep until next CPU interrupt
+ *
+ */
+static inline __attribute__ (( always_inline )) void cpu_halt ( void ) {
+ __asm__ __volatile__ ( "wfi" );
+}
+
+#endif /* _BITS_NAP_H */
diff --git a/src/arch/riscv/include/bits/profile.h b/src/arch/riscv/include/bits/profile.h
new file mode 100644
index 000000000..e9e003dab
--- /dev/null
+++ b/src/arch/riscv/include/bits/profile.h
@@ -0,0 +1,28 @@
+#ifndef _BITS_PROFILE_H
+#define _BITS_PROFILE_H
+
+/** @file
+ *
+ * Profiling
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+/**
+ * Get profiling timestamp
+ *
+ * @ret timestamp Timestamp
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+profile_timestamp ( void ) {
+ unsigned long cycles;
+
+ /* Read timestamp counter */
+ __asm__ __volatile__ ( "rdcycle %0" : "=r" ( cycles ) );
+ return cycles;
+}
+
+#endif /* _BITS_PROFILE_H */
diff --git a/src/arch/arm/include/bits/reboot.h b/src/arch/riscv/include/bits/reboot.h
index 88c50250c..01272483b 100644
--- a/src/arch/arm/include/bits/reboot.h
+++ b/src/arch/riscv/include/bits/reboot.h
@@ -3,10 +3,12 @@
/** @file
*
- * ARM-specific reboot API implementations
+ * RISCV-specific reboot API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+#include <ipxe/sbi_reboot.h>
+
#endif /* _BITS_REBOOT_H */
diff --git a/src/arch/riscv/include/bits/setjmp.h b/src/arch/riscv/include/bits/setjmp.h
new file mode 100644
index 000000000..5186fadaf
--- /dev/null
+++ b/src/arch/riscv/include/bits/setjmp.h
@@ -0,0 +1,16 @@
+#ifndef _BITS_SETJMP_H
+#define _BITS_SETJMP_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** A jump buffer */
+typedef struct {
+ /** Return address (ra) */
+ unsigned long ra;
+ /** Stack pointer (sp) */
+ unsigned long sp;
+ /** Callee-saved registers (s0-s11) */
+ unsigned long s[12];
+} jmp_buf[1];
+
+#endif /* _BITS_SETJMP_H */
diff --git a/src/arch/riscv/include/bits/stdint.h b/src/arch/riscv/include/bits/stdint.h
new file mode 100644
index 000000000..fe1f9946a
--- /dev/null
+++ b/src/arch/riscv/include/bits/stdint.h
@@ -0,0 +1,23 @@
+#ifndef _BITS_STDINT_H
+#define _BITS_STDINT_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+typedef __SIZE_TYPE__ size_t;
+typedef signed long ssize_t;
+typedef signed long off_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long long uint64_t;
+
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed int int32_t;
+typedef signed long long int64_t;
+
+typedef unsigned long physaddr_t;
+typedef unsigned long intptr_t;
+
+#endif /* _BITS_STDINT_H */
diff --git a/src/arch/riscv/include/bits/string.h b/src/arch/riscv/include/bits/string.h
new file mode 100644
index 000000000..87834d91a
--- /dev/null
+++ b/src/arch/riscv/include/bits/string.h
@@ -0,0 +1,82 @@
+#ifndef _BITS_STRING_H
+#define _BITS_STRING_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * String functions
+ *
+ */
+
+extern void riscv_bzero ( void *dest, size_t len );
+extern void riscv_memset ( void *dest, size_t len, int character );
+extern void riscv_memcpy ( void *dest, const void *src, size_t len );
+extern void riscv_memmove ( void *dest, const void *src, size_t len );
+
+/**
+ * Fill memory region
+ *
+ * @v dest Destination region
+ * @v character Fill character
+ * @v len Length
+ * @ret dest Destination region
+ */
+static inline __attribute__ (( always_inline )) void *
+memset ( void *dest, int character, size_t len ) {
+
+ /* For zeroing larger or non-constant lengths, use the
+ * optimised variable-length zeroing code.
+ */
+ if ( __builtin_constant_p ( character ) && ( character == 0 ) ) {
+ riscv_bzero ( dest, len );
+ return dest;
+ }
+
+ /* Not necessarily zeroing: use basic variable-length code */
+ riscv_memset ( dest, len, character );
+ return dest;
+}
+
+/**
+ * Copy memory region
+ *
+ * @v dest Destination region
+ * @v src Source region
+ * @v len Length
+ * @ret dest Destination region
+ */
+static inline __attribute__ (( always_inline )) void *
+memcpy ( void *dest, const void *src, size_t len ) {
+
+ /* Otherwise, use variable-length code */
+ riscv_memcpy ( dest, src, len );
+ return dest;
+}
+
+/**
+ * Copy (possibly overlapping) memory region
+ *
+ * @v dest Destination region
+ * @v src Source region
+ * @v len Length
+ * @ret dest Destination region
+ */
+static inline __attribute__ (( always_inline )) void *
+memmove ( void *dest, const void *src, size_t len ) {
+ ssize_t offset = ( dest - src );
+
+ /* If direction of copy is known to be forwards at build time,
+ * then use variable-length memcpy().
+ */
+ if ( __builtin_constant_p ( offset ) && ( offset <= 0 ) ) {
+ riscv_memcpy ( dest, src, len );
+ return dest;
+ }
+
+ /* Otherwise, use ambidirectional copy */
+ riscv_memmove ( dest, src, len );
+ return dest;
+}
+
+#endif /* _BITS_STRING_H */
diff --git a/src/arch/riscv/include/bits/strings.h b/src/arch/riscv/include/bits/strings.h
new file mode 100644
index 000000000..dd6d458b2
--- /dev/null
+++ b/src/arch/riscv/include/bits/strings.h
@@ -0,0 +1,91 @@
+#ifndef _BITS_STRINGS_H
+#define _BITS_STRINGS_H
+
+/** @file
+ *
+ * String functions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern __asmcall unsigned long riscv_ffs ( unsigned long value );
+extern __asmcall unsigned long riscv_fls ( unsigned long value );
+
+/**
+ * Find first (i.e. least significant) set bit
+ *
+ * @v value Value
+ * @ret lsb Least significant bit set in value (LSB=1), or zero
+ */
+static inline __attribute__ (( always_inline )) int __ffsl ( long value ) {
+
+ return riscv_ffs ( value );
+}
+
+/**
+ * Find first (i.e. least significant) set bit
+ *
+ * @v value Value
+ * @ret lsb Least significant bit set in value (LSB=1), or zero
+ */
+static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){
+ unsigned long low = value;
+ unsigned long high;
+
+ /* Check machine word size */
+ if ( sizeof ( value ) > sizeof ( low ) ) {
+ /* 32-bit */
+ high = ( value >> 32 );
+ if ( low ) {
+ return ( __ffsl ( low ) );
+ } else if ( high ) {
+ return ( 32 + __ffsl ( high ) );
+ } else {
+ return 0;
+ }
+ } else {
+ /* 64-bit */
+ return ( __ffsl ( low ) );
+ }
+}
+
+/**
+ * Find last (i.e. most significant) set bit
+ *
+ * @v value Value
+ * @ret msb Most significant bit set in value (LSB=1), or zero
+ */
+static inline __attribute__ (( always_inline )) int __flsl ( long value ) {
+
+ return riscv_fls ( value );
+}
+
+/**
+ * Find last (i.e. most significant) set bit
+ *
+ * @v value Value
+ * @ret msb Most significant bit set in value (LSB=1), or zero
+ */
+static inline __attribute__ (( always_inline )) int __flsll ( long long value ){
+ unsigned long low = value;
+ unsigned long high;
+
+ /* Check machine word size */
+ if ( sizeof ( value ) > sizeof ( low ) ) {
+ /* 32-bit */
+ high = ( value >> 32 );
+ if ( high ) {
+ return ( 32 + __flsl ( high ) );
+ } else if ( low ) {
+ return ( __flsl ( low ) );
+ } else {
+ return 0;
+ }
+ } else {
+ /* 64-bit */
+ return ( __flsl ( low ) );
+ }
+}
+
+#endif /* _BITS_STRINGS_H */
diff --git a/src/arch/riscv/include/bits/tcpip.h b/src/arch/riscv/include/bits/tcpip.h
new file mode 100644
index 000000000..0ac55b1a0
--- /dev/null
+++ b/src/arch/riscv/include/bits/tcpip.h
@@ -0,0 +1,15 @@
+#ifndef _BITS_TCPIP_H
+#define _BITS_TCPIP_H
+
+/** @file
+ *
+ * Transport-network layer interface
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern uint16_t tcpip_continue_chksum ( uint16_t partial, const void *data,
+ size_t len );
+
+#endif /* _BITS_TCPIP_H */
diff --git a/src/arch/riscv/include/bits/virt_offset.h b/src/arch/riscv/include/bits/virt_offset.h
new file mode 100644
index 000000000..83ac17551
--- /dev/null
+++ b/src/arch/riscv/include/bits/virt_offset.h
@@ -0,0 +1,33 @@
+#ifndef _BITS_VIRT_OFFSET_H
+#define _BITS_VIRT_OFFSET_H
+
+/** @file
+ *
+ * RISCV-specific virtual address offset
+ *
+ * We use the thread pointer register (tp) to hold the virtual address
+ * offset, so that virtual-to-physical address translations work as
+ * expected even while we are executing directly from read-only memory
+ * (and so cannot store a value in a global virt_offset variable).
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * Read virtual address offset held in thread pointer register
+ *
+ * @ret virt_offset Virtual address offset
+ */
+static inline __attribute__ (( const, always_inline )) unsigned long
+tp_virt_offset ( void ) {
+ register unsigned long tp asm ( "tp" );
+
+ __asm__ ( "" : "=r" ( tp ) );
+ return tp;
+}
+
+/** Always read thread pointer register to get virtual address offset */
+#define virt_offset tp_virt_offset()
+
+#endif /* _BITS_VIRT_OFFSET_H */
diff --git a/src/arch/riscv/include/ipxe/csr.h b/src/arch/riscv/include/ipxe/csr.h
new file mode 100644
index 000000000..c14974472
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/csr.h
@@ -0,0 +1,75 @@
+#ifndef _IPXE_CSR_H
+#define _IPXE_CSR_H
+
+/** @file
+ *
+ * Control and status registers (CSRs)
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * Check if CSR can be read
+ *
+ * @v name CSR name
+ * @v allowed CSR can be read
+ */
+#define csr_can_read( name ) ( { \
+ unsigned long stvec_orig; \
+ unsigned long stvec_temp; \
+ unsigned long csr; \
+ int allowed = 0; \
+ \
+ __asm__ __volatile__ ( /* Set temporary trap vector */ \
+ "la %3, 1f\n\t" \
+ "csrrw %2, stvec, %3\n\t" \
+ /* Try reading CSR */ \
+ "csrr %1, " name "\n\t" \
+ /* Mark as allowed if not trapped */ \
+ "addi %0, %0, 1\n\t" \
+ /* Temporary trap vector */ \
+ ".balign 4\n\t" \
+ "\n1:\n\t" \
+ /* Restore original trap vector */ \
+ "csrw stvec, %2\n\t" \
+ : "+r" ( allowed ), \
+ "=r" ( csr ), \
+ "=r" ( stvec_orig ), \
+ "=r" ( stvec_temp ) ); \
+ allowed; \
+ } )
+
+/**
+ * Check if CSR can be written
+ *
+ * @v name CSR name
+ * @v value Value to write
+ * @v allowed CSR can be written
+ */
+#define csr_can_write( name, value ) ( { \
+ unsigned long stvec_orig; \
+ unsigned long stvec_temp; \
+ unsigned long csr = (value); \
+ int allowed = 0; \
+ \
+ __asm__ __volatile__ ( /* Set temporary trap vector */ \
+ "la %3, 1f\n\t" \
+ "csrrw %2, stvec, %3\n\t" \
+ /* Try writing CSR */ \
+ "csrrw %1, " name ", %1\n\t" \
+ /* Mark as allowed if not trapped */ \
+ "addi %0, %0, 1\n\t" \
+ /* Temporary trap vector */ \
+ ".balign 4\n\t" \
+ "\n1:\n\t" \
+ /* Restore original trap vector */ \
+ "csrw stvec, %2\n\t" \
+ : "+r" ( allowed ), \
+ "+r" ( csr ), \
+ "=r" ( stvec_orig ), \
+ "=r" ( stvec_temp ) ); \
+ allowed; \
+ } )
+
+#endif /* _IPXE_CSR_H */
diff --git a/src/arch/riscv/include/ipxe/errno/sbi.h b/src/arch/riscv/include/ipxe/errno/sbi.h
new file mode 100644
index 000000000..2428183d4
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/errno/sbi.h
@@ -0,0 +1,19 @@
+#ifndef _IPXE_ERRNO_SBI_H
+#define _IPXE_ERRNO_SBI_H
+
+/**
+ * @file
+ *
+ * RISC-V SBI platform error codes
+ *
+ * We never need to return SBI error codes ourselves, so we
+ * arbitrarily choose to use the Linux error codes as platform error
+ * codes.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/errno/linux.h>
+
+#endif /* _IPXE_ERRNO_SBI_H */
diff --git a/src/arch/riscv/include/ipxe/hart.h b/src/arch/riscv/include/ipxe/hart.h
new file mode 100644
index 000000000..c201b6c77
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/hart.h
@@ -0,0 +1,16 @@
+#ifndef _IPXE_HART_H
+#define _IPXE_HART_H
+
+/** @file
+ *
+ * Hardware threads (harts)
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern unsigned long boot_hart;
+
+extern int hart_supported ( const char *ext );
+
+#endif /* _IPXE_HART_H */
diff --git a/src/arch/riscv/include/ipxe/riscv_dma.h b/src/arch/riscv/include/ipxe/riscv_dma.h
new file mode 100644
index 000000000..d35904d88
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/riscv_dma.h
@@ -0,0 +1,45 @@
+#ifndef _IPXE_RISCV_DMA_H
+#define _IPXE_RISCV_DMA_H
+
+/** @file
+ *
+ * iPXE DMA API for RISC-V
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef DMAAPI_RISCV
+#define DMAAPI_PREFIX_riscv
+#else
+#define DMAAPI_PREFIX_riscv __riscv_
+#endif
+
+/**
+ * Set addressable space mask
+ *
+ * @v dma DMA device
+ * @v mask Addressable space mask
+ */
+static inline __always_inline void
+DMAAPI_INLINE ( riscv, dma_set_mask ) ( struct dma_device *dma __unused,
+ physaddr_t mask __unused ) {
+
+ /* Nothing to do */
+}
+
+/**
+ * Get DMA address from virtual address
+ *
+ * @v map DMA mapping
+ * @v addr Address within the mapped region
+ * @ret addr Device-side DMA address
+ */
+static inline __always_inline physaddr_t
+DMAAPI_INLINE ( riscv, dma ) ( struct dma_mapping *map __unused, void *addr ) {
+
+ /* Use physical address as device address */
+ return virt_to_phys ( addr );
+}
+
+#endif /* _IPXE_RISCV_DMA_H */
diff --git a/src/arch/riscv/include/ipxe/riscv_io.h b/src/arch/riscv/include/ipxe/riscv_io.h
new file mode 100644
index 000000000..539dbd7ed
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/riscv_io.h
@@ -0,0 +1,141 @@
+#ifndef _IPXE_RISCV_IO_H
+#define _IPXE_RISCV_IO_H
+
+/** @file
+ *
+ * iPXE I/O API for RISC-V
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef IOAPI_RISCV
+#define IOAPI_PREFIX_riscv
+#else
+#define IOAPI_PREFIX_riscv __riscv_
+#endif
+
+#include <ipxe/dummy_pio.h>
+
+/*
+ * Memory space mappings
+ *
+ */
+
+/*
+ * Physical<->Bus address mappings
+ *
+ */
+
+static inline __always_inline unsigned long
+IOAPI_INLINE ( riscv, phys_to_bus ) ( unsigned long phys_addr ) {
+ return phys_addr;
+}
+
+static inline __always_inline unsigned long
+IOAPI_INLINE ( riscv, bus_to_phys ) ( unsigned long bus_addr ) {
+ return bus_addr;
+}
+
+/*
+ * MMIO reads and writes
+ *
+ */
+
+/* Single-register read */
+#define RISCV_READX( _suffix, _type, _insn_suffix ) \
+static inline __always_inline _type \
+IOAPI_INLINE ( riscv, read ## _suffix ) ( volatile _type *io_addr ) { \
+ unsigned long data; \
+ __asm__ __volatile__ ( "fence io, io\n\t" \
+ "l" _insn_suffix " %0, %1\n\t" \
+ : "=r" ( data ) : "m" ( *io_addr ) ); \
+ return data; \
+}
+
+/* Single-register write */
+#define RISCV_WRITEX( _suffix, _type, _insn_suffix) \
+static inline __always_inline void \
+IOAPI_INLINE ( riscv, write ## _suffix ) ( _type data, \
+ volatile _type *io_addr ) { \
+ __asm__ __volatile__ ( "fence io, io\n\t" \
+ "s" _insn_suffix " %0, %1\n\t" \
+ : : "r" ( data ), "m" ( *io_addr ) ); \
+}
+
+/* Double-register hopefully-fused read */
+#define RISCV_READX_FUSED( _suffix, _type, _insn_suffix ) \
+static inline __always_inline _type \
+IOAPI_INLINE ( riscv, read ## _suffix ) ( volatile _type *io_addr ) { \
+ union { \
+ unsigned long half[2]; \
+ _type data; \
+ } u; \
+ __asm__ __volatile__ ( "fence io, io\n\t" \
+ "l" _insn_suffix " %0, 0(%2)\n\t" \
+ "l" _insn_suffix " %1, %3(%2)\n\t" \
+ : "=&r" ( u.half[0] ), \
+ "=&r" ( u.half[1] ) \
+ : "r" ( io_addr ), \
+ "i" ( sizeof ( u.half[0] ) ) ); \
+ return u.data; \
+}
+
+/* Double-register hopefully-fused write */
+#define RISCV_WRITEX_FUSED( _suffix, _type, _insn_suffix ) \
+static inline __always_inline void \
+IOAPI_INLINE ( riscv, write ## _suffix ) ( _type data, \
+ volatile _type *io_addr ) { \
+ union { \
+ unsigned long half[2]; \
+ _type data; \
+ } u = { .data = data }; \
+ __asm__ __volatile__ ( "fence io, io\n\t" \
+ "s" _insn_suffix " %0, 0(%2)\n\t" \
+ "s" _insn_suffix " %1, %3(%2)\n\t" : \
+ : "r" ( u.half[0] ), \
+ "r" ( u.half[1] ), \
+ "r" ( io_addr ), \
+ "i" ( sizeof ( u.half[0] ) ) ); \
+}
+
+RISCV_READX ( b, uint8_t, "bu" );
+RISCV_WRITEX ( b, uint8_t, "b" );
+
+RISCV_READX ( w, uint16_t, "hu" );
+RISCV_WRITEX ( w, uint16_t, "h" );
+
+#if __riscv_xlen > 32
+ RISCV_READX ( l, uint32_t, "wu" );
+ RISCV_WRITEX ( l, uint32_t, "w" );
+#else
+ RISCV_READX ( l, uint32_t, "w" );
+ RISCV_WRITEX ( l, uint32_t, "w" );
+#endif
+
+#if __riscv_xlen >= 64
+ #if __riscv_xlen > 64
+ RISCV_READX ( q, uint64_t, "du" );
+ RISCV_WRITEX ( q, uint64_t, "d" );
+ #else
+ RISCV_READX ( q, uint64_t, "d" );
+ RISCV_WRITEX ( q, uint64_t, "d" );
+ #endif
+#else
+ RISCV_READX_FUSED ( q, uint64_t, "w" );
+ RISCV_WRITEX_FUSED ( q, uint64_t, "w" );
+#endif
+
+/*
+ * Memory barrier
+ *
+ */
+static inline __always_inline void
+IOAPI_INLINE ( riscv, mb ) ( void ) {
+ __asm__ __volatile__ ( "fence" : : : "memory" );
+}
+
+/* Dummy PIO */
+DUMMY_PIO ( riscv );
+
+#endif /* _IPXE_RISCV_IO_H */
diff --git a/src/arch/riscv/include/ipxe/sbi.h b/src/arch/riscv/include/ipxe/sbi.h
new file mode 100644
index 000000000..4364098b9
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/sbi.h
@@ -0,0 +1,213 @@
+#ifndef _IPXE_SBI_H
+#define _IPXE_SBI_H
+
+/** @file
+ *
+ * Supervisor Binary Interface (SBI)
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+/** An SBI function return value */
+struct sbi_return {
+ /** Error status (returned in a0) */
+ long error;
+ /** Data value (returned in a1) */
+ long value;
+};
+
+/**
+ * @defgroup sbierrors SBI errors
+ *
+ * *{
+ */
+#define SBI_SUCCESS 0 /**< Completed successfully */
+#define SBI_ERR_FAILED -1 /**< Failed */
+#define SBI_ERR_NOT_SUPPORTED -2 /**< Not supported */
+#define SBI_ERR_INVALID_PARAM -3 /**< Invalid parameter(s) */
+#define SBI_ERR_DENIED -4 /**< Denied or not allowed */
+#define SBI_ERR_INVALID_ADDRESS -5 /**< Invalid address(es) */
+#define SBI_ERR_ALREADY_AVAILABLE -6 /**< Already available */
+#define SBI_ERR_ALREADY_STARTED -7 /**< Already started */
+#define SBI_ERR_ALREADY_STOPPED -8 /**< Already stopped */
+#define SBI_ERR_NO_SHMEM -9 /**< Shared memory not available */
+#define SBI_ERR_INVALID_STATE -10 /**< Invalid state */
+#define SBI_ERR_BAD_RANGE -11 /**< Bad (or invalid) range */
+#define SBI_ERR_TIMEOUT -12 /**< Failed due to timeout */
+#define SBI_ERR_IO -13 /**< Input/output error */
+/** @} */
+
+/** Construct SBI extension ID */
+#define SBI_EID( c1, c2, c3, c4 ) \
+ ( (int) ( ( (c1) << 24 ) | ( (c2) << 16 ) | ( (c3) << 8 ) | (c4) ) )
+
+/**
+ * Call supervisor with no parameters
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_0 ( int eid, int fid ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" );
+ register unsigned long a1 asm ( "a1" );
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "=r" ( a0 ), "=r" ( a1 )
+ : "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/**
+ * Call supervisor with one parameter
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @v p0 Parameter 0
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_1 ( int eid, int fid, unsigned long p0 ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" ) = p0;
+ register unsigned long a1 asm ( "a1" );
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "+r" ( a0 ), "=r" ( a1 )
+ : "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/**
+ * Call supervisor with two parameters
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @v p0 Parameter 0
+ * @v p1 Parameter 1
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_2 ( int eid, int fid, unsigned long p0, unsigned long p1 ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" ) = p0;
+ register unsigned long a1 asm ( "a1" ) = p1;
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "+r" ( a0 ), "+r" ( a1 )
+ : "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/**
+ * Call supervisor with three parameters
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @v p0 Parameter 0
+ * @v p1 Parameter 1
+ * @v p2 Parameter 2
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_3 ( int eid, int fid, unsigned long p0, unsigned long p1,
+ unsigned long p2 ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" ) = p0;
+ register unsigned long a1 asm ( "a1" ) = p1;
+ register unsigned long a2 asm ( "a2" ) = p2;
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "+r" ( a0 ), "+r" ( a1 )
+ : "r" ( a2 ), "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/**
+ * Call supervisor with no parameters
+ *
+ * @v fid Legacy function ID
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) long
+sbi_legacy_ecall_0 ( int fid ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" );
+
+ __asm__ __volatile__ ( "ecall"
+ : "=r" ( a0 )
+ : "r" ( a7 )
+ : "memory" );
+ return a0;
+}
+
+/**
+ * Call supervisor with one parameter
+ *
+ * @v fid Legacy function ID
+ * @v p0 Parameter 0
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) long
+sbi_legacy_ecall_1 ( int fid, unsigned long p0 ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" ) = p0;
+
+ __asm__ __volatile__ ( "ecall"
+ : "+r" ( a0 )
+ : "r" ( a7 )
+ : "memory" );
+ return a0;
+}
+
+/** Convert an SBI error code to an iPXE status code */
+#define ESBI( error ) EPLATFORM ( EINFO_EPLATFORM, error )
+
+/** Legacy extensions */
+#define SBI_LEGACY_PUTCHAR 0x01 /**< Console Put Character */
+#define SBI_LEGACY_GETCHAR 0x02 /**< Console Get Character */
+#define SBI_LEGACY_SHUTDOWN 0x08 /**< System Shutdown */
+
+/** Base extension */
+#define SBI_BASE 0x10
+#define SBI_BASE_MVENDORID 0x04 /**< Get machine vendor ID */
+
+/** System reset extension */
+#define SBI_SRST SBI_EID ( 'S', 'R', 'S', 'T' )
+#define SBI_SRST_SYSTEM_RESET 0x00 /**< Reset system */
+#define SBI_RESET_SHUTDOWN 0x00000000 /**< Shutdown */
+#define SBI_RESET_COLD 0x00000001 /**< Cold reboot */
+#define SBI_RESET_WARM 0x00000002 /**< Warm reboot */
+
+/** Debug console extension */
+#define SBI_DBCN SBI_EID ( 'D', 'B', 'C', 'N' )
+#define SBI_DBCN_WRITE 0x00 /**< Console Write */
+#define SBI_DBCN_READ 0x01 /**< Console Read */
+#define SBI_DBCN_WRITE_BYTE 0x02 /**< Console Write Byte */
+
+#endif /* _IPXE_SBI_H */
diff --git a/src/arch/riscv/include/ipxe/sbi_reboot.h b/src/arch/riscv/include/ipxe/sbi_reboot.h
new file mode 100644
index 000000000..e8d6e82bf
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/sbi_reboot.h
@@ -0,0 +1,18 @@
+#ifndef _IPXE_BIOS_REBOOT_H
+#define _IPXE_BIOS_REBOOT_H
+
+/** @file
+ *
+ * Supervisor Binary Interface (SBI) reboot mechanism
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef REBOOT_SBI
+#define REBOOT_PREFIX_sbi
+#else
+#define REBOOT_PREFIX_sbi __sbi_
+#endif
+
+#endif /* _IPXE_BIOS_REBOOT_H */
diff --git a/src/arch/riscv/include/ipxe/svpage.h b/src/arch/riscv/include/ipxe/svpage.h
new file mode 100644
index 000000000..897a3379a
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/svpage.h
@@ -0,0 +1,28 @@
+#ifndef _IPXE_SVPAGE_H
+#define _IPXE_SVPAGE_H
+
+/** @file
+ *
+ * Supervisor page table management
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+#ifdef IOMAP_SVPAGE
+#define IOMAP_PREFIX_svpage
+#else
+#define IOMAP_PREFIX_svpage __svpage_
+#endif
+
+static inline __always_inline unsigned long
+IOMAP_INLINE ( svpage, io_to_bus ) ( volatile const void *io_addr ) {
+ /* Not easy to do; just return the CPU address for debugging purposes */
+ return ( ( intptr_t ) io_addr );
+}
+
+extern void * svpage_dma32 ( void );
+
+#endif /* _IPXE_SVPAGE_H */
diff --git a/src/arch/riscv/include/ipxe/xthead.h b/src/arch/riscv/include/ipxe/xthead.h
new file mode 100644
index 000000000..d0c9449ef
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/xthead.h
@@ -0,0 +1,21 @@
+#ifndef _IPXE_XTHEAD_H
+#define _IPXE_XTHEAD_H
+
+/** @file
+ *
+ * T-Head vendor extensions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** T-Head machine vendor ID */
+#define THEAD_MVENDORID 0x5b7
+
+/** T-Head SXSTATUS CSR */
+#define THEAD_SXSTATUS 0x5c0
+#define THEAD_SXSTATUS_THEADISAEE 0x00400000 /**< General ISA extensions */
+
+extern int xthead_supported ( unsigned long feature );
+
+#endif /* _IPXE_XTHEAD_H */
diff --git a/src/arch/riscv/include/ipxe/zicbom.h b/src/arch/riscv/include/ipxe/zicbom.h
new file mode 100644
index 000000000..4ba165f3c
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/zicbom.h
@@ -0,0 +1,17 @@
+#ifndef _IPXE_ZICBOM_H
+#define _IPXE_ZICBOM_H
+
+/** @file
+ *
+ * Cache-block management operations (Zicbom)
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+extern void cache_clean ( const void *start, size_t len );
+extern void cache_invalidate ( void *start, size_t len );
+
+#endif /* _IPXE_ZICBOM_H */
diff --git a/src/arch/riscv/interface/sbi/sbi_console.c b/src/arch/riscv/interface/sbi/sbi_console.c
new file mode 100644
index 000000000..1c3784ec8
--- /dev/null
+++ b/src/arch/riscv/interface/sbi/sbi_console.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+/** @file
+ *
+ * SBI debug console
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/sbi.h>
+#include <ipxe/io.h>
+#include <ipxe/keys.h>
+#include <ipxe/serial.h>
+#include <ipxe/console.h>
+#include <config/console.h>
+
+/* Set default console usage if applicable */
+#if ! ( defined ( CONSOLE_SBI ) && CONSOLE_EXPLICIT ( CONSOLE_SBI ) )
+#undef CONSOLE_SBI
+#define CONSOLE_SBI ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
+#endif
+
+extern void early_uart_putchar ( int character );
+
+/** Dummy serial console (if not present in build) */
+struct uart *serial_console __attribute__ (( weak ));
+
+/** Buffered input character (if any) */
+static unsigned char sbi_console_input;
+
+/**
+ * Print a character to SBI console
+ *
+ * @v character Character to be printed
+ */
+static void sbi_putchar ( int character ) {
+ struct sbi_return ret;
+
+ /* Do nothing if a real serial console has been enabled */
+ if ( serial_console )
+ return;
+
+ /* Write byte to early UART, if enabled */
+ early_uart_putchar ( character );
+
+ /* Write byte to console */
+ ret = sbi_ecall_1 ( SBI_DBCN, SBI_DBCN_WRITE_BYTE, character );
+ if ( ! ret.error )
+ return;
+
+ /* Debug extension not supported: try legacy method */
+ sbi_legacy_ecall_1 ( SBI_LEGACY_PUTCHAR, character );
+}
+
+/**
+ * Get character from SBI console
+ *
+ * @ret character Character read from console, if any
+ */
+static int sbi_getchar ( void ) {
+ int character;
+
+ /* Consume and return buffered character, if any */
+ character = sbi_console_input;
+ sbi_console_input = 0;
+
+ /* Convert DEL to backspace */
+ if ( character == DEL )
+ character = BACKSPACE;
+
+ return character;
+}
+
+/**
+ * Check for character ready to read from SBI console
+ *
+ * @ret True Character available to read
+ * @ret False No character available to read
+ */
+static int sbi_iskey ( void ) {
+ struct sbi_return ret;
+ long key;
+
+ /* Do nothing if we already have a buffered character */
+ if ( sbi_console_input )
+ return sbi_console_input;
+
+ /* Do nothing if a real serial console has been enabled */
+ if ( serial_console )
+ return 0;
+
+ /* Read and buffer byte from console, if any */
+ ret = sbi_ecall_3 ( SBI_DBCN, SBI_DBCN_READ,
+ sizeof ( sbi_console_input ),
+ virt_to_phys ( &sbi_console_input ), 0 );
+ if ( ! ret.error )
+ return ret.value;
+
+ /* Debug extension not supported: try legacy method */
+ key = sbi_legacy_ecall_0 ( SBI_LEGACY_GETCHAR );
+ if ( key > 0 ) {
+ sbi_console_input = key;
+ return key;
+ }
+
+ /* No character available */
+ return 0;
+}
+
+/** SBI console */
+struct console_driver sbi_console_driver __console_driver = {
+ .putchar = sbi_putchar,
+ .getchar = sbi_getchar,
+ .iskey = sbi_iskey,
+ .usage = CONSOLE_SBI,
+};
diff --git a/src/arch/riscv/interface/sbi/sbi_reboot.c b/src/arch/riscv/interface/sbi/sbi_reboot.c
new file mode 100644
index 000000000..b1c742ec7
--- /dev/null
+++ b/src/arch/riscv/interface/sbi/sbi_reboot.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Supervisor Binary Interface (SBI) reboot mechanism
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <ipxe/sbi.h>
+#include <ipxe/reboot.h>
+
+/**
+ * Reboot system
+ *
+ * @v flags Reboot flags
+ */
+static void sbi_reboot ( int flags ) {
+ struct sbi_return ret;
+ int warm;
+ int rc;
+
+ /* Reboot system */
+ warm = ( flags & REBOOT_WARM );
+ ret = sbi_ecall_2 ( SBI_SRST, SBI_SRST_SYSTEM_RESET,
+ ( warm ? SBI_RESET_WARM : SBI_RESET_COLD ), 0 );
+
+ /* Any return is an error */
+ rc = -ESBI ( ret.error );
+ DBGC ( SBI_SRST, "SBI %s reset failed: %s\n",
+ ( warm ? "warm" : "cold" ), strerror ( rc ) );
+
+ /* Try a legacy shutdown */
+ sbi_legacy_ecall_0 ( SBI_LEGACY_SHUTDOWN );
+ DBGC ( SBI_SRST, "SBI legacy shutdown failed\n" );
+}
+
+/**
+ * Power off system
+ *
+ * @ret rc Return status code
+ */
+static int sbi_poweroff ( void ) {
+ struct sbi_return ret;
+ int rc;
+
+ /* Shut down system */
+ ret = sbi_ecall_2 ( SBI_SRST, SBI_SRST_SYSTEM_RESET,
+ SBI_RESET_SHUTDOWN, 0 );
+
+ /* Any return is an error */
+ rc = -ESBI ( ret.error );
+ DBGC ( SBI_SRST, "SBI shutdown failed: %s\n", strerror ( rc ) );
+
+ /* Try a legacy shutdown */
+ sbi_legacy_ecall_0 ( SBI_LEGACY_SHUTDOWN );
+ DBGC ( SBI_SRST, "SBI legacy shutdown failed\n" );
+
+ return rc;
+}
+
+PROVIDE_REBOOT ( sbi, reboot, sbi_reboot );
+PROVIDE_REBOOT ( sbi, poweroff, sbi_poweroff );
diff --git a/src/arch/riscv/prefix/libprefix.S b/src/arch/riscv/prefix/libprefix.S
new file mode 100644
index 000000000..338131103
--- /dev/null
+++ b/src/arch/riscv/prefix/libprefix.S
@@ -0,0 +1,1529 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * RISC-V prefix library
+ *
+ */
+
+#include <config/serial.h>
+#include <config/fault.h>
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+ /* Link-time base address of _prefix
+ *
+ * This will be not be updated if runtime relocations are applied.
+ */
+ .section ".rodata.prefix_link", "a", @progbits
+ .balign ( __riscv_xlen / 8 )
+prefix_link:
+ .dword _base
+ .size prefix_link, . - prefix_link
+
+ /* Virtual address of _prefix
+ *
+ * This will be updated if runtime relocations are applied.
+ */
+ .section ".rodata.prefix_virt", "a", @progbits
+ .balign ( __riscv_xlen / 8 )
+prefix_virt:
+ .dword _prefix
+ .size prefix_virt, . - prefix_virt
+
+/*****************************************************************************
+ *
+ * Print character via debug console extension
+ *
+ *****************************************************************************
+ *
+ * Print a single character via the SBI DBCN extension.
+ *
+ * Parameters:
+ *
+ * a0 - Character to print
+ *
+ * Returns:
+ *
+ * a0 - Zero if character printed successfully
+ * a1 - Overwritten
+ * a6 - Overwritten
+ * a7 - Overwritten
+ *
+ */
+
+/* SBI debug console extension */
+#define SBI_DBCN ( ( 'D' << 24 ) | ( 'B' << 16 ) | ( 'C' << 8 ) | 'N' )
+#define SBI_DBCN_WRITE_BYTE 0x02
+
+ .macro print_char_dbcn
+ li a7, SBI_DBCN
+ li a6, SBI_DBCN_WRITE_BYTE
+ ecall
+ .endm
+
+/*****************************************************************************
+ *
+ * Print character via legacy extension
+ *
+ *****************************************************************************
+ *
+ * Print a single character via the SBI putchar legacy extension.
+ *
+ * Parameters:
+ *
+ * a0 - Character to print
+ *
+ * Returns:
+ *
+ * a0 - Overwritten
+ * a7 - Overwritten
+ *
+ */
+
+/* SBI legacy console putchar */
+#define SBI_LEGACY_PUTCHAR 0x01
+
+ .macro print_char_legacy
+ li a7, SBI_LEGACY_PUTCHAR
+ ecall
+ .endm
+
+/*****************************************************************************
+ *
+ * Print character via early UART
+ *
+ *****************************************************************************
+ *
+ * Print a single character via a UART.
+ *
+ * For devices without a functional SBI console, a UART at a hardcoded
+ * address can be used as a last resort mechanism for obtaining debug
+ * output from the prefix.
+ *
+ * Parameters:
+ *
+ * a0 - Character to print
+ *
+ * Returns:
+ *
+ * a0 - Preserved
+ * a1 - May be overwritten
+ * a6 - May be overwritten
+ * a7 - May be overwritten
+ *
+ */
+
+/* Default to no UART, if not specified */
+#ifndef EARLY_UART_MODEL
+#define EARLY_UART_MODEL none
+#endif
+
+/* Default to a register shift of zero, if not specified */
+#ifndef EARLY_UART_REG_SHIFT
+#define EARLY_UART_REG_SHIFT 0
+#endif
+
+#define print_char_uart _C2 ( print_char_uart_, EARLY_UART_MODEL )
+
+#define early_uart_reg_base _C2 ( early_uart_reg_base_, __riscv_xlen )
+
+ /* Print character via nonexistent UART */
+ .macro print_char_uart_none
+ .endm
+
+ /*
+ * Get UART base address (64-bit addressing)
+ */
+ .macro early_uart_reg_base_64 reg
+ csrr \reg, satp
+ beqz \reg, early_uart_reg_base_64_nonpaged_\@
+ LOADN \reg, early_uart_reg_base_64_virt
+ j early_uart_reg_base_64_done_\@
+early_uart_reg_base_64_nonpaged_\@:
+ li \reg, EARLY_UART_REG_BASE
+early_uart_reg_base_64_done_\@:
+ .endm
+
+ /*
+ * Get UART base address (32-bit addressing)
+ */
+ .macro early_uart_reg_base_32 reg
+ li \reg, EARLY_UART_REG_BASE
+ sub \reg, \reg, tp
+ .endm
+
+/*****************************************************************************
+ *
+ * Print character via 8250-compatible early UART
+ *
+ *****************************************************************************
+ *
+ * Print a single character via an 8250- or 16550-compatible UART.
+ *
+ * Parameters:
+ *
+ * a0 - Character to print
+ *
+ * Returns:
+ *
+ * a0 - Preserved
+ * a1 - Overwritten
+ * a7 - Overwritten
+ *
+ */
+
+/* 8250-compatible UART transmit registers */
+#define EARLY_UART_8250_TX ( 0 << EARLY_UART_REG_SHIFT )
+#define EARLY_UART_8250_LSR ( 5 << EARLY_UART_REG_SHIFT )
+#define EARLY_UART_8250_LSR_THRE 0x20
+
+ .macro print_char_uart_8250
+ early_uart_reg_base a7
+ sb a0, EARLY_UART_8250_TX(a7)
+ fence
+early_uart_8250_wait_\@:
+ lbu a1, EARLY_UART_8250_LSR(a7)
+ andi a1, a1, EARLY_UART_8250_LSR_THRE
+ beqz a1, early_uart_8250_wait_\@
+ .endm
+
+/*****************************************************************************
+ *
+ * Print character via SiFive-compatible early UART
+ *
+ *****************************************************************************
+ *
+ * Print a single character via a SiFive-compatible UART.
+ *
+ * Parameters:
+ *
+ * a0 - Character to print
+ *
+ * Returns:
+ *
+ * a0 - Preserved
+ * a1 - Overwritten
+ * a7 - Overwritten
+ *
+ */
+
+/* SiFive-compatible UART transmit registers */
+#define EARLY_UART_SIFIVE_TXFIFO ( 0 << EARLY_UART_REG_SHIFT )
+
+ .macro print_char_uart_sifive
+ early_uart_reg_base a7
+ sw a0, EARLY_UART_SIFIVE_TXFIFO(a7)
+ fence
+early_uart_sifive_wait_\@:
+ lw a1, EARLY_UART_SIFIVE_TXFIFO(a7)
+ bltz a1, early_uart_sifive_wait_\@
+ .endm
+
+/*****************************************************************************
+ *
+ * Print single character to early UART (from C code)
+ *
+ *****************************************************************************
+ *
+ * This function is called by the SBI console driver to output a
+ * character to the early UART (if enabled).
+ *
+ * The standard C ABI applies to this function.
+ *
+ * Parameters:
+ *
+ * a0 - Character to print
+ *
+ * Returns: none
+ *
+ */
+
+ .section ".prefix.early_uart_putchar", "ax", @progbits
+ .globl early_uart_putchar
+early_uart_putchar:
+ print_char_uart
+ ret
+ .size early_uart_putchar, . - early_uart_putchar
+
+/*****************************************************************************
+ *
+ * Print message to debug console
+ *
+ *****************************************************************************
+ *
+ * Print a NUL-terminated string to the debug console.
+ *
+ * This function prints one character at a time via the "write byte"
+ * call (rather than using "write string"), since this avoids any need
+ * to know the current virtual-physical address translation. It does
+ * not require a valid stack.
+ *
+ * Note that the parameter is passed in register t1 (rather than a0)
+ * and all non-temporary registers are preserved.
+ *
+ * Parameters:
+ *
+ * t1 - Pointer to string
+ *
+ * Returns: none
+ *
+ */
+
+ .section ".prefix.print_message", "ax", @progbits
+ .globl print_message
+print_message:
+ /* Handle alternate link register */
+ mv t0, ra
+print_message_alt:
+ /* Register usage:
+ *
+ * a0 - current character
+ * t0 - alternate link register
+ * t1 - character pointer
+ * t2 - preserved a0
+ * t3 - preserved a1
+ * t4 - preserved a6
+ * t5 - preserved a7
+ */
+ mv t2, a0
+ mv t3, a1
+ mv t4, a6
+ mv t5, a7
+
+1: /* Print each character in turn */
+ lbu a0, (t1)
+ addi t1, t1, 1
+ beqz a0, 2f
+ print_char_uart
+ print_char_dbcn
+ beqz a0, 1b
+ lbu a0, -1(t1)
+ print_char_legacy
+ j 1b
+2:
+ /* Restore registers and return (via alternate link register) */
+ mv a7, t5
+ mv a6, t4
+ mv a1, t3
+ mv a0, t2
+ jr t0
+ .size print_message, . - print_message
+
+ /*
+ * Display progress message (if debugging is enabled)
+ */
+ .macro progress message
+#ifndef NDEBUG
+ .section ".rodata.progress_\@", "a", @progbits
+progress_\@:
+ .asciz "\message"
+ .size progress_\@, . - progress_\@
+ .previous
+ la t1, progress_\@
+ jal t0, print_message_alt
+#endif
+ .endm
+
+/*****************************************************************************
+ *
+ * Print hexadecimal value to debug console
+ *
+ *****************************************************************************
+ *
+ * Print a register value in hexadecimal to the debug console.
+ *
+ * This function does not require a valid stack.
+ *
+ * Note that the parameters are passed in registers t1 and t2 (rather
+ * than a0) and all non-temporary registers are preserved.
+ *
+ * Parameters:
+ *
+ * t1 - Value to print
+ * t2 - Number of bits to print (must be a multiple of 4)
+ *
+ * Returns: none
+ *
+ */
+
+ /*
+ * Convert a single nibble to an ASCII character
+ */
+ .macro nibble_to_ascii reg
+ addi \reg, \reg, -10
+ bltz \reg, dec_\@
+ addi \reg, \reg, ( 'a' - ( '0' + 10 ) )
+dec_\@: addi \reg, \reg, ( '0' + 10 )
+ .endm
+
+ .section ".prefix.print_hex_value", "ax", @progbits
+ .globl print_hex_value
+print_hex_value:
+ /* Handle alternate link register */
+ mv t0, ra
+print_hex_value_alt:
+ /* Register usage:
+ *
+ * a0 - current digit / general temporary
+ * t0 - alternate link register
+ * t1 - current value
+ * t2 - digit counter
+ * t3 - preserved a0
+ * t4 - preserved a1
+ * t5 - preserved a6
+ * t6 - preserved a7
+ */
+ mv t3, a0
+ mv t4, a1
+ mv t5, a6
+ mv t6, a7
+
+ /* Skip any unprinted digits */
+ li a0, __riscv_xlen
+ sub a0, a0, t2
+ sll t1, t1, a0
+
+1: /* Print each digit in turn */
+ srli a0, t1, ( __riscv_xlen - 4 )
+ nibble_to_ascii a0
+ print_char_uart
+ print_char_dbcn
+ beqz a0, 2f
+ srli a0, t1, ( __riscv_xlen - 4 )
+ nibble_to_ascii a0
+ print_char_legacy
+2: slli t1, t1, 4
+ addi t2, t2, -4
+ bgtz t2, 1b
+
+ /* Restore registers and return (via alternate link register) */
+ mv a7, t6
+ mv a6, t5
+ mv a1, t4
+ mv a0, t3
+ jr t0
+ .size print_hex_value, . - print_hex_value
+
+ /*
+ * Display hexadecimal register value (if debugging is enabled)
+ */
+ .macro print_hex_reg reg, bits=__riscv_xlen
+#ifndef NDEBUG
+ mv t1, \reg
+ li t2, \bits
+ jal t0, print_hex_value_alt
+#endif
+ .endm
+
+ /*
+ * Display hexadecimal symbol address (if debugging is enabled)
+ */
+ .macro print_hex_addr sym
+#ifndef NDEBUG
+ la t1, \sym
+ li t2, __riscv_xlen
+ jal t0, print_hex_value_alt
+#endif
+ .endm
+
+ /*
+ * Display hexadecimal data value (if debugging is enabled)
+ */
+ .macro print_hex_data sym
+#ifndef NDEBUG
+ LOADN t1, \sym
+ li t2, __riscv_xlen
+ jal t0, print_hex_value_alt
+#endif
+ .endm
+
+/*****************************************************************************
+ *
+ * Apply compressed relocation records
+ *
+ *****************************************************************************
+ *
+ * Apply compressed relocation records to fix up iPXE to run at its
+ * current virtual address.
+ *
+ * This function must run before .bss is zeroed (since the relocation
+ * records are overlaid with .bss). It does not require a valid stack
+ * pointer.
+ *
+ * Parameters: none
+ *
+ * a0 - Relocation records
+ *
+ * Returns: none
+ *
+ */
+
+/** Number of bits in a skip value */
+#define ZREL_SKIP_BITS 19
+
+ .section ".prefix.apply_relocs", "ax", @progbits
+ .globl apply_relocs
+apply_relocs:
+ /* Register usage:
+ *
+ * a0 - current relocation record pointer
+ * a1 - current relocation target address
+ * a2 - relocation addend
+ * a3 - current relocation record value
+ * a4 - number of bits remaining in current relocation record
+ */
+ la a1, _prefix
+
+ /* Calculate relocation addend */
+ LOADN a2, prefix_virt
+ sub a2, a1, a2
+
+ /* Skip applying relocations if addend is zero */
+ beqz a2, apply_relocs_done
+ progress " reloc"
+
+ /* Test writability
+ *
+ * We do this to avoid accidentally sending an undefined
+ * sequence of commands to a flash device, if we are started
+ * from read-only memory with no paging support.
+ *
+ * We attempt to write an all-ones pattern, on the basis that
+ * this pattern will harmlessly cause any flash device
+ * conforming to the CFI01 specification to enter the default
+ * "read array" state.
+ */
+ la t0, apply_relocs_test
+ li t1, -1
+ STOREN t1, (t0)
+ LOADN t2, (t0)
+ bne t1, t2, apply_relocs_failed
+
+apply_relocs_loop:
+ /* Read new relocation record */
+ LOADN a3, (a0)
+ addi a0, a0, ( __riscv_xlen / 8 )
+ li a4, ( __riscv_xlen - 1 )
+
+ /* Consume and apply skip, if present (i.e. if MSB=0) */
+ bltz a3, 1f
+ addi a4, a4, -ZREL_SKIP_BITS
+ srli t0, a3, ( __riscv_xlen - ( ZREL_SKIP_BITS + 1 ) )
+ slli t0, t0, ( ( __riscv_xlen / 32 ) + 1 )
+ add a1, a1, t0
+1:
+ /* Apply relocations corresponding to set bits in record */
+1: andi t0, a3, 1
+ beqz t0, 2f
+ LOADN t1, (a1)
+ add t1, t1, a2
+ STOREN t1, (a1)
+2: addi a1, a1, ( __riscv_xlen / 8 )
+ srli a3, a3, 1
+ addi a4, a4, -1
+ bnez a4, 1b
+
+ /* Loop until we have reached a terminator record (MSB=0, offset=0) */
+ bnez a3, apply_relocs_loop
+
+ /* Check that relocations were applied successfully */
+ la t0, _prefix
+ LOADN t1, prefix_virt
+ bne t0, t1, apply_relocs_failed
+
+apply_relocs_done:
+ /* Return to caller */
+ progress " ok\r\n"
+ ret
+
+apply_relocs_failed:
+ /* Failure to apply relocations (if relocations were needed)
+ * is a fatal error.
+ */
+ progress " failed\r\n"
+ j reset_system
+ .size apply_relocs, . - apply_relocs
+
+ /* Writability test
+ *
+ * Placed within .data rather than .bss, since we need this to
+ * be within the range of the stored iPXE image.
+ */
+ .section ".data.apply_relocs_test", "aw", @progbits
+ .balign ( __riscv_xlen / 8 )
+apply_relocs_test:
+ .space ( __riscv_xlen / 8 )
+ .size apply_relocs_test, . - apply_relocs_test
+
+/*****************************************************************************
+ *
+ * Enable paging
+ *
+ *****************************************************************************
+ *
+ * This function must be called with flat physical addressing. It
+ * does not require a valid stack pointer.
+ *
+ * Parameters:
+ *
+ * a0 - Page table to fill in (4kB, must be aligned to a 4kB boundary)
+ *
+ * Returns:
+ *
+ * a0 - Size of accessible physical address space (or zero for no limit)
+ * tp - Virtual address offset
+ * pc - Updated to a virtual address if paging enabled
+ *
+ */
+
+/** Number of bits in a page offset */
+#define PAGE_SHIFT 12
+
+/** Page size */
+#define PAGE_SIZE ( 1 << PAGE_SHIFT )
+
+/** Size of a page table entry (log2) */
+#define PTE_SIZE_LOG2 ( ( __riscv_xlen / 32 ) + 1 )
+
+/** Size of a page table entry */
+#define PTE_SIZE ( 1 << PTE_SIZE_LOG2 )
+
+/** Number of page table entries (log2) */
+#define PTE_COUNT_LOG2 ( PAGE_SHIFT - PTE_SIZE_LOG2 )
+
+/** Number of page table entries */
+#define PTE_COUNT ( 1 << PTE_COUNT_LOG2 )
+
+/** Number of bits in a virtual or physical page number */
+#define VPPN_SHIFT PTE_COUNT_LOG2
+
+/* Page table entry flags */
+#define PTE_V 0x00000001 /**< Page table entry is valid */
+#define PTE_R 0x00000002 /**< Page is readable */
+#define PTE_W 0x00000004 /**< Page is writable */
+#define PTE_X 0x00000008 /**< Page is executable */
+#define PTE_A 0x00000040 /**< Page has been accessed */
+#define PTE_D 0x00000080 /**< Page is dirty */
+
+/* Page table entry flags for our leaf pages */
+#define PTE_LEAF ( PTE_D | PTE_A | PTE_X | PTE_W | PTE_R | PTE_V )
+
+/** Physical page number LSB in PTE */
+#define PTE_PPN_LSB(x) ( 10 + (x) * VPPN_SHIFT )
+#define PTE_PPN4_LSB PTE_PPN_LSB(4) /**< PPN[4] LSB (Sv57) */
+#define PTE_PPN3_LSB PTE_PPN_LSB(3) /**< PPN[3] LSB (Sv57 & Sv48) */
+#define PTE_PPN2_LSB PTE_PPN_LSB(2) /**< PPN[2] LSB (Sv57, Sv48, & Sv39) */
+#define PTE_PPN1_LSB PTE_PPN_LSB(1) /**< PPN[1] LSB (all levels) */
+#define PTE_PPN0_LSB PTE_PPN_LSB(0) /**< PPN[0] LSB (all levels) */
+
+/** Page table entry physical page address shift */
+#define PTE_PPN_SHIFT ( PAGE_SHIFT - PTE_PPN0_LSB )
+
+/** Virtual page number LSB */
+#define VPN_LSB(x) ( PAGE_SHIFT + (x) * VPPN_SHIFT )
+#define VPN4_LSB VPN_LSB(4) /**< VPN[4] LSB (Sv57) */
+#define VPN3_LSB VPN_LSB(3) /**< VPN[3] LSB (Sv57 & Sv48) */
+#define VPN2_LSB VPN_LSB(2) /**< VPN[2] LSB (Sv57, Sv48, & Sv39) */
+#define VPN1_LSB VPN_LSB(1) /**< VPN[1] LSB (all levels) */
+#define VPN0_LSB VPN_LSB(0) /**< VPN[0] LSB (all levels) */
+
+/* Paging modes */
+#define SATP_MODE_SV57 10 /**< Five-level paging (Sv57) */
+#define SATP_MODE_SV48 9 /**< Four-level paging (Sv48) */
+#define SATP_MODE_SV39 8 /**< Three-level paging (Sv39) */
+#define SATP_MODE_SV32 1 /**< Two-level paging (Sv32) */
+
+/** Paging mode shift */
+#if __riscv_xlen == 64
+#define SATP_MODE_SHIFT 60
+#else
+#define SATP_MODE_SHIFT 31
+#endif
+
+ .globl enable_paging
+ .equ enable_paging, _C2 ( enable_paging_, __riscv_xlen )
+
+ /* Paging mode names (for debug messages) */
+ .section ".rodata.paging_mode_names", "a", @progbits
+paging_mode_names:
+ .asciz "none"
+ .org ( paging_mode_names + 5 * SATP_MODE_SV32 )
+ .asciz "Sv32"
+ .org ( paging_mode_names + 5 * SATP_MODE_SV39 )
+ .asciz "Sv39"
+ .org ( paging_mode_names + 5 * SATP_MODE_SV48 )
+ .asciz "Sv48"
+ .org ( paging_mode_names + 5 * SATP_MODE_SV57 )
+ .asciz "Sv57"
+ .size paging_mode_names, . - paging_mode_names
+
+ /*
+ * Display paging mode name (if debugging is enabled)
+ */
+ .macro paging_mode_name reg
+#ifndef NDEBUG
+ slli t0, \reg, 2
+ add t0, t0, \reg
+ la t1, paging_mode_names
+ add t1, t1, t0
+ jal t0, print_message_alt
+#endif
+ .endm
+
+ /* Maximum physical alignment
+ *
+ * We align to a "megapage" boundary to simplify the task of
+ * setting up page table mappings.
+ */
+ .globl _max_align
+ .equ _max_align, ( 1 << VPN1_LSB )
+
+ /* Space for page table
+ *
+ * This can be used only once .bss is known to be writable.
+ */
+ .section ".bss.page_table", "a", @nobits
+ .globl page_table
+ .balign PAGE_SIZE
+page_table:
+ .space PAGE_SIZE
+ .size page_table, . - page_table
+
+ /* Convert physical address to virtual address */
+ .macro phys_to_virt rd, rs:vararg
+ _C2 ( phys_to_virt_, __riscv_xlen ) \rd, \rs
+ .endm
+
+/*****************************************************************************
+ *
+ * Disable paging
+ *
+ *****************************************************************************
+ *
+ * This function may be called with either virtual or flat physical
+ * addressing. It does not require a valid stack pointer.
+ *
+ * Parameters:
+ *
+ * tp - Virtual address offset
+ *
+ * Returns:
+ *
+ * tp - Virtual address offset (zeroed)
+ * pc - Updated to a physical address
+ *
+ */
+
+ .globl disable_paging
+ .equ disable_paging, _C2 ( disable_paging_, __riscv_xlen )
+
+/*****************************************************************************
+ *
+ * Enable 64-bit paging
+ *
+ *****************************************************************************
+ *
+ * Construct a 64-bit page table to identity-map the whole of the
+ * mappable physical address space, and to map iPXE itself at its
+ * link-time address (which must be 2MB-aligned and be within the
+ * upper half of the kernel address space).
+ *
+ * This function must be called with flat physical addressing. It
+ * does not require a valid stack pointer.
+ *
+ * Parameters:
+ *
+ * a0 - Page table to fill in (4kB, must be aligned to a 4kB boundary)
+ *
+ * Returns:
+ *
+ * a0 - Size of accessible physical address space (or zero for no limit)
+ * tp - Virtual address offset
+ * pc - Updated to a virtual address if paging enabled
+ *
+ * A 4kB 64-bit page table contains 512 8-byte PTEs. We choose to use
+ * these as:
+ *
+ * - PTE[0-255] : Identity map for the physical address space.
+ *
+ * This conveniently requires exactly 256 PTEs, regardless of the
+ * paging level. Higher paging levels are able to identity-map a
+ * larger physical address space:
+ *
+ * Sv57 : 256 x 256TB "petapages" (55-bit physical address space)
+ * Sv48 : 256 x 512GB "terapages" (46-bit physical address space)
+ * Sv39 : 256 x 1GB "gigapages" (37-bit physical address space)
+ *
+ * Note that Sv48 and Sv39 cannot identity-map the whole of the
+ * available physical address space, since the virtual address
+ * space is not large enough (and is halved by the constraint
+ * that virtual addresses with bit 47/38 set must also have all
+ * higher bits set, and so cannot identity-map to a 55-bit
+ * physical address).
+ *
+ * - PTE[x-y] : Virtual address map for iPXE
+ *
+ * These are 2MB "megapages" used to map the link-time virtual
+ * address range used by iPXE itself. We can use any 2MB-aligned
+ * range within 0xffffffffe0800000-0xffffffffffc00000, which
+ * breaks down as:
+ *
+ * VPN[4] = 511 (in Sv57, must be all-ones in Sv48 and Sv39)
+ * VPN[3] = 511 (in Sv57 and Sv48, must be all-ones in Sv39)
+ * VPN[2] = 511 (in all paging levels)
+ * VPN[1] = 260-510 (in all paging levels)
+ * VPN[0] = 0 (in all paging levels)
+ *
+ * In most builds, only a single 2MB "megapage" will be needed.
+ * We choose a link-time starting address of 0xffffffffeb000000
+ * within the permitted range, since the "eb" pattern is fairly
+ * distinctive and so makes it easy to visually identify any
+ * addresses originating from within iPXE's virtual address
+ * space.
+ *
+ * - PTE[511] : Recursive next level page table pointer
+ *
+ * This is a non-leaf PTE that points back to the page table
+ * itself. It acts as the next level page table pointer for:
+ *
+ * VPN[4] = 511 (in Sv57)
+ * VPN[3] = 511 (in Sv57 and Sv48)
+ * VPN[2] = 511 (in Sv57, Sv48, and Sv39)
+ *
+ * This recursive usage creates some duplicate mappings within
+ * unused portions of the virtual address space, but allows us to
+ * use only a single physical 4kB page table.
+ */
+
+/** SBI base extension */
+#define SBI_BASE 0x10
+#define SBI_BASE_MVENDORID 0x04
+
+/** Non-standard T-Head page table entry additional flags
+ *
+ * T-Head processors such as the C910 use the high bits of the PTE in
+ * a very non-standard way that is incompatible with the RISC-V
+ * specification.
+ *
+ * As per the "Memory Attribute Extension (XTheadMae)", bits 62 and 61
+ * represent cacheability and "bufferability" (i.e. write-back
+ * cacheability) respectively. If we do not enable these bits, then
+ * the processor gets incredibly confused at the point that paging is
+ * enabled. The symptom is that cache lines will occasionally fail to
+ * fill, and so reads from any address may return unrelated data from
+ * a previously read cache line for a different address.
+ */
+#define THEAD_PTE_MAEE ( 0x60 << ( __riscv_xlen - 8 ) )
+
+/** T-Head vendor ID */
+#define THEAD_MVENDORID 0x5b7
+
+/** T-Head "sxstatus" CSR */
+#define THEAD_CSR_SXSTATUS 0x5c0
+#define THEAD_CSR_SXSTATUS_MAEE 0x00200000 /**< XTheadMae enabled */
+
+ .section ".prefix.enable_paging_64", "ax", @progbits
+enable_paging_64:
+ /* Register usage:
+ *
+ * tp - return value (virtual address offset)
+ * a0 - page table base address
+ * a1 - currently attempted paging level
+ * a2 - enabled paging level
+ * a3 - PTE pointer
+ * a4 - PTE stride
+ * a5 - size of accessible physical address space
+ */
+ progress " paging:"
+
+ /* Calculate virtual address offset */
+ LOADN t0, prefix_link
+ la t1, _prefix
+ sub tp, t1, t0
+
+ /* Zero PTE[0-511] */
+ li t0, PTE_COUNT
+ mv a3, a0
+1: STOREN zero, (a3)
+ addi a3, a3, PTE_SIZE
+ addi t0, t0, -1
+ bgtz t0, 1b
+
+ /* Construct PTE[511] as next level page table pointer */
+ srli t0, a0, PTE_PPN_SHIFT
+ ori t0, t0, PTE_V
+ STOREN t0, -PTE_SIZE(a3)
+
+ /* Construct base page table entry for address zero */
+ li t0, PTE_LEAF
+ STOREN t0, (a0)
+
+ /* Check for broken T-Head paging extensions */
+ mv a3, a0
+ li a7, SBI_BASE
+ li a6, SBI_BASE_MVENDORID
+ ecall
+ bnez a0, 1f
+ li t0, THEAD_MVENDORID
+ bne a1, t0, 1f
+ progress "thead-"
+ csrr t0, THEAD_CSR_SXSTATUS
+ li t1, THEAD_CSR_SXSTATUS_MAEE
+ and t0, t0, t1
+ beqz t0, 1f
+ progress "mae-"
+ LOADN t0, (a3)
+ li t1, THEAD_PTE_MAEE
+ or t0, t0, t1
+ STOREN t0, (a3)
+1: mv a0, a3
+
+ /* Calculate PTE[x] address for iPXE virtual address map */
+ LOADN t0, prefix_link
+ srli t0, t0, VPN1_LSB
+ andi t0, t0, ( PTE_COUNT - 1 )
+ slli t0, t0, PTE_SIZE_LOG2
+ add a3, a0, t0
+
+ /* Calculate PTE stride for iPXE virtual address map
+ *
+ * PPN[1] LSB is PTE bit 19 in all paging modes, and so the
+ * stride is always ( 1 << 19 )
+ */
+ li a4, 1
+ slli a4, a4, PTE_PPN1_LSB
+
+ /* Construct PTE[x-1] for early UART, if applicable */
+#ifdef EARLY_UART_REG_BASE
+ li t0, ( EARLY_UART_REG_BASE & ~( ( 1 << VPN1_LSB ) - 1 ) )
+ srli t0, t0, PTE_PPN_SHIFT
+ ori t0, t0, ( PTE_LEAF & ~PTE_X )
+ STOREN t0, -PTE_SIZE(a3)
+#endif
+
+ /* Construct PTE[x-y] for iPXE virtual address map */
+ la t0, _prefix
+ srli t0, t0, PTE_PPN_SHIFT
+ LOADN t1, (a0)
+ or t0, t0, t1
+ la t2, _ebss
+ srli t2, t2, PTE_PPN_SHIFT
+1: STOREN t0, (a3)
+ addi a3, a3, PTE_SIZE
+ add t0, t0, a4
+ ble t0, t2, 1b
+
+ /* Find highest supported paging level */
+ li a1, SATP_MODE_SV57
+enable_paging_64_loop:
+
+ /* Calculate PTE stride for identity map at this paging level
+ *
+ * a1 == 10 == Sv57: PPN[4] LSB is PTE bit 46 => stride := 1 << 46
+ * a1 == 9 == Sv48: PPN[3] LSB is PTE bit 37 => stride := 1 << 37
+ * a1 == 8 == Sv39: PPN[2] LSB is PTE bit 28 => stride := 1 << 28
+ *
+ * and so we calculate stride a4 := ( 1 << ( 9 * a1 - 44 ) )
+ */
+ slli a4, a1, 3
+ add a4, a4, a1
+ addi a4, a4, -44
+ li t0, 1
+ sll a4, t0, a4
+
+ /* Calculate size of accessible physical address space
+ *
+ * The identity map comprises only the lower half of the PTEs,
+ * since virtual addresses for the higher half must have all
+ * high bits set, and so cannot form part of an identity map.
+ */
+ slli a5, a4, ( PTE_PPN_SHIFT + ( PTE_COUNT_LOG2 - 1 ) )
+
+ /* Construct PTE[0-255] for identity map at this paging level */
+ mv a3, a0
+ li t0, ( PTE_COUNT / 2 )
+ LOADN t1, (a0)
+1: STOREN t1, (a3)
+ addi a3, a3, PTE_SIZE
+ add t1, t1, a4
+ addi t0, t0, -1
+ bgtz t0, 1b
+
+ /* Attempt to enable paging, and read back active paging level */
+ slli t0, a1, SATP_MODE_SHIFT
+ srli t1, a0, PAGE_SHIFT
+ or t0, t0, t1
+ csrw satp, t0
+ sfence.vma
+ csrr a2, satp
+ srli a2, a2, SATP_MODE_SHIFT
+
+ /* Loop until we successfully enable paging, or run out of levels */
+ beq a2, a1, 1f
+ csrw satp, zero
+ addi a1, a1, -1
+ li t0, SATP_MODE_SV39
+ bge a1, t0, enable_paging_64_loop
+ mv tp, zero
+ mv a5, zero
+1:
+ /* Adjust return address to a virtual address */
+ sub ra, ra, tp
+
+ /* Return, with or without paging enabled */
+ paging_mode_name a2
+ mv a0, a5
+ ret
+ .size enable_paging_64, . - enable_paging_64
+
+ /* Convert 64-bit physical address to virtual address */
+ .macro phys_to_virt_64 rd, rs:vararg
+ .ifnb \rs
+ mv \rd, \rs
+ .endif
+ .endm
+
+ /* Early UART base address when 64-bit paging is enabled
+ *
+ * When an early UART is in use, we choose to use the 2MB
+ * "megapage" immediately below iPXE itself to map the UART.
+ */
+#ifdef EARLY_UART_REG_BASE
+ .section ".rodata.early_uart_reg_base_64_virt", "a", @progbits
+ .balign 8
+early_uart_reg_base_64_virt:
+ .dword ( _base - ( 1 << VPN1_LSB ) + \
+ ( EARLY_UART_REG_BASE & ( ( 1 << VPN1_LSB ) - 1 ) ) )
+ .size early_uart_reg_base_64_virt, . - early_uart_reg_base_64_virt
+#endif
+
+/*****************************************************************************
+ *
+ * Disable 64-bit paging
+ *
+ *****************************************************************************
+ *
+ * This function may be called with either virtual or flat physical
+ * addressing. It does not require a valid stack pointer.
+ *
+ * Parameters:
+ *
+ * tp - Virtual address offset
+ *
+ * Returns:
+ *
+ * tp - Virtual address offset (zeroed)
+ * pc - Updated to a physical address
+ *
+ */
+
+ .section ".prefix.disable_paging_64", "ax", @progbits
+disable_paging_64:
+ /* Register usage:
+ *
+ * tp - virtual address offset
+ */
+
+ /* Jump to physical address */
+ la t0, 1f
+ bgez t0, 1f
+ add t0, t0, tp
+ jr t0
+1:
+ /* Disable paging */
+ csrw satp, zero
+ sfence.vma
+
+ /* Update return address to a physical address */
+ bgez ra, 1f
+ add ra, ra, tp
+1:
+ /* Return with paging disabled and virtual offset zeroed */
+ mv tp, zero
+ ret
+ .size disable_paging_64, . - disable_paging_64
+
+/*****************************************************************************
+ *
+ * Enable 32-bit paging
+ *
+ *****************************************************************************
+ *
+ * Construct a 32-bit page table to map the whole of the 32-bit
+ * address space with a fixed offset selected to map iPXE itself at
+ * its link-time address (which must be 4MB-aligned).
+ *
+ * This function must be called with flat physical addressing. It
+ * does not require a valid stack pointer.
+ *
+ * Parameters:
+ *
+ * a0 - Page table to fill in (4kB, must be aligned to a 4kB boundary)
+ *
+ * Returns:
+ *
+ * a0 - Size of accessible physical address space (or zero for no limit)
+ * tp - Virtual address offset
+ * pc - Updated to a virtual address if paging enabled
+ *
+ * A 4kB 32-bit page table contains 1024 4-byte PTEs. We choose to
+ * use these to produce a circular map of the 32-bit address space
+ * using 4MB "megapages", with a fixed offset to align the virtual and
+ * link-time addresses.
+ *
+ * To handle the transition from physical to virtual addresses, we
+ * temporarily adjust the PTE covering the current program counter to
+ * be a direct physical map (so that the program counter remains valid
+ * at the moment when paging is enabled), then jump to a virtual
+ * address, then restore the temporarily modified PTE.
+ */
+
+ .equ enable_paging_32_xalign, 32
+
+ .section ".prefix.enable_paging_32", "ax", @progbits
+enable_paging_32:
+ /* Register usage:
+ *
+ * tp - return value (virtual address offset)
+ * a0 - page table base address
+ * a1 - enabled paging level
+ * a2 - PTE pointer
+ * a3 - saved content of temporarily modified PTE
+ */
+ progress " paging:"
+
+ /* Calculate virtual address offset */
+ LOADN t0, prefix_link
+ la t1, _prefix
+ sub tp, t1, t0
+
+ /* Construct PTEs for circular map */
+ mv a2, a0
+ li t0, PTE_COUNT
+ mv t1, tp
+ ori t1, t1, ( PTE_LEAF << PTE_PPN_SHIFT )
+ li t2, ( 1 << ( PTE_PPN1_LSB + PTE_PPN_SHIFT ) )
+1: srli t3, t1, PTE_PPN_SHIFT
+ STOREN t3, (a2)
+ addi a2, a2, PTE_SIZE
+ add t1, t1, t2
+ addi t0, t0, -1
+ bgtz t0, 1b
+
+ /* Temporarily modify PTE for transition code to be an identity map */
+ la t0, enable_paging_32_xstart
+ srli t0, t0, VPN1_LSB
+ slli t1, t0, PTE_SIZE_LOG2
+ add a2, a0, t1
+ LOADN a3, (a2)
+ slli t0, t0, PTE_PPN1_LSB
+ ori t0, t0, PTE_LEAF
+ STOREN t0, (a2)
+
+ /* Adjust PTE pointer to a virtual address */
+ sub a2, a2, tp
+
+ /* Attempt to enable paging, and read back active paging level */
+ la t0, 1f
+ sub t0, t0, tp
+ li t1, ( SATP_MODE_SV32 << SATP_MODE_SHIFT )
+ srli t2, a0, PAGE_SHIFT
+ or t1, t1, t2
+ .balign enable_paging_32_xalign
+ /* Start of transition code */
+enable_paging_32_xstart:
+ csrw satp, t1
+ sfence.vma
+ csrr a1, satp
+ beqz a1, 2f
+ jr t0
+1: /* Restore temporarily modified PTE */
+ STOREN a3, (a2)
+ sfence.vma
+ /* End of transition code */
+ .equ enable_paging_32_xlen, . - enable_paging_32_xstart
+2: srli a1, a1, SATP_MODE_SHIFT
+
+ /* Zero SATP and virtual address offset if paging is not enabled */
+ bnez a1, 1f
+ csrw satp, zero
+ mv tp, zero
+1:
+ /* Adjust return address to a virtual address */
+ sub ra, ra, tp
+
+ /* Return, with or without paging enabled */
+ paging_mode_name a1
+ mv a0, zero
+ ret
+ .size enable_paging_32, . - enable_paging_32
+
+ /* Ensure that transition code did not cross an alignment boundary */
+ .section ".bss.enable_paging_32_xcheck", "aw", @nobits
+ .org . + enable_paging_32_xalign - enable_paging_32_xlen
+
+ /* Convert 32-bit physical address to virtual address */
+ .macro phys_to_virt_32 rd, rs:vararg
+ .ifnb \rs
+ sub \rd, \rs, tp
+ .else
+ sub \rd, \rd, tp
+ .endif
+ .endm
+
+/*****************************************************************************
+ *
+ * Disable 32-bit paging
+ *
+ *****************************************************************************
+ *
+ * This function may be called with either virtual or flat physical
+ * addressing. It does not require a valid stack pointer.
+ *
+ * Parameters:
+ *
+ * tp - Virtual address offset
+ *
+ * Returns:
+ *
+ * tp - Virtual address offset (zeroed)
+ * pc - Updated to a physical address
+ *
+ */
+
+ .equ disable_paging_32_xalign, 16
+
+ .section ".prefix.disable_paging_32", "ax", @progbits
+disable_paging_32:
+ /* Register usage:
+ *
+ * tp - virtual address offset
+ * a0 - page table address
+ * a1 - transition PTE pointer
+ * a2 - transition PTE content
+ */
+
+ /* Get page table address, and exit if paging is already disabled */
+ csrr a0, satp
+ beqz a0, 99f
+ slli a0, a0, PAGE_SHIFT
+ sub a0, a0, tp
+
+ /* Prepare for modifying transition PTE */
+ la t0, disable_paging_32_xstart
+ add t0, t0, tp
+ srli t0, t0, VPN1_LSB
+ slli a1, t0, PTE_SIZE_LOG2
+ add a1, a1, a0
+ slli a2, t0, PTE_PPN1_LSB
+ ori a2, a2, PTE_LEAF
+
+ /* Jump to physical address in transition PTE, and disable paging */
+ la t0, 1f
+ add t0, t0, tp
+ .balign disable_paging_32_xalign
+ /* Start of transition code */
+disable_paging_32_xstart:
+ STOREN a2, (a1)
+ sfence.vma
+ jr t0
+1: csrw satp, zero
+ sfence.vma
+ /* End of transition code */
+ .equ disable_paging_32_xlen, . - disable_paging_32_xstart
+
+ /* Update return address to a physical address */
+ add ra, ra, tp
+
+99: /* Return with paging disabled and virtual offset zeroed */
+ mv tp, zero
+ ret
+ .size disable_paging_32, . - disable_paging_32
+
+ /* Ensure that transition code did not cross an alignment boundary */
+ .section ".bss.disable_paging_32_xcheck", "aw", @nobits
+ .org . + disable_paging_32_xalign - disable_paging_32_xlen
+
+/*****************************************************************************
+ *
+ * Poison .bss section
+ *
+ *****************************************************************************
+ *
+ * Fill the .bss section with an invalid non-zero value to expose bugs
+ * in early initialisation code that erroneously relies upon variables
+ * in .bss before the section has been zeroed.
+ *
+ * We use the value 0xeb55eb55eb55eb55 ("EBSS") since this is
+ * immediately recognisable as a value in a crash dump, and will
+ * trigger a page fault if dereferenced since the address is in a
+ * non-canonical form.
+ *
+ * Poisoning the .bss will overwrite the relocation records, and so
+ * can be done only as a debugging step on a system where relocation
+ * is known to be unnecessary (e.g. because paging is supported).
+ *
+ * This function does not require a valid stack pointer, but will
+ * destroy any existing stack contents if the stack happens to be
+ * placed within the original .bss section.
+ *
+ * Parameters: none
+ *
+ * Returns: none
+ *
+ */
+
+ .equ poison_bss_value_32, 0xeb55eb55
+ .equ poison_bss_value_64, 0xeb55eb55eb55eb55
+ .equ poison_bss_value, _C2 ( poison_bss_value_, __riscv_xlen )
+
+ .section ".prefix.poison_bss", "ax", @progbits
+poison_bss:
+ /* Fill .bss section */
+ la t0, _bss
+ la t1, _ebss
+ li t2, poison_bss_value
+1: STOREN t2, (t0)
+ addi t0, t0, ( __riscv_xlen / 8 )
+ blt t0, t1, 1b
+ ret
+ .size poison_bss, . - poison_bss
+
+/*****************************************************************************
+ *
+ * Install iPXE to a suitable runtime address
+ *
+ *****************************************************************************
+ *
+ * Identify a suitable runtime address for iPXE, relocate there, and
+ * set up for running normal C code.
+ *
+ * A valid temporary stack pointer is required. A 4kB space for a
+ * temporary page table may be provided, and must be provided if the
+ * iPXE image is running from read-only memory.
+ *
+ * Note that this function does not preserve the callee-save registers.
+ *
+ * Parameters:
+ *
+ * a0 - Boot hart ID
+ * a1 - Device tree physical address
+ * a2 - Optional temporary page table space (4kB, aligned to a 4kB boundary)
+ * sp - Valid temporary stack pointer
+ *
+ * Returns:
+ *
+ * pc - Updated to be within the relocated iPXE
+ * sp - Top of internal stack
+ * tp - Virtual address offset
+ *
+ */
+
+ .section ".prefix.install", "ax", @progbits
+ .globl install
+install:
+ /* Register usage:
+ *
+ * s0 - boot hart ID
+ * s1 - device tree physical address
+ * s2 - saved return address
+ * s3 - relocation records physical address
+ * s4 - maximum accessible physical address
+ * s5 - relocation physical address
+ * s6 - relocation offset
+ * tp - virtual address offset
+ */
+ mv tp, zero
+ progress "\r\nSBI->iPXE hart:"
+ print_hex_reg a0
+ progress " temp:"
+ print_hex_reg a2
+ progress " fdt:"
+ print_hex_reg a1
+ progress "\r\nSBI->iPXE phys:"
+ print_hex_addr _prefix
+ progress " virt:"
+ print_hex_data prefix_virt
+ mv s0, a0
+ mv s1, a1
+ mv s2, ra
+ la s3, _edata
+
+ /* Poison .bss if configured to do so */
+#if POISON_BSS
+ call poison_bss
+#endif
+
+ /* Attempt to enable paging, if we have temporary page table space */
+ mv a0, a2
+ beqz a2, 1f
+ call enable_paging
+1: addi s4, a0, -1
+
+ /* Apply relocations, if still needed after enabling paging */
+ mv a0, s3
+ call apply_relocs
+
+ /* Find a suitable address for relocation (using temporary stack) */
+ phys_to_virt a0, s1
+ mv a1, s4
+ phys_to_virt sp
+ call fdtmem_relocate
+ mv s5, a0
+ progress "SBI->iPXE dest:"
+ print_hex_reg a0
+
+ /* Disable paging */
+ call disable_paging
+
+ /* Determine relocation offset */
+ la s6, _prefix
+ sub s6, s5, s6
+
+ /* Copy iPXE image to new location and zero .bss */
+ mv t0, s5
+ la t1, _prefix
+ la t2, _edata
+1: LOADN t3, (t1)
+ STOREN t3, (t0)
+ addi t0, t0, ( __riscv_xlen / 8 )
+ addi t1, t1, ( __riscv_xlen / 8 )
+ blt t1, t2, 1b
+ la t1, _ebss
+ add t1, t1, s6
+2: STOREN zero, (t0)
+ addi t0, t0, ( __riscv_xlen / 8 )
+ blt t0, t1, 2b
+
+ /* Jump to relocated copy */
+ la t0, 1f
+ add t0, t0, s6
+ jr t0
+1:
+ /* Attempt to re-enable paging */
+ la a0, page_table
+ call enable_paging
+
+ /* Reapply relocations, if still needed after enabling paging */
+ phys_to_virt a0, s3
+ call apply_relocs
+
+ /* Load stack pointer */
+ la sp, _estack
+
+ /* Store boot hart */
+ STOREN s0, boot_hart, t0
+
+ /* Copy and register system device tree */
+ phys_to_virt a0, s1
+ mv a1, s4
+ call fdtmem_register
+
+ /* Return to a virtual address in the relocated copy */
+ add ra, s2, s6
+ sub ra, ra, tp
+ progress "\r\n"
+ ret
+ .size install, . - install
+
+/*****************************************************************************
+ *
+ * Reset (or lock up) system
+ *
+ *****************************************************************************
+ *
+ * Reset via system via SBI, as a means of exiting from a prefix that
+ * has no other defined exit path. If the reset fails, lock up the
+ * system since there is nothing else that can sensibly be done.
+ *
+ * This function does not require a valid stack pointer.
+ *
+ * Parameters: none
+ *
+ * Returns: n/a (does not return)
+ *
+ */
+
+/* SBI system reset extension */
+#define SBI_SRST ( ( 'S' << 24 ) | ( 'R' << 16 ) | ( 'S' << 8 ) | 'T' )
+#define SBI_SRST_SYSTEM_RESET 0x00
+#define SBI_RESET_COLD 0x00000001
+
+/* SBI legacy shutdown */
+#define SBI_LEGACY_SHUTDOWN 0x08
+
+ .section ".prefix.reset_system", "ax", @progbits
+ .globl reset_system
+reset_system:
+ /* Register usage: irrelevant (does not return) */
+ progress "\r\niPXE->SBI reset\r\n"
+
+ /* Attempt reset */
+ li a7, SBI_SRST
+ li a6, SBI_SRST_SYSTEM_RESET
+ li a0, SBI_RESET_COLD
+ mv a1, zero
+ ecall
+ progress "(reset failed)\r\n"
+
+ /* Attempt legacy shutdown */
+ li a7, SBI_LEGACY_SHUTDOWN
+ ecall
+ progress "(legacy shutdown failed)\r\n"
+
+ /* If reset failed, lock the system */
+1: wfi
+ j 1b
+ .size reset_system, . - reset_system
+
+/*****************************************************************************
+ *
+ * File split information for the compressor
+ *
+ *****************************************************************************
+ */
+
+/* ELF machine type */
+#define EM_RISCV 243
+
+ .section ".zinfo", "a", @progbits
+ .org 0
+ /* Copy initialised-data portion of image */
+ .ascii "COPY"
+ .word 0
+ .word _filesz
+ .word 1
+ /* Notify compressor of link-time base address */
+ .ascii "BASE"
+ .word 0
+ .dword _base
+ /* Construct compressed relocation records */
+ .ascii "ZREL"
+ .word _reloc_offset
+ .word _reloc_filesz
+ .word EM_RISCV
diff --git a/src/arch/riscv/prefix/lkrnprefix.S b/src/arch/riscv/prefix/lkrnprefix.S
new file mode 100644
index 000000000..9493d016c
--- /dev/null
+++ b/src/arch/riscv/prefix/lkrnprefix.S
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * Linux kernel prefix
+ *
+ */
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+ /* Layout of kernel header */
+ .struct 0
+hdr_code0: .space 4
+hdr_code1: .space 4
+hdr_text_offset: .space 8
+hdr_image_size: .space 8
+hdr_flags: .space 8
+hdr_version: .space 4
+hdr_res1: .space 4
+hdr_res2: .space 8
+hdr_magic: .space 8
+hdr_magic2: .space 4
+hdr_res3: .space 4
+hdr_end:
+ .org 64
+ .previous
+
+/* Header version */
+#define HDR_VERSION( major, minor ) ( ( (major) << 16 ) | (minor) )
+#define HDR_VERSION_0_2 HDR_VERSION ( 0, 2 )
+
+/* Header flags */
+#define HDR_FL_BIG_ENDIAN 0x00000001
+
+/* Magic numbers */
+#define HDR_MAGIC "RISCV\0\0\0"
+#define HDR_MAGIC2 "RSC\x05"
+
+ /*
+ * Linux kernel header
+ */
+ .section ".prefix", "ax", @progbits
+
+ /* Executable code / MZ header (for EFI-compatible binaries) */
+ .org hdr_code0
+ j _lkrn_start
+
+ /* Image load offset
+ *
+ * Must be set to the size of a single "megapage" (2MB for
+ * 64-bit, 4MB for 32-bit).
+ */
+ .org hdr_text_offset
+ .dword _max_align
+
+ /* Image size (including uninitialised-data potions) */
+ .org hdr_image_size
+ .dword _memsz
+
+ /* Flags */
+ .org hdr_flags
+ .dword 0
+
+ /* Version */
+ .org hdr_version
+ .word HDR_VERSION_0_2
+
+ /* Magic numbers */
+ .org hdr_magic
+ .ascii HDR_MAGIC
+ .org hdr_magic2
+ .ascii HDR_MAGIC2
+
+ .org hdr_end
+
+ /*
+ * Linux kernel entry point
+ */
+ .globl _lkrn_start
+_lkrn_start:
+ /* Identify temporary page table and stack space
+ *
+ * Linux expects to be placed at the image load offset from
+ * the start of RAM. Assume that our loaded image is
+ * therefore already writable, and that we can therefore use
+ * the page table and stack within our (not yet zeroed) .bss
+ * section.
+ */
+ la a2, page_table
+ la sp, _estack
+
+ /* Install iPXE */
+ call install
+
+ /* Call main program */
+ call main
+
+ /* We have no return path, since the Linux kernel does not
+ * define that a valid return address exists.
+ *
+ * Attempt a system reset, since there is nothing else we can
+ * viably do at this point.
+ */
+ j reset_system
+ .size _lkrn_start, . - _lkrn_start
diff --git a/src/arch/riscv/prefix/sbiprefix.S b/src/arch/riscv/prefix/sbiprefix.S
new file mode 100644
index 000000000..da3202aa2
--- /dev/null
+++ b/src/arch/riscv/prefix/sbiprefix.S
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * SBI position-independent executable prefix
+ *
+ */
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+/* Page size */
+#define PAGE_SIZE 4096
+
+ /*
+ * SBI entry point
+ */
+ .section ".prefix", "ax", @progbits
+ .org 0
+ .globl _sbi_start
+_sbi_start:
+ /* Identify temporary page table and stack space
+ *
+ * Assume that there is sufficient writable memory (~8kB)
+ * directly below the device tree.
+ */
+ li t0, ~( PAGE_SIZE - 1 )
+ and sp, a1, t0
+ li t0, PAGE_SIZE
+ sub sp, sp, t0
+ mv a2, sp
+
+ /* Install iPXE */
+ call install
+
+ /* Call main program */
+ call main
+
+ /* We have no return path, since the M-mode SBI implementation
+ * will have jumped to us by setting our start address in MEPC
+ * and issuing an MRET instruction.
+ *
+ * Attempt a system reset, since there is nothing else we can
+ * viably do at this point.
+ */
+ j reset_system
+ .size _sbi_start, . - _sbi_start
diff --git a/src/arch/riscv/scripts/sbi.lds b/src/arch/riscv/scripts/sbi.lds
new file mode 100644
index 000000000..04cabcf85
--- /dev/null
+++ b/src/arch/riscv/scripts/sbi.lds
@@ -0,0 +1,133 @@
+/*
+ * Linker script for RISC-V SBI images
+ *
+ */
+
+SECTIONS {
+
+ /* Weak symbols that need zero values if not otherwise defined */
+ saved_pos = .;
+ .weak 0x0 : {
+ _weak = .;
+ *(.weak)
+ *(.weak.*)
+ _eweak = .;
+ }
+ _assert = ASSERT ( ( _weak == _eweak ), ".weak is non-zero length" );
+ _assert = ASSERT ( ( . == saved_pos ), ".weak altered current position" );
+
+ /* Prefix code */
+ .prefix : {
+ _prefix = .;
+ *(.prefix)
+ *(.prefix.*)
+ _eprefix = .;
+ }
+
+ /* Program code */
+ .text : {
+ _text = .;
+ *(.text)
+ *(.text.*)
+ _etext = .;
+ }
+
+ /* Align to page size to allow linker to generate W^X segments */
+ . = ALIGN ( 4096 );
+
+ /* Read-only data */
+ .rodata : {
+ _rodata = .;
+ *(.srodata)
+ *(.srodata.*)
+ *(.rodata)
+ *(.rodata.*)
+ _erodata = .;
+ }
+
+ /* Writable data */
+ .data : {
+ _data = .;
+ *(.sdata)
+ *(.sdata.*)
+ *(.data)
+ *(.data.*)
+ KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */
+ KEEP(*(.provided))
+ KEEP(*(.provided.*))
+ *(.got)
+ *(.got.plt)
+ /* Ensure compressed relocations end up aligned */
+ . = ALIGN ( 16 );
+ _edata = .;
+ }
+
+ /* Uninitialised and discardable data */
+ OVERLAY : {
+
+ /* Runtime relocations (discarded after use) */
+ .rela.dyn {
+ *(.rela)
+ *(.rela.dyn)
+ }
+
+ /* Compressor information block */
+ .zinfo {
+ _zinfo = .;
+ KEEP(*(.zinfo))
+ KEEP(*(.zinfo.*))
+ _ezinfo = .;
+ }
+
+ /* Uninitialised data */
+ .bss {
+ _bss = .;
+ *(.sbss)
+ *(.sbss.*)
+ *(.bss)
+ *(.bss.*)
+ *(COMMON)
+ *(.stack)
+ *(.stack.*)
+ /* Align to allow for easy zeroing by prefix code */
+ . = ALIGN ( 16 );
+ _ebss = .;
+ }
+ }
+
+ /* End virtual address */
+ _end = .;
+
+ /* Base virtual address */
+ _base = ABSOLUTE ( _prefix );
+
+ /* Relocations */
+ _reloc_offset = ( LOADADDR ( .rela.dyn ) - LOADADDR ( .prefix ) );
+ _reloc_filesz = SIZEOF ( .rela.dyn );
+
+ /* Length of initialised data */
+ _filesz = ( ABSOLUTE ( _edata ) - ABSOLUTE ( _prefix ) );
+
+ /* Length of in-memory image */
+ _memsz = ( ABSOLUTE ( _end ) - ABSOLUTE ( _prefix ) );
+
+ /* Unwanted sections */
+ /DISCARD/ : {
+ *(.comment)
+ *(.comment.*)
+ *(.note)
+ *(.note.*)
+ *(.eh_frame)
+ *(.eh_frame.*)
+ *(.dynamic)
+ *(.dynsym)
+ *(.dynstr)
+ *(.hash)
+ *(.gnu.hash)
+ *(.einfo)
+ *(.einfo.*)
+ *(.discard)
+ *(.discard.*)
+ *(.pci_devlist.*)
+ }
+}
diff --git a/src/arch/riscv32/Makefile b/src/arch/riscv32/Makefile
new file mode 100644
index 000000000..0d8b7ef50
--- /dev/null
+++ b/src/arch/riscv32/Makefile
@@ -0,0 +1,24 @@
+# Specify compressor
+#
+ZBIN = $(ZBIN32)
+
+# RISCV32-specific directories containing source files
+#
+SRCDIRS += arch/riscv32/core
+SRCDIRS += arch/riscv32/libgcc
+
+# RISCV32-specific flags
+#
+CFLAGS += -march=rv32gc -mabi=ilp32d
+ASFLAGS += -march=rv32gc -mabi=ilp32d
+LDFLAGS += -m elf32lriscv
+
+# Include common RISCV Makefile
+#
+MAKEDEPS += arch/riscv/Makefile
+include arch/riscv/Makefile
+
+# Include platform-specific Makefile
+#
+MAKEDEPS += arch/riscv32/Makefile.$(PLATFORM)
+include arch/riscv32/Makefile.$(PLATFORM)
diff --git a/src/arch/riscv32/Makefile.efi b/src/arch/riscv32/Makefile.efi
new file mode 100644
index 000000000..2cc1a930f
--- /dev/null
+++ b/src/arch/riscv32/Makefile.efi
@@ -0,0 +1,10 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# Specify EFI image builder
+#
+ELF2EFI = $(ELF2EFI32)
+
+# Include generic EFI Makefile
+#
+MAKEDEPS += arch/riscv/Makefile.efi
+include arch/riscv/Makefile.efi
diff --git a/src/arch/riscv32/Makefile.linux b/src/arch/riscv32/Makefile.linux
new file mode 100644
index 000000000..86ac6b8d5
--- /dev/null
+++ b/src/arch/riscv32/Makefile.linux
@@ -0,0 +1,14 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# Starting virtual address
+#
+LDFLAGS += -Ttext=0x10000
+
+# Compiler flags for building host API wrapper
+#
+LINUX_CFLAGS += -march=rv32gc -mabi=ilp32d
+
+# Include generic Linux Makefile
+#
+MAKEDEPS += arch/riscv/Makefile.linux
+include arch/riscv/Makefile.linux
diff --git a/src/arch/riscv32/Makefile.sbi b/src/arch/riscv32/Makefile.sbi
new file mode 100644
index 000000000..5841dd4b5
--- /dev/null
+++ b/src/arch/riscv32/Makefile.sbi
@@ -0,0 +1,14 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# Set base virtual address to 0xeb000000
+#
+# This is aligned to a 4MB boundary and so allows 4MB megapages to be
+# used to map the iPXE binary. The address pattern is also easily
+# recognisable if leaked to unexpected contexts.
+#
+LDFLAGS += --section-start=.prefix=0xeb000000
+
+# Include generic SBI Makefile
+#
+MAKEDEPS += arch/riscv/Makefile.sbi
+include arch/riscv/Makefile.sbi
diff --git a/src/arch/riscv32/core/riscv32_byteswap.S b/src/arch/riscv32/core/riscv32_byteswap.S
new file mode 100644
index 000000000..571431f35
--- /dev/null
+++ b/src/arch/riscv32/core/riscv32_byteswap.S
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * Byte swapping
+ *
+ */
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+ .section ".text.riscv_swap", "ax", @progbits
+riscv_swap:
+ .globl riscv_swap_word
+ .globl riscv_swap_half
+ .globl riscv_swap_byte
+riscv_swap_word:
+ /* Swap all bytes in a0 */
+ mv t0, ra
+ jal riscv_swap_half
+ mv ra, t0
+ /* Swap words a0 and a1 */
+ mv t1, a0
+ mv a0, a1
+ mv a1, t1
+riscv_swap_half:
+ /* Swap half-words within a0 */
+ slli t2, a0, 16
+ srli a0, a0, 16
+ or a0, a0, t2
+riscv_swap_byte:
+ /* Swap bytes within each half-word of a0 */
+ li t3, 0xff00ff00
+ slli t4, a0, 8
+ and a0, a0, t3
+ and t4, t4, t3
+ srli a0, a0, 8
+ or a0, a0, t4
+ ret
+ .size riscv_swap, . - riscv_swap
diff --git a/src/arch/riscv32/include/ipxe/efi/dhcparch.h b/src/arch/riscv32/include/ipxe/efi/dhcparch.h
new file mode 100644
index 000000000..b74df1925
--- /dev/null
+++ b/src/arch/riscv32/include/ipxe/efi/dhcparch.h
@@ -0,0 +1,20 @@
+#ifndef _IPXE_EFI_DHCPARCH_H
+#define _IPXE_EFI_DHCPARCH_H
+
+/** @file
+ *
+ * DHCP client architecture definitions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/dhcp.h>
+
+/** DHCP client architecture */
+#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_RISCV32
+
+/** DHCP client network device interface */
+#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */
+
+#endif /* _IPXE_EFI_DHCPARCH_H */
diff --git a/src/arch/riscv32/include/ipxe/sbi/dhcparch.h b/src/arch/riscv32/include/ipxe/sbi/dhcparch.h
new file mode 100644
index 000000000..713d4cf5d
--- /dev/null
+++ b/src/arch/riscv32/include/ipxe/sbi/dhcparch.h
@@ -0,0 +1,20 @@
+#ifndef _IPXE_SBI_DHCPARCH_H
+#define _IPXE_SBI_DHCPARCH_H
+
+/** @file
+ *
+ * DHCP client architecture definitions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/dhcp.h>
+
+/** DHCP client architecture */
+#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_RISCV32
+
+/** DHCP client network device interface */
+#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */
+
+#endif /* _IPXE_SBI_DHCPARCH_H */
diff --git a/src/arch/riscv32/include/limits.h b/src/arch/riscv32/include/limits.h
new file mode 100644
index 000000000..bb48b75ab
--- /dev/null
+++ b/src/arch/riscv32/include/limits.h
@@ -0,0 +1,61 @@
+#ifndef LIMITS_H
+#define LIMITS_H 1
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/* Number of bits in a `char' */
+#define CHAR_BIT 8
+
+/* Minimum and maximum values a `signed char' can hold */
+#define SCHAR_MIN (-128)
+#define SCHAR_MAX 127
+
+/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */
+#define UCHAR_MAX 255
+
+/* Minimum and maximum values a `char' can hold */
+#define CHAR_MIN SCHAR_MIN
+#define CHAR_MAX SCHAR_MAX
+
+/* Minimum and maximum values a `signed short int' can hold */
+#define SHRT_MIN (-32768)
+#define SHRT_MAX 32767
+
+/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */
+#define USHRT_MAX 65535
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MIN (-INT_MAX - 1)
+#define INT_MAX 2147483647
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MAX 2147483647
+#define INT_MIN (-INT_MAX - 1)
+
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+
+/* Minimum and maximum values a `signed long' can hold */
+#define LONG_MAX 2147483647
+#define LONG_MIN (-LONG_MAX - 1L)
+
+/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */
+#define ULONG_MAX 4294967295UL
+
+/* Minimum and maximum values a `signed long long' can hold */
+#define LLONG_MAX 9223372036854775807LL
+#define LLONG_MIN (-LONG_MAX - 1LL)
+
+
+/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */
+#define ULLONG_MAX 18446744073709551615ULL
+
+
+#endif /* LIMITS_H */
diff --git a/src/arch/riscv32/libgcc/llshift.S b/src/arch/riscv32/libgcc/llshift.S
new file mode 100644
index 000000000..8ad6a6daa
--- /dev/null
+++ b/src/arch/riscv32/libgcc/llshift.S
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * Long shifts
+ *
+ */
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+/**
+ * Shift left
+ *
+ * @v a1:a0 Value to shift
+ * @v a2 Shift amount
+ * @ret a1:a0 Shifted value
+ */
+ .section ".text.__ashldi3", "ax", @progbits
+ .globl __ashldi3
+__ashldi3:
+ /* Perform shift by 32 bits, if applicable */
+ li t0, 32
+ sub t1, t0, a2
+ bgtz t1, 1f
+ mv a1, a0
+ mv a0, zero
+1: /* Perform shift by modulo-32 bits, if applicable */
+ andi a2, a2, 0x1f
+ beqz a2, 2f
+ srl t2, a0, t1
+ sll a0, a0, a2
+ sll a1, a1, a2
+ or a1, a1, t2
+2: ret
+ .size __ashldi3, . - __ashldi3
+
+/**
+ * Logical shift right
+ *
+ * @v a1:a0 Value to shift
+ * @v a2 Shift amount
+ * @ret a1:a0 Shifted value
+ */
+ .section ".text.__lshrdi3", "ax", @progbits
+ .globl __lshrdi3
+__lshrdi3:
+ /* Perform shift by 32 bits, if applicable */
+ li t0, 32
+ sub t1, t0, a2
+ bgtz t1, 1f
+ mv a0, a1
+ mv a1, zero
+1: /* Perform shift by modulo-32 bits, if applicable */
+ andi a2, a2, 0x1f
+ beqz a2, 2f
+ sll t2, a1, t1
+ srl a1, a1, a2
+ srl a0, a0, a2
+ or a0, a0, t2
+2: ret
+ .size __lshrdi3, . - __lshrdi3
+
+/**
+ * Arithmetic shift right
+ *
+ * @v a1:a0 Value to shift
+ * @v a2 Shift amount
+ * @ret a1:a0 Shifted value
+ */
+ .section ".text.__ashrdi3", "ax", @progbits
+ .globl __ashrdi3
+__ashrdi3:
+ /* Perform shift by 32 bits, if applicable */
+ li t0, 32
+ sub t1, t0, a2
+ bgtz t1, 1f
+ mv a0, a1
+ srai a1, a1, 16
+ srai a1, a1, 16
+1: /* Perform shift by modulo-32 bits, if applicable */
+ andi a2, a2, 0x1f
+ beqz a2, 2f
+ sll t2, a1, t1
+ sra a1, a1, a2
+ srl a0, a0, a2
+ or a0, a0, t2
+2: ret
+ .size __ashrdi3, . - __ashrdi3
diff --git a/src/arch/riscv64/Makefile b/src/arch/riscv64/Makefile
new file mode 100644
index 000000000..017fbacef
--- /dev/null
+++ b/src/arch/riscv64/Makefile
@@ -0,0 +1,23 @@
+# Specify compressor
+#
+ZBIN = $(ZBIN64)
+
+# RISCV64-specific directories containing source files
+#
+SRCDIRS += arch/riscv64/core
+
+# RISCV64-specific flags
+#
+CFLAGS += -march=rv64gc -mabi=lp64d
+ASFLAGS += -march=rv64gc -mabi=lp64d
+LDFLAGS += -m elf64lriscv
+
+# Include common RISCV Makefile
+#
+MAKEDEPS += arch/riscv/Makefile
+include arch/riscv/Makefile
+
+# Include platform-specific Makefile
+#
+MAKEDEPS += arch/riscv64/Makefile.$(PLATFORM)
+include arch/riscv64/Makefile.$(PLATFORM)
diff --git a/src/arch/riscv64/Makefile.efi b/src/arch/riscv64/Makefile.efi
new file mode 100644
index 000000000..3948ca15c
--- /dev/null
+++ b/src/arch/riscv64/Makefile.efi
@@ -0,0 +1,10 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# Specify EFI image builder
+#
+ELF2EFI = $(ELF2EFI64)
+
+# Include generic EFI Makefile
+#
+MAKEDEPS += arch/riscv/Makefile.efi
+include arch/riscv/Makefile.efi
diff --git a/src/arch/riscv64/Makefile.linux b/src/arch/riscv64/Makefile.linux
new file mode 100644
index 000000000..5ceafed8c
--- /dev/null
+++ b/src/arch/riscv64/Makefile.linux
@@ -0,0 +1,10 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# Starting virtual address
+#
+LDFLAGS += -Ttext=0x10000
+
+# Include generic Linux Makefile
+#
+MAKEDEPS += arch/riscv/Makefile.linux
+include arch/riscv/Makefile.linux
diff --git a/src/arch/riscv64/Makefile.sbi b/src/arch/riscv64/Makefile.sbi
new file mode 100644
index 000000000..0f7e1c373
--- /dev/null
+++ b/src/arch/riscv64/Makefile.sbi
@@ -0,0 +1,14 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# Set base virtual address to 0xffffffffeb000000
+#
+# This is aligned to a 2MB boundary and so allows 2MB megapages to be
+# used to map the iPXE binary. The address pattern is also easily
+# recognisable if leaked to unexpected contexts.
+#
+LDFLAGS += --section-start=.prefix=0xffffffffeb000000
+
+# Include generic SBI Makefile
+#
+MAKEDEPS += arch/riscv/Makefile.sbi
+include arch/riscv/Makefile.sbi
diff --git a/src/arch/riscv64/core/riscv64_byteswap.S b/src/arch/riscv64/core/riscv64_byteswap.S
new file mode 100644
index 000000000..ec2b0b9dd
--- /dev/null
+++ b/src/arch/riscv64/core/riscv64_byteswap.S
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * Byte swapping
+ *
+ */
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+ .section ".text.riscv_swap", "ax", @progbits
+riscv_swap:
+ .globl riscv_swap_word
+ .globl riscv_swap_half
+ .globl riscv_swap_byte
+riscv_swap_word:
+ /* Swap low and high words of a0 */
+ slli t0, a0, 32
+ srli a0, a0, 32
+ or a0, a0, t0
+riscv_swap_half:
+ /* Swap half-words within each word of a0 */
+ ld t1, mask16
+ slli t2, a0, 16
+ and a0, a0, t1
+ and t2, t2, t1
+ srli a0, a0, 16
+ or a0, a0, t2
+riscv_swap_byte:
+ /* Swap bytes within each half-word of a0 */
+ ld t3, mask8
+ slli t4, a0, 8
+ and a0, a0, t3
+ and t4, t4, t3
+ srli a0, a0, 8
+ or a0, a0, t4
+ ret
+mask16: .dword 0xffff0000ffff0000
+mask8: .dword 0xff00ff00ff00ff00
+ .size riscv_swap, . - riscv_swap
diff --git a/src/arch/riscv64/include/ipxe/efi/dhcparch.h b/src/arch/riscv64/include/ipxe/efi/dhcparch.h
new file mode 100644
index 000000000..33bca044e
--- /dev/null
+++ b/src/arch/riscv64/include/ipxe/efi/dhcparch.h
@@ -0,0 +1,20 @@
+#ifndef _IPXE_EFI_DHCPARCH_H
+#define _IPXE_EFI_DHCPARCH_H
+
+/** @file
+ *
+ * DHCP client architecture definitions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/dhcp.h>
+
+/** DHCP client architecture */
+#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_RISCV64
+
+/** DHCP client network device interface */
+#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */
+
+#endif /* _IPXE_EFI_DHCPARCH_H */
diff --git a/src/arch/riscv64/include/ipxe/sbi/dhcparch.h b/src/arch/riscv64/include/ipxe/sbi/dhcparch.h
new file mode 100644
index 000000000..e172f064f
--- /dev/null
+++ b/src/arch/riscv64/include/ipxe/sbi/dhcparch.h
@@ -0,0 +1,20 @@
+#ifndef _IPXE_SBI_DHCPARCH_H
+#define _IPXE_SBI_DHCPARCH_H
+
+/** @file
+ *
+ * DHCP client architecture definitions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/dhcp.h>
+
+/** DHCP client architecture */
+#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_RISCV64
+
+/** DHCP client network device interface */
+#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */
+
+#endif /* _IPXE_SBI_DHCPARCH_H */
diff --git a/src/arch/riscv64/include/limits.h b/src/arch/riscv64/include/limits.h
new file mode 100644
index 000000000..a1374a17f
--- /dev/null
+++ b/src/arch/riscv64/include/limits.h
@@ -0,0 +1,61 @@
+#ifndef LIMITS_H
+#define LIMITS_H 1
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/* Number of bits in a `char' */
+#define CHAR_BIT 8
+
+/* Minimum and maximum values a `signed char' can hold */
+#define SCHAR_MIN (-128)
+#define SCHAR_MAX 127
+
+/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */
+#define UCHAR_MAX 255
+
+/* Minimum and maximum values a `char' can hold */
+#define CHAR_MIN SCHAR_MIN
+#define CHAR_MAX SCHAR_MAX
+
+/* Minimum and maximum values a `signed short int' can hold */
+#define SHRT_MIN (-32768)
+#define SHRT_MAX 32767
+
+/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */
+#define USHRT_MAX 65535
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MIN (-INT_MAX - 1)
+#define INT_MAX 2147483647
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MAX 2147483647
+#define INT_MIN (-INT_MAX - 1)
+
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+
+/* Minimum and maximum values a `signed long' can hold */
+#define LONG_MAX 9223372036854775807L
+#define LONG_MIN (-LONG_MAX - 1L)
+
+/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */
+#define ULONG_MAX 18446744073709551615UL
+
+/* Minimum and maximum values a `signed long long' can hold */
+#define LLONG_MAX 9223372036854775807LL
+#define LLONG_MIN (-LONG_MAX - 1LL)
+
+
+/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */
+#define ULLONG_MAX 18446744073709551615ULL
+
+
+#endif /* LIMITS_H */
diff --git a/src/arch/x86/Makefile b/src/arch/x86/Makefile
index ef801365e..4a4d8ee91 100644
--- a/src/arch/x86/Makefile
+++ b/src/arch/x86/Makefile
@@ -3,9 +3,9 @@
ASM_TCHAR := @
ASM_TCHAR_OPS := @
-# Include common x86 headers
+# Include x86-specific headers
#
-INCDIRS += arch/x86/include
+INCDIRS := arch/$(ARCH)/include arch/x86/include $(INCDIRS)
# x86-specific directories containing source files
#
diff --git a/src/arch/x86/core/cpuid.c b/src/arch/x86/core/cpuid.c
index 1a7c93e83..0461b846e 100644
--- a/src/arch/x86/core/cpuid.c
+++ b/src/arch/x86/core/cpuid.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <errno.h>
@@ -84,8 +85,8 @@ int cpuid_supported ( uint32_t function ) {
return rc;
/* Find highest supported function number within this family */
- cpuid ( ( function & CPUID_EXTENDED ), 0, &max_function, &discard_b,
- &discard_c, &discard_d );
+ cpuid ( ( function & ( CPUID_EXTENDED | CPUID_HYPERVISOR ) ), 0,
+ &max_function, &discard_b, &discard_c, &discard_d );
/* Fail if maximum function number is meaningless (e.g. if we
* are attempting to call an extended function on a CPU which
diff --git a/src/arch/x86/core/cpuid_settings.c b/src/arch/x86/core/cpuid_settings.c
index 0b67ee91d..ef0164069 100644
--- a/src/arch/x86/core/cpuid_settings.c
+++ b/src/arch/x86/core/cpuid_settings.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <errno.h>
@@ -38,7 +39,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
* Bit 31 Extended function
* Bits 30-24 (bit 22 = 1) Subfunction number
- * (bit 22 = 0) Number of consecutive functions to call, minus one
+ * Bit 30 (bit 22 = 0) Hypervisor function
+ * Bits 29-24 (bit 22 = 0) Number of consecutive functions to call, minus one
* Bit 23 Return result as little-endian (used for strings)
* Bit 22 Interpret bits 30-24 as a subfunction number
* Bits 21-18 Unused
@@ -98,7 +100,7 @@ enum cpuid_flags {
* @v tag Setting tag
* @ret function Starting function number
*/
-#define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
+#define CPUID_FUNCTION( tag ) ( (tag) & 0xc00000ffUL )
/**
* Extract subfunction number from CPUID setting tag
@@ -109,6 +111,14 @@ enum cpuid_flags {
#define CPUID_SUBFUNCTION( tag ) ( ( (tag) >> 24 ) & 0x7f )
/**
+ * Extract number of consecutive functions from CPUID setting tag
+ *
+ * @v tag Setting tag
+ * @ret num_functions Number of consecutive functions
+ */
+#define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0x3f ) + 1 )
+
+/**
* Extract register array from CPUID setting tag
*
* @v tag Setting tag
@@ -165,12 +175,13 @@ static int cpuid_settings_fetch ( struct settings *settings,
/* Call each function in turn */
function = CPUID_FUNCTION ( setting->tag );
- subfunction = CPUID_SUBFUNCTION ( setting->tag );
if ( setting->tag & CPUID_USE_SUBFUNCTION ) {
+ function &= ~CPUID_HYPERVISOR;
+ subfunction = CPUID_SUBFUNCTION ( setting->tag );
num_functions = 1;
} else {
- num_functions = ( subfunction + 1 );
subfunction = 0;
+ num_functions = CPUID_NUM_FUNCTIONS ( setting->tag );
}
for ( ; num_functions-- ; function++ ) {
@@ -240,6 +251,7 @@ static void cpuid_settings_init ( void ) {
/** CPUID settings initialiser */
struct init_fn cpuid_settings_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "cpuid",
.initialise = cpuid_settings_init,
};
diff --git a/src/arch/x86/core/debugcon.c b/src/arch/x86/core/debugcon.c
index 60de61f55..0e3a5dfc7 100644
--- a/src/arch/x86/core/debugcon.c
+++ b/src/arch/x86/core/debugcon.c
@@ -86,5 +86,6 @@ static void debugcon_init ( void ) {
* Debug port console initialisation function
*/
struct init_fn debugcon_init_fn __init_fn ( INIT_EARLY ) = {
+ .name = "debugcon",
.initialise = debugcon_init,
};
diff --git a/src/arch/x86/core/gdbmach.c b/src/arch/x86/core/gdbmach.c
index af6abfedd..d4d187e35 100644
--- a/src/arch/x86/core/gdbmach.c
+++ b/src/arch/x86/core/gdbmach.c
@@ -31,7 +31,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/uaccess.h>
#include <ipxe/gdbstub.h>
#include <librm.h>
-#include <gdbmach.h>
/** @file
*
diff --git a/src/arch/x86/core/pci_autoboot.c b/src/arch/x86/core/pci_autoboot.c
index 337598091..243e45026 100644
--- a/src/arch/x86/core/pci_autoboot.c
+++ b/src/arch/x86/core/pci_autoboot.c
@@ -44,5 +44,6 @@ static void pci_autoboot_init ( void ) {
/** PCI autoboot device initialisation function */
struct init_fn pci_autoboot_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "autoboot",
.initialise = pci_autoboot_init,
};
diff --git a/src/arch/x86/core/pcidirect.c b/src/arch/x86/core/pcidirect.c
index f4659a1ac..887b78a0b 100644
--- a/src/arch/x86/core/pcidirect.c
+++ b/src/arch/x86/core/pcidirect.c
@@ -45,6 +45,7 @@ void pcidirect_prepare ( struct pci_device *pci, int where ) {
PCIDIRECT_CONFIG_ADDRESS );
}
+PROVIDE_PCIAPI_INLINE ( direct, pci_can_probe );
PROVIDE_PCIAPI_INLINE ( direct, pci_discover );
PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_byte );
PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_word );
@@ -53,5 +54,4 @@ PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_byte );
PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_word );
PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_dword );
PROVIDE_PCIAPI_INLINE ( direct, pci_ioremap );
-
-struct pci_api pcidirect_api = PCIAPI_RUNTIME ( direct );
+PROVIDE_PCIAPI_RUNTIME ( direct, PCIAPI_PRIORITY_DIRECT );
diff --git a/src/arch/x86/core/rdrand.c b/src/arch/x86/core/rdrand.c
index 850ab1f11..05fc3cd23 100644
--- a/src/arch/x86/core/rdrand.c
+++ b/src/arch/x86/core/rdrand.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/arch/x86/core/relocate.c b/src/arch/x86/core/relocate.c
index 765d46560..3cdc53c2e 100644
--- a/src/arch/x86/core/relocate.c
+++ b/src/arch/x86/core/relocate.c
@@ -1,4 +1,5 @@
-#include <ipxe/io.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/memmap.h>
#include <registers.h>
/*
@@ -41,82 +42,73 @@ extern char _etextdata[];
* to the prefix in %edi.
*/
__asmcall void relocate ( struct i386_all_regs *ix86 ) {
- struct memory_map memmap;
- uint32_t start, end, size, padded_size, max;
- uint32_t new_start, new_end;
- unsigned i;
+ struct memmap_region region;
+ physaddr_t start, end, max;
+ physaddr_t new_start, new_end;
+ physaddr_t r_start, r_end;
+ size_t size, padded_size;
- /* Get memory map and current location */
- get_memmap ( &memmap );
+ /* Show whole memory map (for debugging) */
+ memmap_dump_all ( 0 );
+
+ /* Get current location */
start = virt_to_phys ( _textdata );
end = virt_to_phys ( _etextdata );
size = ( end - start );
padded_size = ( size + ALIGN - 1 );
- DBG ( "Relocate: currently at [%x,%x)\n"
- "...need %x bytes for %d-byte alignment\n",
- start, end, padded_size, ALIGN );
+ DBGC ( &region, "Relocate: currently at [%#08lx,%#08lx)\n"
+ "...need %#zx bytes for %d-byte alignment\n",
+ start, end, padded_size, ALIGN );
/* Determine maximum usable address */
max = MAX_ADDR;
if ( ix86->regs.ebp < max ) {
max = ix86->regs.ebp;
- DBG ( "Limiting relocation to [0,%x)\n", max );
+ DBGC ( &region, "Limiting relocation to [0,%#08lx)\n", max );
}
/* Walk through the memory map and find the highest address
- * below 4GB that iPXE will fit into.
+ * above the current iPXE and below 4GB that iPXE will fit
+ * into.
*/
new_end = end;
- for ( i = 0 ; i < memmap.count ; i++ ) {
- struct memory_region *region = &memmap.regions[i];
- uint32_t r_start, r_end;
+ for_each_memmap_from ( &region, end, 0 ) {
- DBG ( "Considering [%llx,%llx)\n", region->start, region->end);
-
/* Truncate block to maximum address. This will be
- * less than 4GB, which means that we can get away
- * with using just 32-bit arithmetic after this stage.
+ * strictly less than 4GB, which means that we can get
+ * away with using just 32-bit arithmetic after this
+ * stage.
*/
- if ( region->start > max ) {
- DBG ( "...starts after max=%x\n", max );
+ DBGC_MEMMAP ( &region, &region );
+ if ( region.min > max ) {
+ DBGC ( &region, "...starts after max=%#08lx\n", max );
+ break;
+ }
+ r_start = region.min;
+ if ( ! memmap_is_usable ( &region ) ) {
+ DBGC ( &region, "...not usable\n" );
continue;
}
- r_start = region->start;
- if ( region->end > max ) {
- DBG ( "...end truncated to max=%x\n", max );
+ r_end = ( r_start + memmap_size ( &region ) );
+ if ( ( r_end == 0 ) || ( r_end > max ) ) {
+ DBGC ( &region, "...end truncated to max=%#08lx\n",
+ max );
r_end = max;
- } else {
- r_end = region->end;
- }
- DBG ( "...usable portion is [%x,%x)\n", r_start, r_end );
-
- /* If we have rounded down r_end below r_ start, skip
- * this block.
- */
- if ( r_end < r_start ) {
- DBG ( "...truncated to negative size\n" );
- continue;
}
+ DBGC ( &region, "...usable portion is [%#08lx,%#08lx)\n",
+ r_start, r_end );
/* Check that there is enough space to fit in iPXE */
- if ( ( r_end - r_start ) < size ) {
- DBG ( "...too small (need %x bytes)\n", size );
+ if ( ( r_end - r_start ) < padded_size ) {
+ DBGC ( &region, "...too small (need %#zx bytes)\n",
+ padded_size );
continue;
}
- /* If the start address of the iPXE we would
- * place in this block is higher than the end address
- * of the current highest block, use this block.
- *
- * Note that this avoids overlaps with the current
- * iPXE, as well as choosing the highest of all viable
- * blocks.
- */
- if ( ( r_end - size ) > new_end ) {
- new_end = r_end;
- DBG ( "...new best block found.\n" );
- }
+ /* Use highest block with enough space */
+ new_end = r_end;
+ DBGC ( &region, "...new best block found.\n" );
}
/* Calculate new location of iPXE, and align it to the
@@ -126,9 +118,9 @@ __asmcall void relocate ( struct i386_all_regs *ix86 ) {
new_start += ( ( start - new_start ) & ( ALIGN - 1 ) );
new_end = new_start + size;
- DBG ( "Relocating from [%x,%x) to [%x,%x)\n",
- start, end, new_start, new_end );
-
+ DBGC ( &region, "Relocating from [%#08lx,%#08lx) to [%#08lx,%#08lx)\n",
+ start, end, new_start, new_end );
+
/* Let prefix know what to copy */
ix86->regs.esi = start;
ix86->regs.edi = new_start;
diff --git a/src/arch/x86/core/runtime.c b/src/arch/x86/core/runtime.c
index 02072b5bf..86083b1f9 100644
--- a/src/arch/x86/core/runtime.c
+++ b/src/arch/x86/core/runtime.c
@@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
@@ -69,6 +70,7 @@ static void cmdline_image_free ( struct refcnt *refcnt ) {
struct image *image = container_of ( refcnt, struct image, refcnt );
DBGC ( image, "RUNTIME freeing command line\n" );
+ free_image ( refcnt );
free ( cmdline_copy );
}
@@ -76,6 +78,7 @@ static void cmdline_image_free ( struct refcnt *refcnt ) {
static struct image cmdline_image = {
.refcnt = REF_INIT ( cmdline_image_free ),
.name = "<CMDLINE>",
+ .flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ),
.type = &script_image_type,
};
@@ -114,9 +117,7 @@ static void cmdline_strip ( char *cmdline, const char *cruft ) {
* @ret rc Return status code
*/
static int cmdline_init ( void ) {
- userptr_t cmdline_user;
char *cmdline;
- size_t len;
int rc;
/* Do nothing if no command line was specified */
@@ -124,19 +125,15 @@ static int cmdline_init ( void ) {
DBGC ( colour, "RUNTIME found no command line\n" );
return 0;
}
- cmdline_user = phys_to_user ( cmdline_phys );
- len = ( strlen_user ( cmdline_user, 0 ) + 1 /* NUL */ );
/* Allocate and copy command line */
- cmdline_copy = malloc ( len );
+ cmdline_copy = strdup ( phys_to_virt ( cmdline_phys ) );
if ( ! cmdline_copy ) {
- DBGC ( colour, "RUNTIME could not allocate %zd bytes for "
- "command line\n", len );
+ DBGC ( colour, "RUNTIME could not allocate command line\n" );
rc = -ENOMEM;
goto err_alloc_cmdline_copy;
}
cmdline = cmdline_copy;
- copy_from_user ( cmdline, cmdline_user, 0, len );
DBGC ( colour, "RUNTIME found command line \"%s\" at %08x\n",
cmdline, cmdline_phys );
@@ -151,7 +148,7 @@ static int cmdline_init ( void ) {
DBGC ( colour, "RUNTIME using command line \"%s\"\n", cmdline );
/* Prepare and register image */
- cmdline_image.data = virt_to_user ( cmdline );
+ cmdline_image.data = cmdline;
cmdline_image.len = strlen ( cmdline );
if ( cmdline_image.len ) {
if ( ( rc = register_image ( &cmdline_image ) ) != 0 ) {
@@ -193,7 +190,7 @@ static int initrd_init ( void ) {
initrd_phys, ( initrd_phys + initrd_len ) );
/* Create initrd image */
- image = image_memory ( "<INITRD>", phys_to_user ( initrd_phys ),
+ image = image_memory ( "<INITRD>", phys_to_virt ( initrd_phys ),
initrd_len );
if ( ! image ) {
DBGC ( colour, "RUNTIME could not create initrd image\n" );
diff --git a/src/arch/x86/core/video_subr.c b/src/arch/x86/core/video_subr.c
index f5cc4cdd4..4e9ef466f 100644
--- a/src/arch/x86/core/video_subr.c
+++ b/src/arch/x86/core/video_subr.c
@@ -109,5 +109,6 @@ struct console_driver vga_console __console_driver = {
};
struct init_fn video_init_fn __init_fn ( INIT_EARLY ) = {
+ .name = "video",
.initialise = video_init,
};
diff --git a/src/arch/x86/core/vram_settings.c b/src/arch/x86/core/vram_settings.c
index 9c169b40c..a97a463fe 100644
--- a/src/arch/x86/core/vram_settings.c
+++ b/src/arch/x86/core/vram_settings.c
@@ -23,6 +23,7 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+#include <string.h>
#include <ipxe/uaccess.h>
#include <ipxe/settings.h>
@@ -47,12 +48,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @ret len Length of setting data, or negative error
*/
static int vram_fetch ( void *data, size_t len ) {
- userptr_t vram = phys_to_user ( VRAM_BASE );
+ const void *vram = phys_to_virt ( VRAM_BASE );
/* Copy video RAM */
if ( len > VRAM_LEN )
len = VRAM_LEN;
- copy_from_user ( data, vram, 0, len );
+ memcpy ( data, vram, len );
return VRAM_LEN;
}
diff --git a/src/arch/x86/core/x86_bigint.c b/src/arch/x86/core/x86_bigint.c
deleted file mode 100644
index 74e5da9a2..000000000
--- a/src/arch/x86/core/x86_bigint.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <stdint.h>
-#include <string.h>
-#include <ipxe/bigint.h>
-
-/** @file
- *
- * Big integer support
- */
-
-/**
- * Multiply big integers
- *
- * @v multiplicand0 Element 0 of big integer to be multiplied
- * @v multiplicand_size Number of elements in multiplicand
- * @v multiplier0 Element 0 of big integer to be multiplied
- * @v multiplier_size Number of elements in multiplier
- * @v result0 Element 0 of big integer to hold result
- */
-void bigint_multiply_raw ( const uint32_t *multiplicand0,
- unsigned int multiplicand_size,
- const uint32_t *multiplier0,
- unsigned int multiplier_size,
- uint32_t *result0 ) {
- unsigned int result_size = ( multiplicand_size + multiplier_size );
- const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
- *multiplicand = ( ( const void * ) multiplicand0 );
- const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
- *multiplier = ( ( const void * ) multiplier0 );
- bigint_t ( result_size ) __attribute__ (( may_alias ))
- *result = ( ( void * ) result0 );
- unsigned int i;
- unsigned int j;
- uint32_t multiplicand_element;
- uint32_t multiplier_element;
- uint32_t *result_elements;
- uint32_t discard_a;
- uint32_t discard_d;
- long index;
-
- /* Zero result */
- memset ( result, 0, sizeof ( *result ) );
-
- /* Multiply integers one element at a time */
- for ( i = 0 ; i < multiplicand_size ; i++ ) {
- multiplicand_element = multiplicand->element[i];
- for ( j = 0 ; j < multiplier_size ; j++ ) {
- multiplier_element = multiplier->element[j];
- result_elements = &result->element[ i + j ];
- /* Perform a single multiply, and add the
- * resulting double-element into the result,
- * carrying as necessary. The carry can
- * never overflow beyond the end of the
- * result, since:
- *
- * a < 2^{n}, b < 2^{m} => ab < 2^{n+m}
- */
- __asm__ __volatile__ ( "mull %5\n\t"
- "addl %%eax, (%6,%2,4)\n\t"
- "adcl %%edx, 4(%6,%2,4)\n\t"
- "\n1:\n\t"
- "adcl $0, 8(%6,%2,4)\n\t"
- "inc %2\n\t"
- /* Does not affect CF */
- "jc 1b\n\t"
- : "=&a" ( discard_a ),
- "=&d" ( discard_d ),
- "=&r" ( index ),
- "+m" ( *result )
- : "0" ( multiplicand_element ),
- "g" ( multiplier_element ),
- "r" ( result_elements ),
- "2" ( 0 ) );
- }
- }
-}
diff --git a/src/arch/x86/core/x86_io.c b/src/arch/x86/core/x86_io.c
index 6c6b6e1e7..270ed7bef 100644
--- a/src/arch/x86/core/x86_io.c
+++ b/src/arch/x86/core/x86_io.c
@@ -32,6 +32,69 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
+/** Threshold for port I/O-mapped addresses
+ *
+ * On x86, port I/O instructions (inb/outb/etc) can take only an 8-bit
+ * or 16-bit address (in %dx). All I/O ports must therefore have a
+ * value in the first 64kB of the address space.
+ *
+ * Virtual addresses below 64kB can never be MMIO addresses:
+ *
+ * - In the UEFI memory model and x86_64 BIOS memory model, virtual
+ * addresses below 64kB are identity-mapped to the corresponding
+ * physical address. Since the first 64kB of address space is
+ * always RAM, no MMIO device can exist within this region.
+ *
+ * - In the i386 BIOS memory model, virtual addresses below 64kB cover
+ * the iPXE binary itself (which starts at address zero). Since the
+ * size of .textdata can never realistically be below 64kB (not
+ * least since the heap alone is 512kB), and since iPXE is placed
+ * into RAM as a contiguous block, no MMIO device can exist within
+ * this region.
+ *
+ * We therefore know that any (virtual) address returned by ioremap()
+ * must be outside the first 64kB of the address space. We can
+ * therefore use this as a threshold to determine whether a given
+ * address is a port I/O address or an MMIO address.
+ */
+#define PIO_THRESHOLD 0x10000
+
+/**
+ * Read from I/O-mapped or memory-mapped device
+ *
+ * @v io_addr I/O address
+ * @ret data Value read
+ */
+#define X86_IOREADX( _api_func, _suffix, _type ) \
+static _type x86_ ## _api_func ( volatile _type *io_addr ) { \
+ if ( ( ( intptr_t ) io_addr ) < PIO_THRESHOLD ) { \
+ return in ## _suffix ( io_addr ); \
+ } else { \
+ return read ## _suffix ( io_addr ); \
+ } \
+}
+X86_IOREADX ( ioread8, b, uint8_t );
+X86_IOREADX ( ioread16, w, uint16_t );
+X86_IOREADX ( ioread32, l, uint32_t );
+
+/**
+ * Write to I/O-mapped or memory-mapped device
+ *
+ * @v data Value to write
+ * @v io_addr I/O address
+ */
+#define X86_IOWRITEX( _api_func, _suffix, _type ) \
+static void x86_ ## _api_func ( _type data, volatile _type *io_addr ) { \
+ if ( ( ( intptr_t ) io_addr ) < PIO_THRESHOLD ) { \
+ out ## _suffix ( data, io_addr ); \
+ } else { \
+ write ## _suffix ( data, io_addr ); \
+ } \
+}
+X86_IOWRITEX ( iowrite8, b, uint8_t );
+X86_IOWRITEX ( iowrite16, w, uint16_t );
+X86_IOWRITEX ( iowrite32, l, uint32_t );
+
/**
* Read 64-bit qword from memory-mapped device
*
@@ -101,3 +164,9 @@ PROVIDE_IOAPI_INLINE ( x86, writeq );
PROVIDE_IOAPI ( x86, readq, i386_readq );
PROVIDE_IOAPI ( x86, writeq, i386_writeq );
#endif
+PROVIDE_IOAPI ( x86, ioread8, x86_ioread8 );
+PROVIDE_IOAPI ( x86, ioread16, x86_ioread16 );
+PROVIDE_IOAPI ( x86, ioread32, x86_ioread32 );
+PROVIDE_IOAPI ( x86, iowrite8, x86_iowrite8 );
+PROVIDE_IOAPI ( x86, iowrite16, x86_iowrite16 );
+PROVIDE_IOAPI ( x86, iowrite32, x86_iowrite32 );
diff --git a/src/arch/x86/core/x86_string.c b/src/arch/x86/core/x86_string.c
index 1a1e79dac..923552f66 100644
--- a/src/arch/x86/core/x86_string.c
+++ b/src/arch/x86/core/x86_string.c
@@ -28,6 +28,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <config/defaults.h>
diff --git a/src/arch/x86/core/x86_tcpip.c b/src/arch/x86/core/x86_tcpip.c
index ed323d5d0..b3bfe2546 100644
--- a/src/arch/x86/core/x86_tcpip.c
+++ b/src/arch/x86/core/x86_tcpip.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/arch/x86/core/x86_uart.c b/src/arch/x86/core/x86_uart.c
index e455775bf..03809ff9b 100644
--- a/src/arch/x86/core/x86_uart.c
+++ b/src/arch/x86/core/x86_uart.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -29,41 +29,47 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
-#include <errno.h>
-#include <ipxe/uart.h>
+#include <string.h>
+#include <ipxe/serial.h>
+#include <ipxe/ns16550.h>
-/** UART port bases */
-static uint16_t uart_base[] = {
- [COM1] = 0x3f8,
- [COM2] = 0x2f8,
- [COM3] = 0x3e8,
- [COM4] = 0x2e8,
-};
+/** Define a fixed ISA UART */
+#define ISA_UART( NAME, BASE ) \
+ static struct ns16550_uart ns16550_ ## NAME = { \
+ .base = ( ( void * ) (BASE) ), \
+ .clock = NS16550_CLK_DEFAULT, \
+ }; \
+ struct uart NAME = { \
+ .refcnt = REF_INIT ( ref_no_free ), \
+ .name = #NAME, \
+ .op = &ns16550_operations, \
+ .priv = &ns16550_ ## NAME, \
+ }
+
+/* Fixed ISA UARTs */
+ISA_UART ( com1, COM1_BASE );
+ISA_UART ( com2, COM2_BASE );
+ISA_UART ( com3, COM3_BASE );
+ISA_UART ( com4, COM4_BASE );
/**
- * Select UART port
+ * Register fixed ISA UARTs
*
- * @v uart UART
- * @v port Port number, or 0 to disable
* @ret rc Return status code
*/
-int uart_select ( struct uart *uart, unsigned int port ) {
+int uart_register_fixed ( void ) {
+ static struct uart *ports[] = { COM1, COM2, COM3, COM4 };
+ unsigned int i;
int rc;
- /* Set new UART base */
- if ( port >= ( sizeof ( uart_base ) / sizeof ( uart_base[0] ) ) ) {
- rc = -ENODEV;
- goto err;
+ /* Register all fixed ISA UARTs */
+ for ( i = 0 ; i < ( sizeof ( ports ) / sizeof ( ports[0] ) ) ; i++ ) {
+ if ( ( rc = uart_register ( ports[i] ) ) != 0 ) {
+ DBGC ( ports[i], "UART could not register %s: %s\n",
+ ports[i]->name, strerror ( rc ) );
+ return rc;
+ }
}
- uart->base = ( ( void * ) ( intptr_t ) uart_base[port] );
-
- /* Check that UART exists */
- if ( ( rc = uart_exists ( uart ) ) != 0 )
- goto err;
return 0;
-
- err:
- uart->base = NULL;
- return rc;
}
diff --git a/src/arch/x86/drivers/net/undiisr.S b/src/arch/x86/drivers/net/undiisr.S
index 8ba5c5354..0b07cb396 100644
--- a/src/arch/x86/drivers/net/undiisr.S
+++ b/src/arch/x86/drivers/net/undiisr.S
@@ -33,8 +33,16 @@ undiisr:
/* Check that we have an UNDI entry point */
cmpw $0, undinet_entry_point
je chain
-
+
+ /* Mask interrupt and set rearm flag */
+ movw undiisr_imr, %dx
+ inb %dx, %al
+ orb undiisr_bit, %al
+ outb %al, %dx
+ movb %al, undiisr_rearm
+
/* Issue UNDI API call */
+ movw %ds, %ax
movw %ax, %es
movw $undinet_params, %di
movw $PXENV_UNDI_ISR, %bx
diff --git a/src/arch/x86/drivers/net/undinet.c b/src/arch/x86/drivers/net/undinet.c
index 43cb18bfe..f4f78432a 100644
--- a/src/arch/x86/drivers/net/undinet.c
+++ b/src/arch/x86/drivers/net/undinet.c
@@ -373,6 +373,18 @@ extern void undiisr ( void );
uint8_t __data16 ( undiisr_irq );
#define undiisr_irq __use_data16 ( undiisr_irq )
+/** IRQ mask register */
+uint16_t __data16 ( undiisr_imr );
+#define undiisr_imr __use_data16 ( undiisr_imr )
+
+/** IRQ mask bit */
+uint8_t __data16 ( undiisr_bit );
+#define undiisr_bit __use_data16 ( undiisr_bit )
+
+/** IRQ rearm flag */
+uint8_t __data16 ( undiisr_rearm );
+#define undiisr_rearm __use_data16 ( undiisr_rearm )
+
/** IRQ chain vector */
struct segoff __data16 ( undiisr_next_handler );
#define undiisr_next_handler __use_data16 ( undiisr_next_handler )
@@ -395,6 +407,9 @@ static void undinet_hook_isr ( unsigned int irq ) {
assert ( undiisr_irq == 0 );
undiisr_irq = irq;
+ undiisr_imr = IMR_REG ( irq );
+ undiisr_bit = IMR_BIT ( irq );
+ undiisr_rearm = 0;
hook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ),
&undiisr_next_handler );
}
@@ -588,6 +603,14 @@ static void undinet_poll ( struct net_device *netdev ) {
* support interrupts.
*/
if ( ! undinet_isr_triggered() ) {
+
+ /* Rearm interrupt if needed */
+ if ( undiisr_rearm ) {
+ undiisr_rearm = 0;
+ assert ( undinic->irq != 0 );
+ enable_irq ( undinic->irq );
+ }
+
/* Allow interrupt to occur */
profile_start ( &undinet_irq_profiler );
__asm__ __volatile__ ( "sti\n\t"
@@ -838,15 +861,19 @@ static const struct undinet_irq_broken undinet_irq_broken_list[] = {
{ 0x8086, 0x1503, PCI_ANY_ID, PCI_ANY_ID },
/* HP 745 G3 laptop */
{ 0x14e4, 0x1687, PCI_ANY_ID, PCI_ANY_ID },
+ /* ASUSTeK KNPA-U16 server */
+ { 0x8086, 0x1521, 0x1043, PCI_ANY_ID },
};
/**
* Check for devices with broken support for generating interrupts
*
- * @v desc Device description
+ * @v netdev Net device
* @ret irq_is_broken Interrupt support is broken; no interrupts are generated
*/
-static int undinet_irq_is_broken ( struct device_description *desc ) {
+static int undinet_irq_is_broken ( struct net_device *netdev ) {
+ struct undi_nic *undinic = netdev->priv;
+ struct device_description *desc = &netdev->dev->desc;
const struct undinet_irq_broken *broken;
struct pci_device pci;
uint16_t subsys_vendor;
@@ -872,9 +899,25 @@ static int undinet_irq_is_broken ( struct device_description *desc ) {
( broken->pci_subsys_vendor == PCI_ANY_ID ) ) &&
( ( broken->pci_subsys == subsys ) ||
( broken->pci_subsys == PCI_ANY_ID ) ) ) {
+ DBGC ( undinic, "UNDINIC %p %04x:%04x subsys "
+ "%04x:%04x has broken interrupts\n",
+ undinic, desc->vendor, desc->device,
+ subsys_vendor, subsys );
return 1;
}
}
+
+ /* Check for a PCI Express capability. Given the number of
+ * issues found with legacy INTx emulation on PCIe systems, we
+ * assume that there is a high chance of interrupts not
+ * working on any PCIe device.
+ */
+ if ( pci_find_capability ( &pci, PCI_CAP_ID_EXP ) ) {
+ DBGC ( undinic, "UNDINIC %p is PCI Express: assuming "
+ "interrupts are unreliable\n", undinic );
+ return 1;
+ }
+
return 0;
}
@@ -972,6 +1015,10 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) {
}
DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n",
undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
+ if ( undinic->irq ) {
+ /* Sanity check - prefix should have disabled the IRQ */
+ assert ( ! irq_enabled ( undinic->irq ) );
+ }
/* Get interface information */
memset ( &undi_iface, 0, sizeof ( undi_iface ) );
@@ -993,7 +1040,7 @@ int undinet_probe ( struct undi_device *undi, struct device *dev ) {
undinic );
undinic->hacks |= UNDI_HACK_EB54;
}
- if ( undinet_irq_is_broken ( &dev->desc ) ) {
+ if ( undinet_irq_is_broken ( netdev ) ) {
DBGC ( undinic, "UNDINIC %p forcing polling mode due to "
"broken interrupts\n", undinic );
undinic->irq_supported = 0;
diff --git a/src/arch/x86/drivers/xen/hvm.c b/src/arch/x86/drivers/xen/hvm.c
index b77cdd14c..cf41cc955 100644
--- a/src/arch/x86/drivers/xen/hvm.c
+++ b/src/arch/x86/drivers/xen/hvm.c
@@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdio.h>
+#include <string.h>
#include <errno.h>
#include <ipxe/malloc.h>
#include <ipxe/pci.h>
diff --git a/src/arch/x86/hci/commands/cpuid_cmd.c b/src/arch/x86/hci/commands/cpuid_cmd.c
index d73ce2a3e..f4d7305e8 100644
--- a/src/arch/x86/hci/commands/cpuid_cmd.c
+++ b/src/arch/x86/hci/commands/cpuid_cmd.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdio.h>
@@ -95,7 +96,4 @@ static int cpuid_exec ( int argc, char **argv ) {
}
/** x86 CPU feature detection command */
-struct command cpuid_command __command = {
- .name = "cpuid",
- .exec = cpuid_exec,
-};
+COMMAND ( cpuid, cpuid_exec );
diff --git a/src/arch/x86/hci/commands/pxe_cmd.c b/src/arch/x86/hci/commands/pxe_cmd.c
index 473b97f97..cf1a36ed6 100644
--- a/src/arch/x86/hci/commands/pxe_cmd.c
+++ b/src/arch/x86/hci/commands/pxe_cmd.c
@@ -105,13 +105,5 @@ static int stoppxe_exec ( int argc __unused, char **argv __unused ) {
}
/** PXE commands */
-struct command pxe_commands[] __command = {
- {
- .name = "startpxe",
- .exec = startpxe_exec,
- },
- {
- .name = "stoppxe",
- .exec = stoppxe_exec,
- },
-};
+COMMAND ( startpxe, startpxe_exec );
+COMMAND ( stoppxe, stoppxe_exec );
diff --git a/src/arch/x86/image/bzimage.c b/src/arch/x86/image/bzimage.c
index 2c776147d..16a47fc57 100644
--- a/src/arch/x86/image/bzimage.c
+++ b/src/arch/x86/image/bzimage.c
@@ -32,12 +32,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <realmode.h>
#include <bzimage.h>
-#include <initrd.h>
+#include <ipxe/initrd.h>
#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/segment.h>
@@ -56,7 +57,7 @@ struct bzimage_context {
/** Real-mode kernel portion load segment address */
unsigned int rm_kernel_seg;
/** Real-mode kernel portion load address */
- userptr_t rm_kernel;
+ void *rm_kernel;
/** Real-mode kernel portion file size */
size_t rm_filesz;
/** Real-mode heap top (offset from rm_kernel) */
@@ -68,7 +69,7 @@ struct bzimage_context {
/** Real-mode kernel portion total memory size */
size_t rm_memsz;
/** Non-real-mode kernel portion load address */
- userptr_t pm_kernel;
+ void *pm_kernel;
/** Non-real-mode kernel portion file and memory size */
size_t pm_sz;
/** Video mode */
@@ -76,14 +77,9 @@ struct bzimage_context {
/** Memory limit */
uint64_t mem_limit;
/** Initrd address */
- physaddr_t ramdisk_image;
+ void *initrd;
/** Initrd size */
- physaddr_t ramdisk_size;
-
- /** Command line magic block */
- struct bzimage_cmdline cmdline_magic;
- /** bzImage header */
- struct bzimage_header bzhdr;
+ physaddr_t initrd_size;
};
/**
@@ -91,35 +87,31 @@ struct bzimage_context {
*
* @v image bzImage file
* @v bzimg bzImage context
- * @v src bzImage to parse
* @ret rc Return status code
*/
static int bzimage_parse_header ( struct image *image,
- struct bzimage_context *bzimg,
- userptr_t src ) {
+ struct bzimage_context *bzimg ) {
+ const struct bzimage_header *bzhdr;
unsigned int syssize;
int is_bzimage;
+ /* Initialise context */
+ memset ( bzimg, 0, sizeof ( *bzimg ) );
+
/* Sanity check */
- if ( image->len < ( BZI_HDR_OFFSET + sizeof ( bzimg->bzhdr ) ) ) {
- DBGC ( image, "bzImage %p too short for kernel header\n",
- image );
+ if ( image->len < ( BZI_HDR_OFFSET + sizeof ( *bzhdr ) ) ) {
+ DBGC ( image, "bzImage %s too short for kernel header\n",
+ image->name );
return -ENOEXEC;
}
-
- /* Read in header structures */
- memset ( bzimg, 0, sizeof ( *bzimg ) );
- copy_from_user ( &bzimg->cmdline_magic, src, BZI_CMDLINE_OFFSET,
- sizeof ( bzimg->cmdline_magic ) );
- copy_from_user ( &bzimg->bzhdr, src, BZI_HDR_OFFSET,
- sizeof ( bzimg->bzhdr ) );
+ bzhdr = ( image->data + BZI_HDR_OFFSET );
/* Calculate size of real-mode portion */
- bzimg->rm_filesz = ( ( ( bzimg->bzhdr.setup_sects ?
- bzimg->bzhdr.setup_sects : 4 ) + 1 ) << 9 );
+ bzimg->rm_filesz = ( ( ( bzhdr->setup_sects ?
+ bzhdr->setup_sects : 4 ) + 1 ) << 9 );
if ( bzimg->rm_filesz > image->len ) {
- DBGC ( image, "bzImage %p too short for %zd byte of setup\n",
- image, bzimg->rm_filesz );
+ DBGC ( image, "bzImage %s too short for %zd byte of setup\n",
+ image->name, bzimg->rm_filesz );
return -ENOEXEC;
}
bzimg->rm_memsz = BZI_ASSUMED_RM_SIZE;
@@ -129,13 +121,14 @@ static int bzimage_parse_header ( struct image *image,
syssize = ( ( bzimg->pm_sz + 15 ) / 16 );
/* Check for signatures and determine version */
- if ( bzimg->bzhdr.boot_flag != BZI_BOOT_FLAG ) {
- DBGC ( image, "bzImage %p missing 55AA signature\n", image );
+ if ( bzhdr->boot_flag != BZI_BOOT_FLAG ) {
+ DBGC ( image, "bzImage %s missing 55AA signature\n",
+ image->name );
return -ENOEXEC;
}
- if ( bzimg->bzhdr.header == BZI_SIGNATURE ) {
+ if ( bzhdr->header == BZI_SIGNATURE ) {
/* 2.00+ */
- bzimg->version = bzimg->bzhdr.version;
+ bzimg->version = bzhdr->version;
} else {
/* Pre-2.00. Check that the syssize field is correct,
* as a guard against accepting arbitrary binary data,
@@ -145,20 +138,21 @@ static int bzimage_parse_header ( struct image *image,
* check this field.
*/
bzimg->version = 0x0100;
- if ( bzimg->bzhdr.syssize != syssize ) {
- DBGC ( image, "bzImage %p bad syssize %x (expected "
- "%x)\n", image, bzimg->bzhdr.syssize, syssize );
+ if ( bzhdr->syssize != syssize ) {
+ DBGC ( image, "bzImage %s bad syssize %x (expected "
+ "%x)\n", image->name, bzhdr->syssize,
+ syssize );
return -ENOEXEC;
}
}
/* Determine image type */
is_bzimage = ( ( bzimg->version >= 0x0200 ) ?
- ( bzimg->bzhdr.loadflags & BZI_LOAD_HIGH ) : 0 );
+ ( bzhdr->loadflags & BZI_LOAD_HIGH ) : 0 );
/* Calculate load address of real-mode portion */
bzimg->rm_kernel_seg = ( is_bzimage ? 0x1000 : 0x9000 );
- bzimg->rm_kernel = real_to_user ( bzimg->rm_kernel_seg, 0 );
+ bzimg->rm_kernel = real_to_virt ( bzimg->rm_kernel_seg, 0 );
/* Allow space for the stack and heap */
bzimg->rm_memsz += BZI_STACK_SIZE;
@@ -169,24 +163,24 @@ static int bzimage_parse_header ( struct image *image,
bzimg->rm_memsz += BZI_CMDLINE_SIZE;
/* Calculate load address of protected-mode portion */
- bzimg->pm_kernel = phys_to_user ( is_bzimage ? BZI_LOAD_HIGH_ADDR
+ bzimg->pm_kernel = phys_to_virt ( is_bzimage ? BZI_LOAD_HIGH_ADDR
: BZI_LOAD_LOW_ADDR );
/* Extract video mode */
- bzimg->vid_mode = bzimg->bzhdr.vid_mode;
+ bzimg->vid_mode = bzhdr->vid_mode;
/* Extract memory limit */
bzimg->mem_limit = ( ( bzimg->version >= 0x0203 ) ?
- bzimg->bzhdr.initrd_addr_max : BZI_INITRD_MAX );
+ bzhdr->initrd_addr_max : BZI_INITRD_MAX );
/* Extract command line size */
bzimg->cmdline_size = ( ( bzimg->version >= 0x0206 ) ?
- bzimg->bzhdr.cmdline_size : BZI_CMDLINE_SIZE );
+ bzhdr->cmdline_size : BZI_CMDLINE_SIZE );
- DBGC ( image, "bzImage %p version %04x RM %#lx+%#zx PM %#lx+%#zx "
- "cmdlen %zd\n", image, bzimg->version,
- user_to_phys ( bzimg->rm_kernel, 0 ), bzimg->rm_filesz,
- user_to_phys ( bzimg->pm_kernel, 0 ), bzimg->pm_sz,
+ DBGC ( image, "bzImage %s version %04x RM %#lx+%#zx PM %#lx+%#zx "
+ "cmdlen %zd\n", image->name, bzimg->version,
+ virt_to_phys ( bzimg->rm_kernel ), bzimg->rm_filesz,
+ virt_to_phys ( bzimg->pm_kernel ), bzimg->pm_sz,
bzimg->cmdline_size );
return 0;
@@ -197,49 +191,44 @@ static int bzimage_parse_header ( struct image *image,
*
* @v image bzImage file
* @v bzimg bzImage context
- * @v dst bzImage to update
*/
static void bzimage_update_header ( struct image *image,
- struct bzimage_context *bzimg,
- userptr_t dst ) {
+ struct bzimage_context *bzimg ) {
+ struct bzimage_header *bzhdr = ( bzimg->rm_kernel + BZI_HDR_OFFSET );
+ struct bzimage_cmdline *cmdline;
/* Set loader type */
if ( bzimg->version >= 0x0200 )
- bzimg->bzhdr.type_of_loader = BZI_LOADER_TYPE_IPXE;
+ bzhdr->type_of_loader = BZI_LOADER_TYPE_IPXE;
/* Set heap end pointer */
if ( bzimg->version >= 0x0201 ) {
- bzimg->bzhdr.heap_end_ptr = ( bzimg->rm_heap - 0x200 );
- bzimg->bzhdr.loadflags |= BZI_CAN_USE_HEAP;
+ bzhdr->heap_end_ptr = ( bzimg->rm_heap - 0x200 );
+ bzhdr->loadflags |= BZI_CAN_USE_HEAP;
}
/* Set command line */
if ( bzimg->version >= 0x0202 ) {
- bzimg->bzhdr.cmd_line_ptr = user_to_phys ( bzimg->rm_kernel,
- bzimg->rm_cmdline );
+ bzhdr->cmd_line_ptr = ( virt_to_phys ( bzimg->rm_kernel )
+ + bzimg->rm_cmdline );
} else {
- bzimg->cmdline_magic.magic = BZI_CMDLINE_MAGIC;
- bzimg->cmdline_magic.offset = bzimg->rm_cmdline;
+ cmdline = ( bzimg->rm_kernel + BZI_CMDLINE_OFFSET );
+ cmdline->magic = BZI_CMDLINE_MAGIC;
+ cmdline->offset = bzimg->rm_cmdline;
if ( bzimg->version >= 0x0200 )
- bzimg->bzhdr.setup_move_size = bzimg->rm_memsz;
+ bzhdr->setup_move_size = bzimg->rm_memsz;
}
/* Set video mode */
- bzimg->bzhdr.vid_mode = bzimg->vid_mode;
+ bzhdr->vid_mode = bzimg->vid_mode;
+ DBGC ( image, "bzImage %s vidmode %d\n",
+ image->name, bzhdr->vid_mode );
/* Set initrd address */
if ( bzimg->version >= 0x0200 ) {
- bzimg->bzhdr.ramdisk_image = bzimg->ramdisk_image;
- bzimg->bzhdr.ramdisk_size = bzimg->ramdisk_size;
+ bzhdr->ramdisk_image = virt_to_phys ( bzimg->initrd );
+ bzhdr->ramdisk_size = bzimg->initrd_size;
}
-
- /* Write out header structures */
- copy_to_user ( dst, BZI_CMDLINE_OFFSET, &bzimg->cmdline_magic,
- sizeof ( bzimg->cmdline_magic ) );
- copy_to_user ( dst, BZI_HDR_OFFSET, &bzimg->bzhdr,
- sizeof ( bzimg->bzhdr ) );
-
- DBGC ( image, "bzImage %p vidmode %d\n", image, bzimg->vid_mode );
}
/**
@@ -270,8 +259,9 @@ static int bzimage_parse_cmdline ( struct image *image,
} else {
bzimg->vid_mode = strtoul ( vga, &end, 0 );
if ( *end ) {
- DBGC ( image, "bzImage %p strange \"vga=\" "
- "terminator '%c'\n", image, *end );
+ DBGC ( image, "bzImage %s strange \"vga=\" "
+ "terminator '%c'\n",
+ image->name, *end );
}
}
if ( sep )
@@ -298,8 +288,8 @@ static int bzimage_parse_cmdline ( struct image *image,
case ' ':
break;
default:
- DBGC ( image, "bzImage %p strange \"mem=\" "
- "terminator '%c'\n", image, *end );
+ DBGC ( image, "bzImage %s strange \"mem=\" "
+ "terminator '%c'\n", image->name, *end );
break;
}
bzimg->mem_limit -= 1;
@@ -317,76 +307,13 @@ static int bzimage_parse_cmdline ( struct image *image,
static void bzimage_set_cmdline ( struct image *image,
struct bzimage_context *bzimg ) {
const char *cmdline = ( image->cmdline ? image->cmdline : "" );
- size_t cmdline_len;
+ char *rm_cmdline;
/* Copy command line down to real-mode portion */
- cmdline_len = ( strlen ( cmdline ) + 1 );
- if ( cmdline_len > bzimg->cmdline_size )
- cmdline_len = bzimg->cmdline_size;
- copy_to_user ( bzimg->rm_kernel, bzimg->rm_cmdline,
- cmdline, cmdline_len );
- DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline );
-}
-
-/**
- * Align initrd length
- *
- * @v len Length
- * @ret len Length rounded up to INITRD_ALIGN
- */
-static inline size_t bzimage_align ( size_t len ) {
-
- return ( ( len + INITRD_ALIGN - 1 ) & ~( INITRD_ALIGN - 1 ) );
-}
-
-/**
- * Load initrd
- *
- * @v image bzImage image
- * @v initrd initrd image
- * @v address Address at which to load, or UNULL
- * @ret len Length of loaded image, excluding zero-padding
- */
-static size_t bzimage_load_initrd ( struct image *image,
- struct image *initrd,
- userptr_t address ) {
- const char *filename = cpio_name ( initrd );
- struct cpio_header cpio;
- size_t offset;
- size_t pad_len;
-
- /* Skip hidden images */
- if ( initrd->flags & IMAGE_HIDDEN )
- return 0;
-
- /* Create cpio header for non-prebuilt images */
- offset = cpio_header ( initrd, &cpio );
-
- /* Copy in initrd image body (and cpio header if applicable) */
- if ( address ) {
- memmove_user ( address, offset, initrd->data, 0, initrd->len );
- if ( offset ) {
- memset_user ( address, 0, 0, offset );
- copy_to_user ( address, 0, &cpio, sizeof ( cpio ) );
- copy_to_user ( address, sizeof ( cpio ), filename,
- cpio_name_len ( initrd ) );
- }
- DBGC ( image, "bzImage %p initrd %p [%#08lx,%#08lx,%#08lx)"
- "%s%s\n", image, initrd, user_to_phys ( address, 0 ),
- user_to_phys ( address, offset ),
- user_to_phys ( address, ( offset + initrd->len ) ),
- ( filename ? " " : "" ), ( filename ? filename : "" ) );
- DBGC2_MD5A ( image, user_to_phys ( address, offset ),
- user_to_virt ( address, offset ), initrd->len );
- }
- offset += initrd->len;
-
- /* Zero-pad to next INITRD_ALIGN boundary */
- pad_len = ( ( -offset ) & ( INITRD_ALIGN - 1 ) );
- if ( address )
- memset_user ( address, offset, 0, pad_len );
-
- return offset;
+ rm_cmdline = ( bzimg->rm_kernel + bzimg->rm_cmdline );
+ snprintf ( rm_cmdline, bzimg->cmdline_size, "%s", cmdline );
+ DBGC ( image, "bzImage %s command line \"%s\"\n",
+ image->name, rm_cmdline );
}
/**
@@ -398,48 +325,52 @@ static size_t bzimage_load_initrd ( struct image *image,
*/
static int bzimage_check_initrds ( struct image *image,
struct bzimage_context *bzimg ) {
- struct image *initrd;
- userptr_t bottom;
- size_t len = 0;
+ struct memmap_region region;
+ physaddr_t min;
+ physaddr_t max;
+ physaddr_t dest;
int rc;
/* Calculate total loaded length of initrds */
- for_each_image ( initrd ) {
-
- /* Calculate length */
- len += bzimage_load_initrd ( image, initrd, UNULL );
- len = bzimage_align ( len );
-
- DBGC ( image, "bzImage %p initrd %p from [%#08lx,%#08lx)%s%s\n",
- image, initrd, user_to_phys ( initrd->data, 0 ),
- user_to_phys ( initrd->data, initrd->len ),
- ( initrd->cmdline ? " " : "" ),
- ( initrd->cmdline ? initrd->cmdline : "" ) );
- DBGC2_MD5A ( image, user_to_phys ( initrd->data, 0 ),
- user_to_virt ( initrd->data, 0 ), initrd->len );
- }
+ bzimg->initrd_size = initrd_len();
- /* Calculate lowest usable address */
- bottom = userptr_add ( bzimg->pm_kernel, bzimg->pm_sz );
+ /* Succeed if there are no initrds */
+ if ( ! bzimg->initrd_size )
+ return 0;
- /* Check that total length fits within space available for
- * reshuffling. This is a conservative check, since CPIO
- * headers are not present during reshuffling, but this
- * doesn't hurt and keeps the code simple.
- */
- if ( ( rc = initrd_reshuffle_check ( len, bottom ) ) != 0 ) {
- DBGC ( image, "bzImage %p failed reshuffle check: %s\n",
- image, strerror ( rc ) );
+ /* Calculate available load region after reshuffling */
+ if ( ( rc = initrd_region ( bzimg->initrd_size, &region ) ) != 0 ) {
+ DBGC ( image, "bzImage %s no region for initrds: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
- /* Check that total length fits within kernel's memory limit */
- if ( user_to_phys ( bottom, len ) > bzimg->mem_limit ) {
- DBGC ( image, "bzImage %p not enough space for initrds\n",
- image );
+ /* Limit region to avoiding kernel itself */
+ min = virt_to_phys ( bzimg->pm_kernel + bzimg->pm_sz );
+ if ( min < region.min )
+ min = region.min;
+
+ /* Limit region to kernel's memory limit */
+ max = region.max;
+ if ( max > bzimg->mem_limit )
+ max = bzimg->mem_limit;
+
+ /* Calculate installation address */
+ if ( max < ( bzimg->initrd_size - 1 ) ) {
+ DBGC ( image, "bzImage %s not enough space for initrds\n",
+ image->name );
+ return -ENOBUFS;
+ }
+ dest = ( ( max + 1 - bzimg->initrd_size ) & ~( INITRD_ALIGN - 1 ) );
+ if ( dest < min ) {
+ DBGC ( image, "bzImage %s not enough space for initrds\n",
+ image->name );
return -ENOBUFS;
}
+ bzimg->initrd = phys_to_virt ( dest );
+ DBGC ( image, "bzImage %s loading initrds from %#08lx downwards\n",
+ image->name, max );
return 0;
}
@@ -451,65 +382,21 @@ static int bzimage_check_initrds ( struct image *image,
*/
static void bzimage_load_initrds ( struct image *image,
struct bzimage_context *bzimg ) {
- struct image *initrd;
- struct image *highest = NULL;
- struct image *other;
- userptr_t top;
- userptr_t dest;
- size_t offset;
size_t len;
- /* Reshuffle initrds into desired order */
- initrd_reshuffle ( userptr_add ( bzimg->pm_kernel, bzimg->pm_sz ) );
-
- /* Find highest initrd */
- for_each_image ( initrd ) {
- if ( ( highest == NULL ) ||
- ( userptr_sub ( initrd->data, highest->data ) > 0 ) ) {
- highest = initrd;
- }
- }
-
/* Do nothing if there are no initrds */
- if ( ! highest )
+ if ( ! bzimg->initrd )
return;
- /* Find highest usable address */
- top = userptr_add ( highest->data, bzimage_align ( highest->len ) );
- if ( user_to_phys ( top, -1 ) > bzimg->mem_limit ) {
- top = phys_to_user ( ( bzimg->mem_limit + 1 ) &
- ~( INITRD_ALIGN - 1 ) );
- }
- DBGC ( image, "bzImage %p loading initrds from %#08lx downwards\n",
- image, user_to_phys ( top, -1 ) );
-
- /* Load initrds in order */
- for_each_image ( initrd ) {
-
- /* Calculate cumulative length of following
- * initrds (including padding).
- */
- offset = 0;
- for_each_image ( other ) {
- if ( other == initrd )
- offset = 0;
- offset += bzimage_load_initrd ( image, other, UNULL );
- offset = bzimage_align ( offset );
- }
-
- /* Load initrd at this address */
- dest = userptr_add ( top, -offset );
- len = bzimage_load_initrd ( image, initrd, dest );
-
- /* Record initrd location */
- if ( ! bzimg->ramdisk_image )
- bzimg->ramdisk_image = user_to_phys ( dest, 0 );
- bzimg->ramdisk_size = ( user_to_phys ( dest, len ) -
- bzimg->ramdisk_image );
- }
- DBGC ( image, "bzImage %p initrds at [%#08lx,%#08lx)\n",
- image, bzimg->ramdisk_image,
- ( bzimg->ramdisk_image + bzimg->ramdisk_size ) );
+ /* Reshuffle initrds into desired order */
+ initrd_reshuffle();
+
+ /* Load initrds */
+ DBGC ( image, "bzImage %s initrds at [%#08lx,%#08lx)\n",
+ image->name, virt_to_phys ( bzimg->initrd ),
+ ( virt_to_phys ( bzimg->initrd ) + bzimg->initrd_size ) );
+ len = initrd_load_all ( bzimg->initrd );
+ assert ( len == bzimg->initrd_size );
}
/**
@@ -523,21 +410,20 @@ static int bzimage_exec ( struct image *image ) {
int rc;
/* Read and parse header from image */
- if ( ( rc = bzimage_parse_header ( image, &bzimg,
- image->data ) ) != 0 )
+ if ( ( rc = bzimage_parse_header ( image, &bzimg ) ) != 0 )
return rc;
/* Prepare segments */
if ( ( rc = prep_segment ( bzimg.rm_kernel, bzimg.rm_filesz,
bzimg.rm_memsz ) ) != 0 ) {
- DBGC ( image, "bzImage %p could not prepare RM segment: %s\n",
- image, strerror ( rc ) );
+ DBGC ( image, "bzImage %s could not prepare RM segment: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
if ( ( rc = prep_segment ( bzimg.pm_kernel, bzimg.pm_sz,
bzimg.pm_sz ) ) != 0 ) {
- DBGC ( image, "bzImage %p could not prepare PM segment: %s\n",
- image, strerror ( rc ) );
+ DBGC ( image, "bzImage %s could not prepare PM segment: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
@@ -553,10 +439,9 @@ static int bzimage_exec ( struct image *image ) {
unregister_image ( image_get ( image ) );
/* Load segments */
- memcpy_user ( bzimg.rm_kernel, 0, image->data,
- 0, bzimg.rm_filesz );
- memcpy_user ( bzimg.pm_kernel, 0, image->data,
- bzimg.rm_filesz, bzimg.pm_sz );
+ memcpy ( bzimg.rm_kernel, image->data, bzimg.rm_filesz );
+ memcpy ( bzimg.pm_kernel, ( image->data + bzimg.rm_filesz ),
+ bzimg.pm_sz );
/* Store command line */
bzimage_set_cmdline ( image, &bzimg );
@@ -570,10 +455,10 @@ static int bzimage_exec ( struct image *image ) {
bzimage_load_initrds ( image, &bzimg );
/* Update kernel header */
- bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
+ bzimage_update_header ( image, &bzimg );
- DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 "
- "(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ),
+ DBGC ( image, "bzImage %s jumping to RM kernel at %04x:0000 (stack "
+ "%04x:%04zx)\n", image->name, ( bzimg.rm_kernel_seg + 0x20 ),
bzimg.rm_kernel_seg, bzimg.rm_heap );
/* Jump to the kernel */
@@ -609,8 +494,7 @@ int bzimage_probe ( struct image *image ) {
int rc;
/* Read and parse header from image */
- if ( ( rc = bzimage_parse_header ( image, &bzimg,
- image->data ) ) != 0 )
+ if ( ( rc = bzimage_parse_header ( image, &bzimg ) ) != 0 )
return rc;
return 0;
diff --git a/src/arch/x86/image/com32.c b/src/arch/x86/image/com32.c
index 6f0e66041..2b31fb2e5 100644
--- a/src/arch/x86/image/com32.c
+++ b/src/arch/x86/image/com32.c
@@ -39,7 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/image.h>
#include <ipxe/segment.h>
#include <ipxe/init.h>
-#include <ipxe/io.h>
+#include <ipxe/memmap.h>
#include <ipxe/console.h>
/**
@@ -49,8 +49,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
* @ret rc Return status code
*/
static int com32_exec_loop ( struct image *image ) {
- struct memory_map memmap;
- unsigned int i;
+ struct memmap_region region;
int state;
uint32_t avail_mem_top;
@@ -59,21 +58,12 @@ static int com32_exec_loop ( struct image *image ) {
switch ( state ) {
case 0: /* First time through; invoke COM32 program */
- /* Get memory map */
- get_memmap ( &memmap );
-
/* Find end of block covering COM32 image loading area */
- for ( i = 0, avail_mem_top = 0 ; i < memmap.count ; i++ ) {
- if ( (memmap.regions[i].start <= COM32_START_PHYS) &&
- (memmap.regions[i].end > COM32_START_PHYS + image->len) ) {
- avail_mem_top = memmap.regions[i].end;
- break;
- }
- }
-
- DBGC ( image, "COM32 %p: available memory top = 0x%x\n",
- image, avail_mem_top );
-
+ memmap_describe ( COM32_START_PHYS, 1, &region );
+ assert ( memmap_is_usable ( &region ) );
+ avail_mem_top = ( COM32_START_PHYS + memmap_size ( &region ) );
+ DBGC ( image, "COM32 %s: available memory top = 0x%x\n",
+ image->name, avail_mem_top );
assert ( avail_mem_top != 0 );
/* Hook COMBOOT API interrupts */
@@ -114,32 +104,32 @@ static int com32_exec_loop ( struct image *image ) {
/* Restore registers */
"popal\n\t" )
:
- : "r" ( avail_mem_top ),
- "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ),
- "r" ( virt_to_phys ( com32_farcall_wrapper ) ),
- "r" ( get_fbms() * 1024 - ( COM32_BOUNCE_SEG << 4 ) ),
+ : "R" ( avail_mem_top ),
+ "R" ( virt_to_phys ( com32_cfarcall_wrapper ) ),
+ "R" ( virt_to_phys ( com32_farcall_wrapper ) ),
+ "R" ( get_fbms() * 1024 - ( COM32_BOUNCE_SEG << 4 ) ),
"i" ( COM32_BOUNCE_SEG << 4 ),
- "r" ( virt_to_phys ( com32_intcall_wrapper ) ),
- "r" ( virt_to_phys ( image->cmdline ?
+ "R" ( virt_to_phys ( com32_intcall_wrapper ) ),
+ "R" ( virt_to_phys ( image->cmdline ?
image->cmdline : "" ) ),
"i" ( COM32_START_PHYS )
: "memory" );
- DBGC ( image, "COM32 %p: returned\n", image );
+ DBGC ( image, "COM32 %s: returned\n", image->name );
break;
case COMBOOT_EXIT:
- DBGC ( image, "COM32 %p: exited\n", image );
+ DBGC ( image, "COM32 %s: exited\n", image->name );
break;
case COMBOOT_EXIT_RUN_KERNEL:
assert ( image->replacement );
- DBGC ( image, "COM32 %p: exited to run kernel %s\n",
- image, image->replacement->name );
+ DBGC ( image, "COM32 %s: exited to run kernel %s\n",
+ image->name, image->replacement->name );
break;
case COMBOOT_EXIT_COMMAND:
- DBGC ( image, "COM32 %p: exited after executing command\n",
- image );
+ DBGC ( image, "COM32 %s: exited after executing command\n",
+ image->name );
break;
default:
@@ -162,17 +152,15 @@ static int com32_exec_loop ( struct image *image ) {
static int com32_identify ( struct image *image ) {
const char *ext;
static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 };
- uint8_t buf[5];
- if ( image->len >= 5 ) {
+ if ( image->len >= sizeof ( magic ) ) {
/* Check for magic number
* mov eax,21cd4cffh
* B8 FF 4C CD 21
*/
- copy_from_user ( buf, image->data, 0, sizeof(buf) );
- if ( ! memcmp ( buf, magic, sizeof(buf) ) ) {
- DBGC ( image, "COM32 %p: found magic number\n",
- image );
+ if ( memcmp ( image->data, magic, sizeof ( magic) ) == 0 ) {
+ DBGC ( image, "COM32 %s: found magic number\n",
+ image->name );
return 0;
}
}
@@ -182,16 +170,16 @@ static int com32_identify ( struct image *image ) {
ext = strrchr( image->name, '.' );
if ( ! ext ) {
- DBGC ( image, "COM32 %p: no extension\n",
- image );
+ DBGC ( image, "COM32 %s: no extension\n",
+ image->name );
return -ENOEXEC;
}
++ext;
if ( strcasecmp( ext, "c32" ) ) {
- DBGC ( image, "COM32 %p: unrecognized extension %s\n",
- image, ext );
+ DBGC ( image, "COM32 %s: unrecognized extension %s\n",
+ image->name, ext );
return -ENOEXEC;
}
@@ -206,20 +194,20 @@ static int com32_identify ( struct image *image ) {
*/
static int com32_load_image ( struct image *image ) {
size_t filesz, memsz;
- userptr_t buffer;
+ void *buffer;
int rc;
filesz = image->len;
memsz = filesz;
- buffer = phys_to_user ( COM32_START_PHYS );
+ buffer = phys_to_virt ( COM32_START_PHYS );
if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
- DBGC ( image, "COM32 %p: could not prepare segment: %s\n",
- image, strerror ( rc ) );
+ DBGC ( image, "COM32 %s: could not prepare segment: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
/* Copy image to segment */
- memcpy_user ( buffer, 0, image->data, 0, filesz );
+ memcpy ( buffer, image->data, filesz );
return 0;
}
@@ -230,22 +218,20 @@ static int com32_load_image ( struct image *image ) {
* @ret rc Return status code
*/
static int com32_prepare_bounce_buffer ( struct image * image ) {
- unsigned int seg;
- userptr_t seg_userptr;
+ void *seg;
size_t filesz, memsz;
int rc;
- seg = COM32_BOUNCE_SEG;
- seg_userptr = real_to_user ( seg, 0 );
+ seg = real_to_virt ( COM32_BOUNCE_SEG, 0 );
/* Ensure the entire 64k segment is free */
memsz = 0xFFFF;
filesz = 0;
/* Prepare, verify, and load the real-mode segment */
- if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
- DBGC ( image, "COM32 %p: could not prepare bounce buffer segment: %s\n",
- image, strerror ( rc ) );
+ if ( ( rc = prep_segment ( seg, filesz, memsz ) ) != 0 ) {
+ DBGC ( image, "COM32 %s: could not prepare bounce buffer segment: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
@@ -261,8 +247,6 @@ static int com32_prepare_bounce_buffer ( struct image * image ) {
static int com32_probe ( struct image *image ) {
int rc;
- DBGC ( image, "COM32 %p: name '%s'\n", image, image->name );
-
/* Check if this is a COMBOOT image */
if ( ( rc = com32_identify ( image ) ) != 0 ) {
return rc;
diff --git a/src/arch/x86/image/comboot.c b/src/arch/x86/image/comboot.c
index 9a847f0ff..6eba027c6 100644
--- a/src/arch/x86/image/comboot.c
+++ b/src/arch/x86/image/comboot.c
@@ -35,7 +35,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <realmode.h>
#include <basemem.h>
#include <comboot.h>
-#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/segment.h>
#include <ipxe/init.h>
@@ -67,62 +66,53 @@ struct comboot_psp {
*
* @v image COMBOOT image
*/
-static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) {
+static void comboot_copy_cmdline ( struct image * image, void *seg ) {
const char *cmdline = ( image->cmdline ? image->cmdline : "" );
int cmdline_len = strlen ( cmdline );
+ uint8_t *psp_cmdline;
+
+ /* Limit length of command line */
if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN )
cmdline_len = COMBOOT_MAX_CMDLINE_LEN;
- uint8_t len_byte = cmdline_len;
- char spc = ' ', cr = '\r';
/* Copy length to byte before command line */
- copy_to_user ( seg_userptr, COMBOOT_PSP_CMDLINE_OFFSET - 1,
- &len_byte, 1 );
+ psp_cmdline = ( seg + COMBOOT_PSP_CMDLINE_OFFSET );
+ psp_cmdline[-1] = cmdline_len;
/* Command line starts with space */
- copy_to_user ( seg_userptr,
- COMBOOT_PSP_CMDLINE_OFFSET,
- &spc, 1 );
+ psp_cmdline[0] = ' ';
/* Copy command line */
- copy_to_user ( seg_userptr,
- COMBOOT_PSP_CMDLINE_OFFSET + 1,
- cmdline, cmdline_len );
+ memcpy ( &psp_cmdline[1], cmdline, cmdline_len );
/* Command line ends with CR */
- copy_to_user ( seg_userptr,
- COMBOOT_PSP_CMDLINE_OFFSET + cmdline_len + 1,
- &cr, 1 );
+ psp_cmdline[ 1 + cmdline_len ] = '\r';
}
/**
* Initialize PSP
*
* @v image COMBOOT image
- * @v seg_userptr segment to initialize
+ * @v seg segment to initialize
*/
-static void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) {
- struct comboot_psp psp;
+static void comboot_init_psp ( struct image * image, void *seg ) {
+ struct comboot_psp *psp;
/* Fill PSP */
+ psp = seg;
/* INT 20h instruction, byte order reversed */
- psp.int20 = 0x20CD;
+ psp->int20 = 0x20CD;
/* get_fbms() returns BIOS free base memory counter, which is in
* kilobytes; x * 1024 / 16 == x * 64 == x << 6 */
- psp.first_non_free_para = get_fbms() << 6;
-
- DBGC ( image, "COMBOOT %p: first non-free paragraph = 0x%x\n",
- image, psp.first_non_free_para );
+ psp->first_non_free_para = get_fbms() << 6;
- /* Copy the PSP to offset 0 of segment.
- * The rest of the PSP was already zeroed by
- * comboot_prepare_segment. */
- copy_to_user ( seg_userptr, 0, &psp, sizeof( psp ) );
+ DBGC ( image, "COMBOOT %s: first non-free paragraph = 0x%x\n",
+ image->name, psp->first_non_free_para );
/* Copy the command line to the PSP */
- comboot_copy_cmdline ( image, seg_userptr );
+ comboot_copy_cmdline ( image, seg );
}
/**
@@ -132,7 +122,7 @@ static void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) {
* @ret rc Return status code
*/
static int comboot_exec_loop ( struct image *image ) {
- userptr_t seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
+ void *seg = real_to_virt ( COMBOOT_PSP_SEG, 0 );
int state;
state = rmsetjmp ( comboot_return );
@@ -141,7 +131,7 @@ static int comboot_exec_loop ( struct image *image ) {
case 0: /* First time through; invoke COMBOOT program */
/* Initialize PSP */
- comboot_init_psp ( image, seg_userptr );
+ comboot_init_psp ( image, seg );
/* Hook COMBOOT API interrupts */
hook_comboot_interrupts();
@@ -181,23 +171,23 @@ static int comboot_exec_loop ( struct image *image ) {
"xorw %%di, %%di\n\t"
"xorw %%bp, %%bp\n\t"
"lret\n\t" )
- : : "r" ( COMBOOT_PSP_SEG ) : "eax" );
- DBGC ( image, "COMBOOT %p: returned\n", image );
+ : : "R" ( COMBOOT_PSP_SEG ) : "eax" );
+ DBGC ( image, "COMBOOT %s: returned\n", image->name );
break;
case COMBOOT_EXIT:
- DBGC ( image, "COMBOOT %p: exited\n", image );
+ DBGC ( image, "COMBOOT %s: exited\n", image->name );
break;
case COMBOOT_EXIT_RUN_KERNEL:
assert ( image->replacement );
- DBGC ( image, "COMBOOT %p: exited to run kernel %s\n",
- image, image->replacement->name );
+ DBGC ( image, "COMBOOT %s: exited to run kernel %s\n",
+ image->name, image->replacement->name );
break;
case COMBOOT_EXIT_COMMAND:
- DBGC ( image, "COMBOOT %p: exited after executing command\n",
- image );
+ DBGC ( image, "COMBOOT %s: exited after executing command\n",
+ image->name );
break;
default:
@@ -223,16 +213,16 @@ static int comboot_identify ( struct image *image ) {
ext = strrchr( image->name, '.' );
if ( ! ext ) {
- DBGC ( image, "COMBOOT %p: no extension\n",
- image );
+ DBGC ( image, "COMBOOT %s: no extension\n",
+ image->name );
return -ENOEXEC;
}
++ext;
if ( strcasecmp( ext, "cbt" ) ) {
- DBGC ( image, "COMBOOT %p: unrecognized extension %s\n",
- image, ext );
+ DBGC ( image, "COMBOOT %s: unrecognized extension %s\n",
+ image->name, ext );
return -ENOEXEC;
}
@@ -246,12 +236,12 @@ static int comboot_identify ( struct image *image ) {
*/
static int comboot_prepare_segment ( struct image *image )
{
- userptr_t seg_userptr;
+ void *seg;
size_t filesz, memsz;
int rc;
/* Load image in segment */
- seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
+ seg = real_to_virt ( COMBOOT_PSP_SEG, 0 );
/* Allow etra 0x100 bytes before image for PSP */
filesz = image->len + 0x100;
@@ -260,17 +250,17 @@ static int comboot_prepare_segment ( struct image *image )
memsz = 0xFFFF;
/* Prepare, verify, and load the real-mode segment */
- if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
- DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n",
- image, strerror ( rc ) );
+ if ( ( rc = prep_segment ( seg, filesz, memsz ) ) != 0 ) {
+ DBGC ( image, "COMBOOT %s: could not prepare segment: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
/* Zero PSP */
- memset_user ( seg_userptr, 0, 0, 0x100 );
+ memset ( seg, 0, 0x100 );
/* Copy image to segment:0100 */
- memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len );
+ memcpy ( ( seg + 0x100 ), image->data, image->len );
return 0;
}
@@ -284,9 +274,6 @@ static int comboot_prepare_segment ( struct image *image )
static int comboot_probe ( struct image *image ) {
int rc;
- DBGC ( image, "COMBOOT %p: name '%s'\n",
- image, image->name );
-
/* Check if this is a COMBOOT image */
if ( ( rc = comboot_identify ( image ) ) != 0 ) {
@@ -307,8 +294,8 @@ static int comboot_exec ( struct image *image ) {
/* Sanity check for filesize */
if( image->len >= 0xFF00 ) {
- DBGC( image, "COMBOOT %p: image too large\n",
- image );
+ DBGC( image, "COMBOOT %s: image too large\n",
+ image->name );
return -ENOEXEC;
}
diff --git a/src/arch/x86/image/elfboot.c b/src/arch/x86/image/elfboot.c
index dc3568929..d0f91d1c0 100644
--- a/src/arch/x86/image/elfboot.c
+++ b/src/arch/x86/image/elfboot.c
@@ -23,8 +23,10 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+#include <string.h>
#include <errno.h>
#include <elf.h>
+#include <librm.h>
#include <ipxe/image.h>
#include <ipxe/elf.h>
#include <ipxe/features.h>
@@ -52,8 +54,8 @@ static int elfboot_exec ( struct image *image ) {
/* Load the image using core ELF support */
if ( ( rc = elf_load ( image, &entry, &max ) ) != 0 ) {
- DBGC ( image, "ELF %p could not load: %s\n",
- image, strerror ( rc ) );
+ DBGC ( image, "ELF %s could not load: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
@@ -63,14 +65,15 @@ static int elfboot_exec ( struct image *image ) {
shutdown_boot();
/* Jump to OS with flat physical addressing */
- DBGC ( image, "ELF %p starting execution at %lx\n", image, entry );
+ DBGC ( image, "ELF %s starting execution at %lx\n",
+ image->name, entry );
__asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"call *%%edi\n\t"
"popl %%ebp\n\t" /* gcc bug */ )
: : "D" ( entry )
: "eax", "ebx", "ecx", "edx", "esi", "memory" );
- DBGC ( image, "ELF %p returned\n", image );
+ DBGC ( image, "ELF %s returned\n", image->name );
/* It isn't safe to continue after calling shutdown() */
while ( 1 ) {}
@@ -86,13 +89,13 @@ static int elfboot_exec ( struct image *image ) {
* @v dest Destination address
* @ret rc Return status code
*/
-static int elfboot_check_segment ( struct image *image, Elf_Phdr *phdr,
+static int elfboot_check_segment ( struct image *image, const Elf_Phdr *phdr,
physaddr_t dest ) {
/* Check that ELF segment uses flat physical addressing */
if ( phdr->p_vaddr != dest ) {
- DBGC ( image, "ELF %p uses virtual addressing (phys %x, "
- "virt %x)\n", image, phdr->p_paddr, phdr->p_vaddr );
+ DBGC ( image, "ELF %s uses virtual addressing (phys %x, virt "
+ "%x)\n", image->name, phdr->p_paddr, phdr->p_vaddr );
return -ENOEXEC;
}
@@ -106,7 +109,7 @@ static int elfboot_check_segment ( struct image *image, Elf_Phdr *phdr,
* @ret rc Return status code
*/
static int elfboot_probe ( struct image *image ) {
- Elf32_Ehdr ehdr;
+ const Elf32_Ehdr *ehdr;
static const uint8_t e_ident[] = {
[EI_MAG0] = ELFMAG0,
[EI_MAG1] = ELFMAG1,
@@ -121,16 +124,22 @@ static int elfboot_probe ( struct image *image ) {
int rc;
/* Read ELF header */
- copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
- if ( memcmp ( ehdr.e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) {
- DBGC ( image, "Invalid ELF identifier\n" );
+ if ( image->len < sizeof ( *ehdr ) ) {
+ DBGC ( image, "ELF %s too short for ELF header\n",
+ image->name );
+ return -ENOEXEC;
+ }
+ ehdr = image->data;
+ if ( memcmp ( ehdr->e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) {
+ DBGC ( image, "ELF %s invalid identifier\n", image->name );
return -ENOEXEC;
}
/* Check that this image uses flat physical addressing */
- if ( ( rc = elf_segments ( image, &ehdr, elfboot_check_segment,
+ if ( ( rc = elf_segments ( image, ehdr, elfboot_check_segment,
&entry, &max ) ) != 0 ) {
- DBGC ( image, "Unloadable ELF image\n" );
+ DBGC ( image, "ELF %s is not loadable: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
diff --git a/src/arch/x86/image/initrd.c b/src/arch/x86/image/initrd.c
deleted file mode 100644
index d7b1f5773..000000000
--- a/src/arch/x86/image/initrd.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <errno.h>
-#include <initrd.h>
-#include <ipxe/image.h>
-#include <ipxe/uaccess.h>
-#include <ipxe/init.h>
-#include <ipxe/memblock.h>
-#include <ipxe/cpio.h>
-
-/** @file
- *
- * Initial ramdisk (initrd) reshuffling
- *
- */
-
-/** Maximum address available for initrd */
-userptr_t initrd_top;
-
-/** Minimum address available for initrd */
-userptr_t initrd_bottom;
-
-/**
- * Squash initrds as high as possible in memory
- *
- * @v top Highest possible address
- * @ret used Lowest address used by initrds
- */
-static userptr_t initrd_squash_high ( userptr_t top ) {
- userptr_t current = top;
- struct image *initrd;
- struct image *highest;
- size_t len;
-
- /* Squash up any initrds already within or below the region */
- while ( 1 ) {
-
- /* Find the highest image not yet in its final position */
- highest = NULL;
- for_each_image ( initrd ) {
- if ( ( userptr_sub ( initrd->data, current ) < 0 ) &&
- ( ( highest == NULL ) ||
- ( userptr_sub ( initrd->data,
- highest->data ) > 0 ) ) ) {
- highest = initrd;
- }
- }
- if ( ! highest )
- break;
-
- /* Move this image to its final position */
- len = ( ( highest->len + INITRD_ALIGN - 1 ) &
- ~( INITRD_ALIGN - 1 ) );
- current = userptr_sub ( current, len );
- DBGC ( &images, "INITRD squashing %s [%#08lx,%#08lx)->"
- "[%#08lx,%#08lx)\n", highest->name,
- user_to_phys ( highest->data, 0 ),
- user_to_phys ( highest->data, highest->len ),
- user_to_phys ( current, 0 ),
- user_to_phys ( current, highest->len ) );
- memmove_user ( current, 0, highest->data, 0, highest->len );
- highest->data = current;
- }
-
- /* Copy any remaining initrds (e.g. embedded images) to the region */
- for_each_image ( initrd ) {
- if ( userptr_sub ( initrd->data, top ) >= 0 ) {
- len = ( ( initrd->len + INITRD_ALIGN - 1 ) &
- ~( INITRD_ALIGN - 1 ) );
- current = userptr_sub ( current, len );
- DBGC ( &images, "INITRD copying %s [%#08lx,%#08lx)->"
- "[%#08lx,%#08lx)\n", initrd->name,
- user_to_phys ( initrd->data, 0 ),
- user_to_phys ( initrd->data, initrd->len ),
- user_to_phys ( current, 0 ),
- user_to_phys ( current, initrd->len ) );
- memcpy_user ( current, 0, initrd->data, 0,
- initrd->len );
- initrd->data = current;
- }
- }
-
- return current;
-}
-
-/**
- * Swap position of two adjacent initrds
- *
- * @v low Lower initrd
- * @v high Higher initrd
- * @v free Free space
- * @v free_len Length of free space
- */
-static void initrd_swap ( struct image *low, struct image *high,
- userptr_t free, size_t free_len ) {
- size_t len = 0;
- size_t frag_len;
- size_t new_len;
-
- DBGC ( &images, "INITRD swapping %s [%#08lx,%#08lx)<->[%#08lx,%#08lx) "
- "%s\n", low->name, user_to_phys ( low->data, 0 ),
- user_to_phys ( low->data, low->len ),
- user_to_phys ( high->data, 0 ),
- user_to_phys ( high->data, high->len ), high->name );
-
- /* Round down length of free space */
- free_len &= ~( INITRD_ALIGN - 1 );
- assert ( free_len > 0 );
-
- /* Swap image data */
- while ( len < high->len ) {
-
- /* Calculate maximum fragment length */
- frag_len = ( high->len - len );
- if ( frag_len > free_len )
- frag_len = free_len;
- new_len = ( ( len + frag_len + INITRD_ALIGN - 1 ) &
- ~( INITRD_ALIGN - 1 ) );
-
- /* Swap fragments */
- memcpy_user ( free, 0, high->data, len, frag_len );
- memmove_user ( low->data, new_len, low->data, len, low->len );
- memcpy_user ( low->data, len, free, 0, frag_len );
- len = new_len;
- }
-
- /* Adjust data pointers */
- high->data = low->data;
- low->data = userptr_add ( low->data, len );
-}
-
-/**
- * Swap position of any two adjacent initrds not currently in the correct order
- *
- * @v free Free space
- * @v free_len Length of free space
- * @ret swapped A pair of initrds was swapped
- */
-static int initrd_swap_any ( userptr_t free, size_t free_len ) {
- struct image *low;
- struct image *high;
- size_t padded_len;
- userptr_t adjacent;
-
- /* Find any pair of initrds that can be swapped */
- for_each_image ( low ) {
-
- /* Calculate location of adjacent image (if any) */
- padded_len = ( ( low->len + INITRD_ALIGN - 1 ) &
- ~( INITRD_ALIGN - 1 ) );
- adjacent = userptr_add ( low->data, padded_len );
-
- /* Search for adjacent image */
- for_each_image ( high ) {
-
- /* Stop search if all remaining potential
- * adjacent images are already in the correct
- * order.
- */
- if ( high == low )
- break;
-
- /* If we have found the adjacent image, swap and exit */
- if ( high->data == adjacent ) {
- initrd_swap ( low, high, free, free_len );
- return 1;
- }
- }
- }
-
- /* Nothing swapped */
- return 0;
-}
-
-/**
- * Dump initrd locations (for debug)
- *
- */
-static void initrd_dump ( void ) {
- struct image *initrd;
-
- /* Do nothing unless debugging is enabled */
- if ( ! DBG_LOG )
- return;
-
- /* Dump initrd locations */
- for_each_image ( initrd ) {
- DBGC ( &images, "INITRD %s at [%#08lx,%#08lx)\n",
- initrd->name, user_to_phys ( initrd->data, 0 ),
- user_to_phys ( initrd->data, initrd->len ) );
- DBGC2_MD5A ( &images, user_to_phys ( initrd->data, 0 ),
- user_to_virt ( initrd->data, 0 ), initrd->len );
- }
-}
-
-/**
- * Reshuffle initrds into desired order at top of memory
- *
- * @v bottom Lowest address available for initrds
- *
- * After this function returns, the initrds have been rearranged in
- * memory and the external heap structures will have been corrupted.
- * Reshuffling must therefore take place immediately prior to jumping
- * to the loaded OS kernel; no further execution within iPXE is
- * permitted.
- */
-void initrd_reshuffle ( userptr_t bottom ) {
- userptr_t top;
- userptr_t used;
- userptr_t free;
- size_t free_len;
-
- /* Calculate limits of available space for initrds */
- top = initrd_top;
- if ( userptr_sub ( initrd_bottom, bottom ) > 0 )
- bottom = initrd_bottom;
-
- /* Debug */
- DBGC ( &images, "INITRD region [%#08lx,%#08lx)\n",
- user_to_phys ( bottom, 0 ), user_to_phys ( top, 0 ) );
- initrd_dump();
-
- /* Squash initrds as high as possible in memory */
- used = initrd_squash_high ( top );
-
- /* Calculate available free space */
- free = bottom;
- free_len = userptr_sub ( used, free );
-
- /* Bubble-sort initrds into desired order */
- while ( initrd_swap_any ( free, free_len ) ) {}
-
- /* Debug */
- initrd_dump();
-}
-
-/**
- * Check that there is enough space to reshuffle initrds
- *
- * @v len Total length of initrds (including padding)
- * @v bottom Lowest address available for initrds
- * @ret rc Return status code
- */
-int initrd_reshuffle_check ( size_t len, userptr_t bottom ) {
- userptr_t top;
- size_t available;
-
- /* Calculate limits of available space for initrds */
- top = initrd_top;
- if ( userptr_sub ( initrd_bottom, bottom ) > 0 )
- bottom = initrd_bottom;
- available = userptr_sub ( top, bottom );
-
- /* Allow for a sensible minimum amount of free space */
- len += INITRD_MIN_FREE_LEN;
-
- /* Check for available space */
- return ( ( len < available ) ? 0 : -ENOBUFS );
-}
-
-/**
- * initrd startup function
- *
- */
-static void initrd_startup ( void ) {
- size_t len;
-
- /* Record largest memory block available. Do this after any
- * allocations made during driver startup (e.g. large host
- * memory blocks for Infiniband devices, which may still be in
- * use at the time of rearranging if a SAN device is hooked)
- * but before any allocations for downloaded images (which we
- * can safely reuse when rearranging).
- */
- len = largest_memblock ( &initrd_bottom );
- initrd_top = userptr_add ( initrd_bottom, len );
-}
-
-/** initrd startup function */
-struct startup_fn startup_initrd __startup_fn ( STARTUP_LATE ) = {
- .name = "initrd",
- .startup = initrd_startup,
-};
diff --git a/src/arch/x86/image/multiboot.c b/src/arch/x86/image/multiboot.c
index cada021ab..40d6941da 100644
--- a/src/arch/x86/image/multiboot.c
+++ b/src/arch/x86/image/multiboot.c
@@ -31,14 +31,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#include <stdio.h>
+#include <string.h>
#include <errno.h>
#include <assert.h>
#include <realmode.h>
#include <multiboot.h>
-#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/segment.h>
-#include <ipxe/io.h>
+#include <ipxe/memmap.h>
#include <ipxe/elf.h>
#include <ipxe/init.h>
#include <ipxe/features.h>
@@ -59,6 +59,9 @@ FEATURE ( FEATURE_IMAGE, "MBOOT", DHCP_EB_FEATURE_MULTIBOOT, 1 );
*/
#define MAX_MODULES 8
+/** Maximum number of memory map entries */
+#define MAX_MEMMAP 8
+
/**
* Maximum combined length of command lines
*
@@ -87,14 +90,6 @@ FEATURE ( FEATURE_IMAGE, "MBOOT", DHCP_EB_FEATURE_MULTIBOOT, 1 );
*/
#define MB_UNSUPPORTED_FLAGS ( MB_COMPULSORY_FLAGS & ~MB_SUPPORTED_FLAGS )
-/** A multiboot header descriptor */
-struct multiboot_header_info {
- /** The actual multiboot header */
- struct multiboot_header mb;
- /** Offset of header within the multiboot image */
- size_t offset;
-};
-
/** Multiboot module command lines */
static char __bss16_array ( mb_cmdlines, [MB_MAX_CMDLINE] );
#define mb_cmdlines __use_data16 ( mb_cmdlines )
@@ -114,32 +109,43 @@ static void multiboot_build_memmap ( struct image *image,
struct multiboot_info *mbinfo,
struct multiboot_memory_map *mbmemmap,
unsigned int limit ) {
- struct memory_map memmap;
- unsigned int i;
-
- /* Get memory map */
- get_memmap ( &memmap );
+ struct memmap_region region;
+ unsigned int remaining;
/* Translate into multiboot format */
memset ( mbmemmap, 0, sizeof ( *mbmemmap ) );
- for ( i = 0 ; i < memmap.count ; i++ ) {
- if ( i >= limit ) {
- DBGC ( image, "MULTIBOOT %p limit of %d memmap "
- "entries reached\n", image, limit );
+ remaining = limit;
+ for_each_memmap ( &region, 0 ) {
+
+ /* Ignore any non-memory regions */
+ if ( ! ( region.flags & MEMMAP_FL_MEMORY ) )
+ continue;
+ DBGC_MEMMAP ( image, &region );
+
+ /* Check Multiboot memory map limit */
+ if ( ! remaining ) {
+ DBGC ( image, "MULTIBOOT %s limit of %d memmap "
+ "entries reached\n", image->name, limit );
break;
}
- mbmemmap[i].size = ( sizeof ( mbmemmap[i] ) -
- sizeof ( mbmemmap[i].size ) );
- mbmemmap[i].base_addr = memmap.regions[i].start;
- mbmemmap[i].length = ( memmap.regions[i].end -
- memmap.regions[i].start );
- mbmemmap[i].type = MBMEM_RAM;
- mbinfo->mmap_length += sizeof ( mbmemmap[i] );
- if ( memmap.regions[i].start == 0 )
- mbinfo->mem_lower = ( memmap.regions[i].end / 1024 );
- if ( memmap.regions[i].start == 0x100000 )
- mbinfo->mem_upper = ( ( memmap.regions[i].end -
- 0x100000 ) / 1024 );
+
+ /* Populate Multiboot memory map entry */
+ mbmemmap->size = ( sizeof ( *mbmemmap ) -
+ sizeof ( mbmemmap->size ) );
+ mbmemmap->base_addr = region.min;
+ mbmemmap->length = memmap_size ( &region );
+ mbmemmap->type = MBMEM_RAM;
+
+ /* Update Multiboot information */
+ mbinfo->mmap_length += sizeof ( *mbmemmap );
+ if ( mbmemmap->base_addr == 0 )
+ mbinfo->mem_lower = ( mbmemmap->length / 1024 );
+ if ( mbmemmap->base_addr == 0x100000 )
+ mbinfo->mem_upper = ( mbmemmap->length / 1024 );
+
+ /* Move to next Multiboot memory map entry */
+ mbmemmap++;
+ remaining--;
}
}
@@ -199,8 +205,8 @@ static int multiboot_add_modules ( struct image *image, physaddr_t start,
for_each_image ( module_image ) {
if ( mbinfo->mods_count >= limit ) {
- DBGC ( image, "MULTIBOOT %p limit of %d modules "
- "reached\n", image, limit );
+ DBGC ( image, "MULTIBOOT %s limit of %d modules "
+ "reached\n", image->name, limit );
break;
}
@@ -212,18 +218,18 @@ static int multiboot_add_modules ( struct image *image, physaddr_t start,
start = ( ( start + 0xfff ) & ~0xfff );
/* Prepare segment */
- if ( ( rc = prep_segment ( phys_to_user ( start ),
+ if ( ( rc = prep_segment ( phys_to_virt ( start ),
module_image->len,
module_image->len ) ) != 0 ) {
- DBGC ( image, "MULTIBOOT %p could not prepare module "
- "%s: %s\n", image, module_image->name,
+ DBGC ( image, "MULTIBOOT %s could not prepare module "
+ "%s: %s\n", image->name, module_image->name,
strerror ( rc ) );
return rc;
}
/* Copy module */
- memcpy_user ( phys_to_user ( start ), 0,
- module_image->data, 0, module_image->len );
+ memcpy ( phys_to_virt ( start ), module_image->data,
+ module_image->len );
/* Add module to list */
module = &modules[mbinfo->mods_count++];
@@ -231,8 +237,8 @@ static int multiboot_add_modules ( struct image *image, physaddr_t start,
module->mod_end = ( start + module_image->len );
module->string = multiboot_add_cmdline ( module_image );
module->reserved = 0;
- DBGC ( image, "MULTIBOOT %p module %s is [%x,%x)\n",
- image, module_image->name, module->mod_start,
+ DBGC ( image, "MULTIBOOT %s module %s is [%x,%x)\n",
+ image->name, module_image->name, module->mod_start,
module->mod_end );
start += module_image->len;
}
@@ -255,8 +261,7 @@ static char __bss16_array ( mb_bootloader_name, [32] );
#define mb_bootloader_name __use_data16 ( mb_bootloader_name )
/** The multiboot memory map */
-static struct multiboot_memory_map
- __bss16_array ( mbmemmap, [MAX_MEMORY_REGIONS] );
+static struct multiboot_memory_map __bss16_array ( mbmemmap, [MAX_MEMMAP] );
#define mbmemmap __use_data16 ( mbmemmap )
/** The multiboot module list */
@@ -267,94 +272,101 @@ static struct multiboot_module __bss16_array ( mbmodules, [MAX_MODULES] );
* Find multiboot header
*
* @v image Multiboot file
- * @v hdr Multiboot header descriptor to fill in
- * @ret rc Return status code
+ * @ret offset Offset to Multiboot header, or negative error
*/
-static int multiboot_find_header ( struct image *image,
- struct multiboot_header_info *hdr ) {
- uint32_t buf[64];
+static int multiboot_find_header ( struct image *image ) {
+ const struct multiboot_header *mb;
size_t offset;
- unsigned int buf_idx;
uint32_t checksum;
- /* Scan through first 8kB of image file 256 bytes at a time.
- * (Use the buffering to avoid the overhead of a
- * copy_from_user() for every dword.)
- */
- for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) {
+ /* Scan through first 8kB of image file */
+ for ( offset = 0 ; offset < 8192 ; offset += 4 ) {
/* Check for end of image */
- if ( offset > image->len )
+ if ( ( offset + sizeof ( *mb ) ) > image->len )
break;
- /* Refill buffer if applicable */
- buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) );
- if ( buf_idx == 0 ) {
- copy_from_user ( buf, image->data, offset,
- sizeof ( buf ) );
- }
+ mb = ( image->data + offset );
/* Check signature */
- if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC )
+ if ( mb->magic != MULTIBOOT_HEADER_MAGIC )
continue;
/* Copy header and verify checksum */
- copy_from_user ( &hdr->mb, image->data, offset,
- sizeof ( hdr->mb ) );
- checksum = ( hdr->mb.magic + hdr->mb.flags +
- hdr->mb.checksum );
+ checksum = ( mb->magic + mb->flags + mb->checksum );
if ( checksum != 0 )
continue;
- /* Record offset of multiboot header and return */
- hdr->offset = offset;
- return 0;
+ /* Return header */
+ return offset;
}
/* No multiboot header found */
+ DBGC ( image, "MULTIBOOT %s has no multiboot header\n",
+ image->name );
return -ENOEXEC;
}
/**
* Load raw multiboot image into memory
*
- * @v image Multiboot file
- * @v hdr Multiboot header descriptor
+ * @v image Multiboot image
+ * @v offset Offset to Multiboot header
* @ret entry Entry point
* @ret max Maximum used address
* @ret rc Return status code
*/
-static int multiboot_load_raw ( struct image *image,
- struct multiboot_header_info *hdr,
+static int multiboot_load_raw ( struct image *image, size_t offset,
physaddr_t *entry, physaddr_t *max ) {
- size_t offset;
+ const struct multiboot_header *mb = ( image->data + offset );
size_t filesz;
size_t memsz;
- userptr_t buffer;
+ void *buffer;
int rc;
/* Sanity check */
- if ( ! ( hdr->mb.flags & MB_FLAG_RAW ) ) {
- DBGC ( image, "MULTIBOOT %p is not flagged as a raw image\n",
- image );
+ if ( ! ( mb->flags & MB_FLAG_RAW ) ) {
+ DBGC ( image, "MULTIBOOT %s is not flagged as a raw image\n",
+ image->name );
return -EINVAL;
}
- /* Verify and prepare segment */
- offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr );
- filesz = ( hdr->mb.load_end_addr ?
- ( hdr->mb.load_end_addr - hdr->mb.load_addr ) :
+ /* Calculate starting offset within file */
+ if ( ( mb->load_addr > mb->header_addr ) ||
+ ( ( mb->header_addr - mb->load_addr ) > offset ) ) {
+ DBGC ( image, "MULTIBOOT %s has misplaced header\n",
+ image->name );
+ return -EINVAL;
+ }
+ offset -= ( mb->header_addr - mb->load_addr );
+ assert ( offset < image->len );
+
+ /* Calculate length of initialized data */
+ filesz = ( mb->load_end_addr ?
+ ( mb->load_end_addr - mb->load_addr ) :
( image->len - offset ) );
- memsz = ( hdr->mb.bss_end_addr ?
- ( hdr->mb.bss_end_addr - hdr->mb.load_addr ) : filesz );
- buffer = phys_to_user ( hdr->mb.load_addr );
+ if ( filesz > image->len ) {
+ DBGC ( image, "MULTIBOOT %s has overlength data\n",
+ image->name );
+ return -EINVAL;
+ }
+
+ /* Calculate length of uninitialised data */
+ memsz = ( mb->bss_end_addr ?
+ ( mb->bss_end_addr - mb->load_addr ) : filesz );
+ DBGC ( image, "MULTIBOOT %s loading [%zx,%zx) to [%x,%zx,%zx)\n",
+ image->name, offset, ( offset + filesz ), mb->load_addr,
+ ( mb->load_addr + filesz ), ( mb->load_addr + memsz ) );
+
+ /* Verify and prepare segment */
+ buffer = phys_to_virt ( mb->load_addr );
if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
- DBGC ( image, "MULTIBOOT %p could not prepare segment: %s\n",
- image, strerror ( rc ) );
+ DBGC ( image, "MULTIBOOT %s could not prepare segment: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
/* Copy image to segment */
- memcpy_user ( buffer, 0, image->data, offset, filesz );
+ memcpy ( buffer, ( image->data + offset ), filesz );
/* Record execution entry point and maximum used address */
- *entry = hdr->mb.entry_addr;
- *max = ( hdr->mb.load_addr + memsz );
+ *entry = mb->entry_addr;
+ *max = ( mb->load_addr + memsz );
return 0;
}
@@ -373,8 +385,8 @@ static int multiboot_load_elf ( struct image *image, physaddr_t *entry,
/* Load ELF image*/
if ( ( rc = elf_load ( image, entry, max ) ) != 0 ) {
- DBGC ( image, "MULTIBOOT %p ELF image failed to load: %s\n",
- image, strerror ( rc ) );
+ DBGC ( image, "MULTIBOOT %s ELF image failed to load: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
@@ -388,22 +400,24 @@ static int multiboot_load_elf ( struct image *image, physaddr_t *entry,
* @ret rc Return status code
*/
static int multiboot_exec ( struct image *image ) {
- struct multiboot_header_info hdr;
+ const struct multiboot_header *mb;
physaddr_t entry;
physaddr_t max;
+ int offset;
int rc;
/* Locate multiboot header, if present */
- if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
- DBGC ( image, "MULTIBOOT %p has no multiboot header\n",
- image );
+ offset = multiboot_find_header ( image );
+ if ( offset < 0 ) {
+ rc = offset;
return rc;
}
+ mb = ( image->data + offset );
/* Abort if we detect flags that we cannot support */
- if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) {
- DBGC ( image, "MULTIBOOT %p flags %08x not supported\n",
- image, ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) );
+ if ( mb->flags & MB_UNSUPPORTED_FLAGS ) {
+ DBGC ( image, "MULTIBOOT %s flags %#08x not supported\n",
+ image->name, ( mb->flags & MB_UNSUPPORTED_FLAGS ) );
return -ENOTSUP;
}
@@ -413,8 +427,10 @@ static int multiboot_exec ( struct image *image ) {
* behaviour.
*/
if ( ( ( rc = multiboot_load_elf ( image, &entry, &max ) ) != 0 ) &&
- ( ( rc = multiboot_load_raw ( image, &hdr, &entry, &max ) ) != 0 ))
+ ( ( rc = multiboot_load_raw ( image, offset, &entry,
+ &max ) ) != 0 ) ) {
return rc;
+ }
/* Populate multiboot information structure */
memset ( &mbinfo, 0, sizeof ( mbinfo ) );
@@ -444,8 +460,8 @@ static int multiboot_exec ( struct image *image ) {
( sizeof(mbmemmap) / sizeof(mbmemmap[0]) ) );
/* Jump to OS with flat physical addressing */
- DBGC ( image, "MULTIBOOT %p starting execution at %lx\n",
- image, entry );
+ DBGC ( image, "MULTIBOOT %s starting execution at %lx\n",
+ image->name, entry );
__asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t"
"call *%%edi\n\t"
"popl %%ebp\n\t" )
@@ -454,7 +470,7 @@ static int multiboot_exec ( struct image *image ) {
"D" ( entry )
: "ecx", "edx", "esi", "memory" );
- DBGC ( image, "MULTIBOOT %p returned\n", image );
+ DBGC ( image, "MULTIBOOT %s returned\n", image->name );
/* It isn't safe to continue after calling shutdown() */
while ( 1 ) {}
@@ -469,17 +485,19 @@ static int multiboot_exec ( struct image *image ) {
* @ret rc Return status code
*/
static int multiboot_probe ( struct image *image ) {
- struct multiboot_header_info hdr;
+ const struct multiboot_header *mb;
+ int offset;
int rc;
/* Locate multiboot header, if present */
- if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
- DBGC ( image, "MULTIBOOT %p has no multiboot header\n",
- image );
+ offset = multiboot_find_header ( image );
+ if ( offset < 0 ) {
+ rc = offset;
return rc;
}
- DBGC ( image, "MULTIBOOT %p found header with flags %08x\n",
- image, hdr.mb.flags );
+ mb = ( image->data + offset );
+ DBGC ( image, "MULTIBOOT %s found header at +%#x with flags %#08x\n",
+ image->name, offset, mb->flags );
return 0;
}
diff --git a/src/arch/x86/image/nbi.c b/src/arch/x86/image/nbi.c
index b691bee20..e0a46758e 100644
--- a/src/arch/x86/image/nbi.c
+++ b/src/arch/x86/image/nbi.c
@@ -1,3 +1,4 @@
+#include <string.h>
#include <errno.h>
#include <assert.h>
#include <realmode.h>
@@ -106,12 +107,12 @@ struct ebinfo {
* @ret rc Return status code
*/
static int nbi_prepare_segment ( struct image *image, size_t offset __unused,
- userptr_t dest, size_t filesz, size_t memsz ){
+ void *dest, size_t filesz, size_t memsz ) {
int rc;
if ( ( rc = prep_segment ( dest, filesz, memsz ) ) != 0 ) {
- DBGC ( image, "NBI %p could not prepare segment: %s\n",
- image, strerror ( rc ) );
+ DBGC ( image, "NBI %s could not prepare segment: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
@@ -129,9 +130,9 @@ static int nbi_prepare_segment ( struct image *image, size_t offset __unused,
* @ret rc Return status code
*/
static int nbi_load_segment ( struct image *image, size_t offset,
- userptr_t dest, size_t filesz,
+ void *dest, size_t filesz,
size_t memsz __unused ) {
- memcpy_user ( dest, 0, image->data, offset, filesz );
+ memcpy ( dest, ( image->data + offset ), filesz );
return 0;
}
@@ -144,22 +145,22 @@ static int nbi_load_segment ( struct image *image, size_t offset,
* @ret rc Return status code
*/
static int nbi_process_segments ( struct image *image,
- struct imgheader *imgheader,
+ const struct imgheader *imgheader,
int ( * process ) ( struct image *image,
size_t offset,
- userptr_t dest,
+ void *dest,
size_t filesz,
size_t memsz ) ) {
- struct segheader sh;
+ const struct segheader *sh;
size_t offset = 0;
size_t sh_off;
- userptr_t dest;
+ void *dest;
size_t filesz;
size_t memsz;
int rc;
/* Copy image header to target location */
- dest = real_to_user ( imgheader->location.segment,
+ dest = real_to_virt ( imgheader->location.segment,
imgheader->location.offset );
filesz = memsz = NBI_HEADER_LENGTH;
if ( ( rc = process ( image, offset, dest, filesz, memsz ) ) != 0 )
@@ -170,32 +171,32 @@ static int nbi_process_segments ( struct image *image,
sh_off = NBI_LENGTH ( imgheader->length );
do {
/* Read segment header */
- copy_from_user ( &sh, image->data, sh_off, sizeof ( sh ) );
- if ( sh.length == 0 ) {
+ sh = ( image->data + sh_off );
+ if ( sh->length == 0 ) {
/* Avoid infinite loop? */
- DBGC ( image, "NBI %p invalid segheader length 0\n",
- image );
+ DBGC ( image, "NBI %s invalid segheader length 0\n",
+ image->name );
return -ENOEXEC;
}
/* Calculate segment load address */
- switch ( NBI_LOADADDR_FLAGS ( sh.flags ) ) {
+ switch ( NBI_LOADADDR_FLAGS ( sh->flags ) ) {
case NBI_LOADADDR_ABS:
- dest = phys_to_user ( sh.loadaddr );
+ dest = phys_to_virt ( sh->loadaddr );
break;
case NBI_LOADADDR_AFTER:
- dest = userptr_add ( dest, memsz + sh.loadaddr );
+ dest = ( dest + memsz + sh->loadaddr );
break;
case NBI_LOADADDR_BEFORE:
- dest = userptr_add ( dest, -sh.loadaddr );
+ dest = ( dest - sh->loadaddr );
break;
case NBI_LOADADDR_END:
/* Not correct according to the spec, but
* maintains backwards compatibility with
* previous versions of Etherboot.
*/
- dest = phys_to_user ( ( extmemsize() + 1024 ) * 1024
- - sh.loadaddr );
+ dest = phys_to_virt ( ( extmemsize() + 1024 ) * 1024
+ - sh->loadaddr );
break;
default:
/* Cannot be reached */
@@ -203,10 +204,11 @@ static int nbi_process_segments ( struct image *image,
}
/* Process this segment */
- filesz = sh.imglength;
- memsz = sh.memlength;
+ filesz = sh->imglength;
+ memsz = sh->memlength;
if ( ( offset + filesz ) > image->len ) {
- DBGC ( image, "NBI %p segment outside file\n", image );
+ DBGC ( image, "NBI %s segment outside file\n",
+ image->name );
return -ENOEXEC;
}
if ( ( rc = process ( image, offset, dest,
@@ -216,17 +218,18 @@ static int nbi_process_segments ( struct image *image,
offset += filesz;
/* Next segheader */
- sh_off += NBI_LENGTH ( sh.length );
+ sh_off += NBI_LENGTH ( sh->length );
if ( sh_off >= NBI_HEADER_LENGTH ) {
- DBGC ( image, "NBI %p header overflow\n", image );
+ DBGC ( image, "NBI %s header overflow\n",
+ image->name );
return -ENOEXEC;
}
- } while ( ! NBI_LAST_SEGHEADER ( sh.flags ) );
+ } while ( ! NBI_LAST_SEGHEADER ( sh->flags ) );
if ( offset != image->len ) {
- DBGC ( image, "NBI %p length wrong (file %zd, metadata %zd)\n",
- image, image->len, offset );
+ DBGC ( image, "NBI %s length wrong (file %zd, metadata %zd)\n",
+ image->name, image->len, offset );
return -ENOEXEC;
}
@@ -239,12 +242,13 @@ static int nbi_process_segments ( struct image *image,
* @v imgheader Image header information
* @ret rc Return status code, if image returns
*/
-static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
+static int nbi_boot16 ( struct image *image,
+ const struct imgheader *imgheader ) {
int discard_D, discard_S, discard_b;
int32_t rc;
- DBGC ( image, "NBI %p executing 16-bit image at %04x:%04x\n", image,
- imgheader->execaddr.segoff.segment,
+ DBGC ( image, "NBI %s executing 16-bit image at %04x:%04x\n",
+ image->name, imgheader->execaddr.segoff.segment,
imgheader->execaddr.segoff.offset );
__asm__ __volatile__ (
@@ -277,7 +281,8 @@ static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
* @v imgheader Image header information
* @ret rc Return status code, if image returns
*/
-static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) {
+static int nbi_boot32 ( struct image *image,
+ const struct imgheader *imgheader ) {
struct ebinfo loaderinfo = {
product_major_version, product_minor_version,
0
@@ -285,8 +290,8 @@ static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) {
int discard_D, discard_S, discard_b;
int32_t rc;
- DBGC ( image, "NBI %p executing 32-bit image at %lx\n",
- image, imgheader->execaddr.linear );
+ DBGC ( image, "NBI %s executing 32-bit image at %lx\n",
+ image->name, imgheader->execaddr.linear );
/* Jump to OS with flat physical addressing */
__asm__ __volatile__ (
@@ -321,14 +326,15 @@ static int nbi_prepare_dhcp ( struct image *image ) {
boot_netdev = last_opened_netdev();
if ( ! boot_netdev ) {
- DBGC ( image, "NBI %p could not identify a network device\n",
- image );
+ DBGC ( image, "NBI %s could not identify a network device\n",
+ image->name );
return -ENODEV;
}
if ( ( rc = create_fakedhcpack ( boot_netdev, basemem_packet,
sizeof ( basemem_packet ) ) ) != 0 ) {
- DBGC ( image, "NBI %p failed to build DHCP packet\n", image );
+ DBGC ( image, "NBI %s failed to build DHCP packet\n",
+ image->name );
return rc;
}
@@ -342,15 +348,15 @@ static int nbi_prepare_dhcp ( struct image *image ) {
* @ret rc Return status code
*/
static int nbi_exec ( struct image *image ) {
- struct imgheader imgheader;
+ const struct imgheader *imgheader;
int may_return;
int rc;
/* Retrieve image header */
- copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) );
+ imgheader = image->data;
- DBGC ( image, "NBI %p placing header at %hx:%hx\n", image,
- imgheader.location.segment, imgheader.location.offset );
+ DBGC ( image, "NBI %s placing header at %hx:%hx\n", image->name,
+ imgheader->location.segment, imgheader->location.offset );
/* NBI files can have overlaps between segments; the bss of
* one segment may overlap the initialised data of another. I
@@ -359,10 +365,10 @@ static int nbi_exec ( struct image *image ) {
* passes: first to initialise the segments, then to copy the
* data. This avoids zeroing out already-copied data.
*/
- if ( ( rc = nbi_process_segments ( image, &imgheader,
+ if ( ( rc = nbi_process_segments ( image, imgheader,
nbi_prepare_segment ) ) != 0 )
return rc;
- if ( ( rc = nbi_process_segments ( image, &imgheader,
+ if ( ( rc = nbi_process_segments ( image, imgheader,
nbi_load_segment ) ) != 0 )
return rc;
@@ -371,25 +377,25 @@ static int nbi_exec ( struct image *image ) {
return rc;
/* Shut down now if NBI image will not return */
- may_return = NBI_PROGRAM_RETURNS ( imgheader.flags );
+ may_return = NBI_PROGRAM_RETURNS ( imgheader->flags );
if ( ! may_return )
shutdown_boot();
/* Execute NBI image */
- if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) {
- rc = nbi_boot32 ( image, &imgheader );
+ if ( NBI_LINEAR_EXEC_ADDR ( imgheader->flags ) ) {
+ rc = nbi_boot32 ( image, imgheader );
} else {
- rc = nbi_boot16 ( image, &imgheader );
+ rc = nbi_boot16 ( image, imgheader );
}
if ( ! may_return ) {
/* Cannot continue after shutdown() called */
- DBGC ( image, "NBI %p returned %d from non-returnable image\n",
- image, rc );
+ DBGC ( image, "NBI %s returned %d from non-returnable image\n",
+ image->name, rc );
while ( 1 ) {}
}
- DBGC ( image, "NBI %p returned %d\n", image, rc );
+ DBGC ( image, "NBI %s returned %d\n", image->name, rc );
return rc;
}
@@ -401,18 +407,19 @@ static int nbi_exec ( struct image *image ) {
* @ret rc Return status code
*/
static int nbi_probe ( struct image *image ) {
- struct imgheader imgheader;
+ const struct imgheader *imgheader;
/* If we don't have enough data give up */
if ( image->len < NBI_HEADER_LENGTH ) {
- DBGC ( image, "NBI %p too short for an NBI image\n", image );
+ DBGC ( image, "NBI %s too short for an NBI image\n",
+ image->name );
return -ENOEXEC;
}
+ imgheader = image->data;
/* Check image header */
- copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) );
- if ( imgheader.magic != NBI_MAGIC ) {
- DBGC ( image, "NBI %p has no NBI signature\n", image );
+ if ( imgheader->magic != NBI_MAGIC ) {
+ DBGC ( image, "NBI %s has no NBI signature\n", image->name );
return -ENOEXEC;
}
diff --git a/src/arch/x86/image/pxe_image.c b/src/arch/x86/image/pxe_image.c
index b6bcb18b4..f88eadc09 100644
--- a/src/arch/x86/image/pxe_image.c
+++ b/src/arch/x86/image/pxe_image.c
@@ -30,10 +30,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
+#include <string.h>
#include <pxe.h>
#include <pxe_call.h>
#include <pic8259.h>
-#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/segment.h>
#include <ipxe/netdevice.h>
@@ -54,24 +54,24 @@ const char *pxe_cmdline;
* @ret rc Return status code
*/
static int pxe_exec ( struct image *image ) {
- userptr_t buffer = real_to_user ( 0, 0x7c00 );
+ void *buffer = real_to_virt ( 0, 0x7c00 );
struct net_device *netdev;
int rc;
/* Verify and prepare segment */
if ( ( rc = prep_segment ( buffer, image->len, image->len ) ) != 0 ) {
- DBGC ( image, "IMAGE %p could not prepare segment: %s\n",
- image, strerror ( rc ) );
+ DBGC ( image, "IMAGE %s could not prepare segment: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
/* Copy image to segment */
- memcpy_user ( buffer, 0, image->data, 0, image->len );
+ memcpy ( buffer, image->data, image->len );
/* Arbitrarily pick the most recently opened network device */
if ( ( netdev = last_opened_netdev() ) == NULL ) {
- DBGC ( image, "IMAGE %p could not locate PXE net device\n",
- image );
+ DBGC ( image, "IMAGE %s could not locate PXE net device\n",
+ image->name );
return -ENODEV;
}
netdev_get ( netdev );
@@ -142,7 +142,7 @@ int pxe_probe ( struct image *image ) {
* @ret rc Return status code
*/
int pxe_probe_no_mz ( struct image *image ) {
- uint16_t magic;
+ const uint16_t *magic;
int rc;
/* Probe PXE image */
@@ -152,11 +152,11 @@ int pxe_probe_no_mz ( struct image *image ) {
/* Reject image with an "MZ" signature which may indicate an
* EFI image incorrectly handed out to a BIOS system.
*/
- if ( image->len >= sizeof ( magic ) ) {
- copy_from_user ( &magic, image->data, 0, sizeof ( magic ) );
- if ( magic == cpu_to_le16 ( EFI_IMAGE_DOS_SIGNATURE ) ) {
- DBGC ( image, "IMAGE %p may be an EFI image\n",
- image );
+ if ( image->len >= sizeof ( *magic ) ) {
+ magic = image->data;
+ if ( *magic == cpu_to_le16 ( EFI_IMAGE_DOS_SIGNATURE ) ) {
+ DBGC ( image, "IMAGE %s may be an EFI image\n",
+ image->name );
return -ENOTTY;
}
}
diff --git a/src/arch/x86/image/sdi.c b/src/arch/x86/image/sdi.c
index fa2d0b73f..c0cded239 100644
--- a/src/arch/x86/image/sdi.c
+++ b/src/arch/x86/image/sdi.c
@@ -45,63 +45,36 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
FEATURE ( FEATURE_IMAGE, "SDI", DHCP_EB_FEATURE_SDI, 1 );
/**
- * Parse SDI image header
- *
- * @v image SDI file
- * @v sdi SDI header to fill in
- * @ret rc Return status code
- */
-static int sdi_parse_header ( struct image *image, struct sdi_header *sdi ) {
-
- /* Sanity check */
- if ( image->len < sizeof ( *sdi ) ) {
- DBGC ( image, "SDI %p too short for SDI header\n", image );
- return -ENOEXEC;
- }
-
- /* Read in header */
- copy_from_user ( sdi, image->data, 0, sizeof ( *sdi ) );
-
- /* Check signature */
- if ( sdi->magic != SDI_MAGIC ) {
- DBGC ( image, "SDI %p is not an SDI image\n", image );
- return -ENOEXEC;
- }
-
- return 0;
-}
-
-/**
* Execute SDI image
*
* @v image SDI file
* @ret rc Return status code
*/
static int sdi_exec ( struct image *image ) {
- struct sdi_header sdi;
+ const struct sdi_header *sdi;
uint32_t sdiptr;
- int rc;
- /* Parse image header */
- if ( ( rc = sdi_parse_header ( image, &sdi ) ) != 0 )
- return rc;
+ /* Sanity check */
+ assert ( image->len >= sizeof ( *sdi ) );
+ sdi = image->data;
/* Check that image is bootable */
- if ( sdi.boot_size == 0 ) {
- DBGC ( image, "SDI %p is not bootable\n", image );
+ if ( sdi->boot_size == 0 ) {
+ DBGC ( image, "SDI %s is not bootable\n", image->name );
return -ENOTTY;
}
- DBGC ( image, "SDI %p image at %08lx+%08zx\n",
- image, user_to_phys ( image->data, 0 ), image->len );
- DBGC ( image, "SDI %p boot code at %08lx+%llx\n", image,
- user_to_phys ( image->data, sdi.boot_offset ), sdi.boot_size );
+ DBGC ( image, "SDI %s image at %08lx+%08zx\n",
+ image->name, virt_to_phys ( image->data ), image->len );
+ DBGC ( image, "SDI %s boot code at %08llx+%llx\n", image->name,
+ ( virt_to_phys ( image->data ) + sdi->boot_offset ),
+ sdi->boot_size );
/* Copy boot code */
- memcpy_user ( real_to_user ( SDI_BOOT_SEG, SDI_BOOT_OFF ), 0,
- image->data, sdi.boot_offset, sdi.boot_size );
+ memcpy ( real_to_virt ( SDI_BOOT_SEG, SDI_BOOT_OFF ),
+ ( image->data + sdi->boot_offset ), sdi->boot_size );
/* Jump to boot code */
- sdiptr = ( user_to_phys ( image->data, 0 ) | SDI_WTF );
+ sdiptr = ( virt_to_phys ( image->data ) | SDI_WTF );
__asm__ __volatile__ ( REAL_CODE ( "ljmp %0, %1\n\t" )
: : "i" ( SDI_BOOT_SEG ),
"i" ( SDI_BOOT_OFF ),
@@ -122,12 +95,22 @@ static int sdi_exec ( struct image *image ) {
* @ret rc Return status code
*/
static int sdi_probe ( struct image *image ) {
- struct sdi_header sdi;
- int rc;
+ const struct sdi_header *sdi;
- /* Parse image */
- if ( ( rc = sdi_parse_header ( image, &sdi ) ) != 0 )
- return rc;
+ /* Sanity check */
+ if ( image->len < sizeof ( *sdi ) ) {
+ DBGC ( image, "SDI %s too short for SDI header\n",
+ image->name );
+ return -ENOEXEC;
+ }
+ sdi = image->data;
+
+ /* Check signature */
+ if ( sdi->magic != SDI_MAGIC ) {
+ DBGC ( image, "SDI %s is not an SDI image\n",
+ image->name );
+ return -ENOEXEC;
+ }
return 0;
}
diff --git a/src/arch/x86/image/ucode.c b/src/arch/x86/image/ucode.c
index 499c0a940..fd4689e00 100644
--- a/src/arch/x86/image/ucode.c
+++ b/src/arch/x86/image/ucode.c
@@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <assert.h>
#include <errno.h>
#include <ipxe/uaccess.h>
@@ -149,41 +150,38 @@ static const char * ucode_vendor_name ( const union ucode_vendor_id *vendor ) {
*
* @v update Microcode update
* @v control Microcode update control
+ * @v status Microcode update status
* @v summary Microcode update summary
* @v id APIC ID
* @v optional Status report is optional
* @ret rc Return status code
*/
-static int ucode_status ( struct ucode_update *update,
- struct ucode_control *control,
+static int ucode_status ( const struct ucode_update *update,
+ const struct ucode_control *control,
+ const struct ucode_status *status,
struct ucode_summary *summary,
unsigned int id, int optional ) {
- struct ucode_status status;
struct ucode_descriptor *desc;
/* Sanity check */
assert ( id <= control->apic_max );
- /* Read status report */
- copy_from_user ( &status, phys_to_user ( control->status ),
- ( id * sizeof ( status ) ), sizeof ( status ) );
-
/* Ignore empty optional status reports */
- if ( optional && ( ! status.signature ) )
+ if ( optional && ( ! status->signature ) )
return 0;
DBGC ( update, "UCODE %#08x signature %#08x ucode %#08x->%#08x\n",
- id, status.signature, status.before, status.after );
+ id, status->signature, status->before, status->after );
/* Check CPU signature */
- if ( ! status.signature ) {
+ if ( ! status->signature ) {
DBGC2 ( update, "UCODE %#08x has no signature\n", id );
return -ENOENT;
}
/* Check APIC ID is correct */
- if ( status.id != id ) {
+ if ( status->id != id ) {
DBGC ( update, "UCODE %#08x wrong APIC ID %#08x\n",
- id, status.id );
+ id, status->id );
return -EINVAL;
}
@@ -195,29 +193,29 @@ static int ucode_status ( struct ucode_update *update,
}
/* Check microcode was not downgraded */
- if ( status.after < status.before ) {
+ if ( status->after < status->before ) {
DBGC ( update, "UCODE %#08x was downgraded %#08x->%#08x\n",
- id, status.before, status.after );
+ id, status->before, status->after );
return -ENOTTY;
}
/* Check that expected updates (if any) were applied */
for ( desc = update->desc ; desc->signature ; desc++ ) {
- if ( ( desc->signature == status.signature ) &&
- ( status.after < desc->version ) ) {
+ if ( ( desc->signature == status->signature ) &&
+ ( status->after < desc->version ) ) {
DBGC ( update, "UCODE %#08x failed update %#08x->%#08x "
- "(wanted %#08x)\n", id, status.before,
- status.after, desc->version );
+ "(wanted %#08x)\n", id, status->before,
+ status->after, desc->version );
return -EIO;
}
}
/* Update summary */
summary->count++;
- if ( status.before < summary->low )
- summary->low = status.before;
- if ( status.after > summary->high )
- summary->high = status.after;
+ if ( status->before < summary->low )
+ summary->low = status->before;
+ if ( status->after > summary->high )
+ summary->high = status->after;
return 0;
}
@@ -231,13 +229,13 @@ static int ucode_status ( struct ucode_update *update,
* @ret rc Return status code
*/
static int ucode_update_all ( struct image *image,
- struct ucode_update *update,
+ const struct ucode_update *update,
struct ucode_summary *summary ) {
struct ucode_control control;
struct ucode_vendor *vendor;
- userptr_t status;
+ struct ucode_status *status;
unsigned int max;
- unsigned int i;
+ unsigned int id;
size_t len;
int rc;
@@ -248,7 +246,7 @@ static int ucode_update_all ( struct image *image,
/* Allocate status reports */
max = mp_max_cpuid();
- len = ( ( max + 1 ) * sizeof ( struct ucode_status ) );
+ len = ( ( max + 1 ) * sizeof ( *status ) );
status = umalloc ( len );
if ( ! status ) {
DBGC ( image, "UCODE %s could not allocate %d status reports\n",
@@ -256,12 +254,12 @@ static int ucode_update_all ( struct image *image,
rc = -ENOMEM;
goto err_alloc;
}
- memset_user ( status, 0, 0, len );
+ memset ( status, 0, len );
/* Construct control structure */
memset ( &control, 0, sizeof ( control ) );
control.desc = virt_to_phys ( update->desc );
- control.status = user_to_phys ( status, 0 );
+ control.status = virt_to_phys ( status );
vendor = update->vendor;
if ( vendor ) {
control.ver_clear = vendor->ver_clear;
@@ -274,8 +272,9 @@ static int ucode_update_all ( struct image *image,
/* Update microcode on boot processor */
mp_exec_boot ( ucode_update, &control );
- if ( ( rc = ucode_status ( update, &control, summary,
- mp_boot_cpuid(), 0 ) ) != 0 ) {
+ id = mp_boot_cpuid();
+ if ( ( rc = ucode_status ( update, &control, &status[id],
+ summary, id, 0 ) ) != 0 ) {
DBGC ( image, "UCODE %s failed on boot processor: %s\n",
image->name, strerror ( rc ) );
goto err_boot;
@@ -293,9 +292,9 @@ static int ucode_update_all ( struct image *image,
/* Check status reports */
summary->count = 0;
- for ( i = 0 ; i <= max ; i++ ) {
- if ( ( rc = ucode_status ( update, &control, summary,
- i, 1 ) ) != 0 ) {
+ for ( id = 0 ; id <= max ; id++ ) {
+ if ( ( rc = ucode_status ( update, &control, &status[id],
+ summary, id, 1 ) ) != 0 ) {
goto err_status;
}
}
@@ -359,24 +358,22 @@ static void ucode_describe ( struct image *image, size_t start,
* @ret rc Return status code
*/
static int ucode_verify ( struct image *image, size_t start, size_t len ) {
- uint32_t checksum = 0;
- uint32_t dword;
- size_t offset;
+ const uint32_t *dword;
+ uint32_t checksum;
+ unsigned int count;
/* Check length is a multiple of dwords */
- if ( ( len % sizeof ( dword ) ) != 0 ) {
+ if ( ( len % sizeof ( *dword ) ) != 0 ) {
DBGC ( image, "UCODE %s+%#04zx invalid length %#zx\n",
image->name, start, len );
return -EINVAL;
}
+ dword = ( image->data + start );
/* Calculate checksum */
- for ( offset = start ; len ;
- offset += sizeof ( dword ), len -= sizeof ( dword ) ) {
- copy_from_user ( &dword, image->data, offset,
- sizeof ( dword ) );
- checksum += dword;
- }
+ count = ( len / sizeof ( *dword ) );
+ for ( checksum = 0 ; count ; count-- )
+ checksum += *(dword++);
if ( checksum != 0 ) {
DBGC ( image, "UCODE %s+%#04zx bad checksum %#08x\n",
image->name, start, checksum );
@@ -396,9 +393,9 @@ static int ucode_verify ( struct image *image, size_t start, size_t len ) {
*/
static int ucode_parse_intel ( struct image *image, size_t start,
struct ucode_update *update ) {
- struct intel_ucode_header hdr;
- struct intel_ucode_ext_header exthdr;
- struct intel_ucode_ext ext;
+ const struct intel_ucode_header *hdr;
+ const struct intel_ucode_ext_header *exthdr;
+ const struct intel_ucode_ext *ext;
struct ucode_descriptor desc;
size_t remaining;
size_t offset;
@@ -409,27 +406,27 @@ static int ucode_parse_intel ( struct image *image, size_t start,
/* Read header */
remaining = ( image->len - start );
- if ( remaining < sizeof ( hdr ) ) {
+ if ( remaining < sizeof ( *hdr ) ) {
DBGC ( image, "UCODE %s+%#04zx too small for Intel header\n",
image->name, start );
return -ENOEXEC;
}
- copy_from_user ( &hdr, image->data, start, sizeof ( hdr ) );
+ hdr = ( image->data + start );
/* Determine lengths */
- data_len = hdr.data_len;
+ data_len = hdr->data_len;
if ( ! data_len )
data_len = INTEL_UCODE_DATA_LEN;
- len = hdr.len;
+ len = hdr->len;
if ( ! len )
- len = ( sizeof ( hdr ) + data_len );
+ len = ( sizeof ( *hdr ) + data_len );
/* Verify a selection of fields */
- if ( ( hdr.hver != INTEL_UCODE_HVER ) ||
- ( hdr.lver != INTEL_UCODE_LVER ) ||
- ( len < sizeof ( hdr ) ) ||
+ if ( ( hdr->hver != INTEL_UCODE_HVER ) ||
+ ( hdr->lver != INTEL_UCODE_LVER ) ||
+ ( len < sizeof ( *hdr ) ) ||
( len > remaining ) ||
- ( data_len > ( len - sizeof ( hdr ) ) ) ||
+ ( data_len > ( len - sizeof ( *hdr ) ) ) ||
( ( data_len % sizeof ( uint32_t ) ) != 0 ) ||
( ( len % INTEL_UCODE_ALIGN ) != 0 ) ) {
DBGC2 ( image, "UCODE %s+%#04zx is not an Intel update\n",
@@ -444,48 +441,46 @@ static int ucode_parse_intel ( struct image *image, size_t start,
return rc;
/* Populate descriptor */
- desc.signature = hdr.signature;
- desc.version = hdr.version;
- desc.address = user_to_phys ( image->data,
- ( start + sizeof ( hdr ) ) );
+ desc.signature = hdr->signature;
+ desc.version = hdr->version;
+ desc.address = ( virt_to_phys ( image->data ) + start +
+ sizeof ( *hdr ) );
/* Add non-extended descriptor, if applicable */
- ucode_describe ( image, start, &ucode_intel, &desc, hdr.platforms,
+ ucode_describe ( image, start, &ucode_intel, &desc, hdr->platforms,
update );
/* Construct extended descriptors, if applicable */
- offset = ( sizeof ( hdr ) + data_len );
- if ( offset <= ( len - sizeof ( exthdr ) ) ) {
+ offset = ( sizeof ( *hdr ) + data_len );
+ if ( offset <= ( len - sizeof ( *exthdr ) ) ) {
/* Read extended header */
- copy_from_user ( &exthdr, image->data, ( start + offset ),
- sizeof ( exthdr ) );
- offset += sizeof ( exthdr );
+ exthdr = ( image->data + start + offset );
+ offset += sizeof ( *exthdr );
/* Read extended signatures */
- for ( i = 0 ; i < exthdr.count ; i++ ) {
+ for ( i = 0 ; i < exthdr->count ; i++ ) {
/* Read extended signature */
- if ( offset > ( len - sizeof ( ext ) ) ) {
+ if ( offset > ( len - sizeof ( *ext ) ) ) {
DBGC ( image, "UCODE %s+%#04zx extended "
"signature overrun\n",
image->name, start );
return -EINVAL;
}
- copy_from_user ( &ext, image->data, ( start + offset ),
- sizeof ( ext ) );
- offset += sizeof ( ext );
+ ext = ( image->data + start + offset );
+ offset += sizeof ( *ext );
/* Avoid duplicating non-extended descriptor */
- if ( ( ext.signature == hdr.signature ) &&
- ( ext.platforms == hdr.platforms ) ) {
+ if ( ( ext->signature == hdr->signature ) &&
+ ( ext->platforms == hdr->platforms ) ) {
continue;
}
/* Construct descriptor, if applicable */
- desc.signature = ext.signature;
+ desc.signature = ext->signature;
ucode_describe ( image, start, &ucode_intel, &desc,
- ext.platforms, update );
+ ext->platforms, update );
}
}
@@ -502,10 +497,10 @@ static int ucode_parse_intel ( struct image *image, size_t start,
*/
static int ucode_parse_amd ( struct image *image, size_t start,
struct ucode_update *update ) {
- struct amd_ucode_header hdr;
- struct amd_ucode_equivalence equiv;
- struct amd_ucode_patch_header phdr;
- struct amd_ucode_patch patch;
+ const struct amd_ucode_header *hdr;
+ const struct amd_ucode_equivalence *equiv;
+ const struct amd_ucode_patch_header *phdr;
+ const struct amd_ucode_patch *patch;
struct ucode_descriptor desc;
size_t remaining;
size_t offset;
@@ -515,91 +510,85 @@ static int ucode_parse_amd ( struct image *image, size_t start,
/* Read header */
remaining = ( image->len - start );
- if ( remaining < sizeof ( hdr ) ) {
+ if ( remaining < sizeof ( *hdr ) ) {
DBGC ( image, "UCODE %s+%#04zx too small for AMD header\n",
image->name, start );
return -ENOEXEC;
}
- copy_from_user ( &hdr, image->data, start, sizeof ( hdr ) );
+ hdr = ( image->data + start );
/* Check header */
- if ( hdr.magic != AMD_UCODE_MAGIC ) {
+ if ( hdr->magic != AMD_UCODE_MAGIC ) {
DBGC2 ( image, "UCODE %s+%#04zx is not an AMD update\n",
image->name, start );
return -ENOEXEC;
}
DBGC2 ( image, "UCODE %s+%#04zx is an AMD update\n",
image->name, start );
- if ( hdr.type != AMD_UCODE_EQUIV_TYPE ) {
+ if ( hdr->type != AMD_UCODE_EQUIV_TYPE ) {
DBGC ( image, "UCODE %s+%#04zx unsupported equivalence table "
- "type %d\n", image->name, start, hdr.type );
+ "type %d\n", image->name, start, hdr->type );
return -ENOTSUP;
}
- if ( hdr.len > ( remaining - sizeof ( hdr ) ) ) {
+ if ( hdr->len > ( remaining - sizeof ( *hdr ) ) ) {
DBGC ( image, "UCODE %s+%#04zx truncated equivalence table\n",
image->name, start );
return -EINVAL;
}
/* Count number of equivalence table entries */
- offset = sizeof ( hdr );
- for ( count = 0 ; offset < ( sizeof ( hdr ) + hdr.len ) ;
- count++, offset += sizeof ( equiv ) ) {
- copy_from_user ( &equiv, image->data, ( start + offset ),
- sizeof ( equiv ) );
- if ( ! equiv.signature )
+ offset = sizeof ( *hdr );
+ equiv = ( image->data + start + offset );
+ for ( count = 0 ; offset < ( sizeof ( *hdr ) + hdr->len ) ;
+ count++, offset += sizeof ( *equiv ) ) {
+ if ( ! equiv[count].signature )
break;
}
DBGC2 ( image, "UCODE %s+%#04zx has %d equivalence table entries\n",
image->name, start, count );
/* Parse available updates */
- offset = ( sizeof ( hdr ) + hdr.len );
+ offset = ( sizeof ( *hdr ) + hdr->len );
used = 0;
while ( used < count ) {
/* Read patch header */
- if ( ( offset + sizeof ( phdr ) ) > remaining ) {
+ if ( ( offset + sizeof ( *phdr ) ) > remaining ) {
DBGC ( image, "UCODE %s+%#04zx truncated patch "
"header\n", image->name, start );
return -EINVAL;
}
- copy_from_user ( &phdr, image->data, ( start + offset ),
- sizeof ( phdr ) );
- offset += sizeof ( phdr );
+ phdr = ( image->data + start + offset );
+ offset += sizeof ( *phdr );
/* Validate patch header */
- if ( phdr.type != AMD_UCODE_PATCH_TYPE ) {
+ if ( phdr->type != AMD_UCODE_PATCH_TYPE ) {
DBGC ( image, "UCODE %s+%#04zx unsupported patch type "
- "%d\n", image->name, start, phdr.type );
+ "%d\n", image->name, start, phdr->type );
return -ENOTSUP;
}
- if ( phdr.len < sizeof ( patch ) ) {
+ if ( phdr->len < sizeof ( *patch ) ) {
DBGC ( image, "UCODE %s+%#04zx underlength patch\n",
image->name, start );
return -EINVAL;
}
- if ( phdr.len > ( remaining - offset ) ) {
+ if ( phdr->len > ( remaining - offset ) ) {
DBGC ( image, "UCODE %s+%#04zx truncated patch\n",
image->name, start );
return -EINVAL;
}
/* Read patch and construct descriptor */
- copy_from_user ( &patch, image->data, ( start + offset ),
- sizeof ( patch ) );
- desc.version = patch.version;
- desc.address = user_to_phys ( image->data, ( start + offset ) );
- offset += phdr.len;
+ patch = ( image->data + start + offset );
+ desc.version = patch->version;
+ desc.address = ( virt_to_phys ( image->data ) +
+ start + offset );
+ offset += phdr->len;
/* Parse equivalence table to find matching signatures */
for ( i = 0 ; i < count ; i++ ) {
- copy_from_user ( &equiv, image->data,
- ( start + sizeof ( hdr ) +
- ( i * ( sizeof ( equiv ) ) ) ),
- sizeof ( equiv ) );
- if ( patch.id == equiv.id ) {
- desc.signature = equiv.signature;
+ if ( patch->id == equiv[i].id ) {
+ desc.signature = equiv[i].signature;
ucode_describe ( image, start, &ucode_amd,
&desc, 0, update );
used++;
@@ -744,19 +733,19 @@ static int ucode_exec ( struct image *image ) {
* @ret rc Return status code
*/
static int ucode_probe ( struct image *image ) {
- union {
+ const union {
struct intel_ucode_header intel;
struct amd_ucode_header amd;
- } header;
+ } *header;
/* Sanity check */
- if ( image->len < sizeof ( header ) ) {
+ if ( image->len < sizeof ( *header ) ) {
DBGC ( image, "UCODE %s too short\n", image->name );
return -ENOEXEC;
}
/* Read first microcode image header */
- copy_from_user ( &header, image->data, 0, sizeof ( header ) );
+ header = image->data;
/* Check for something that looks like an Intel update
*
@@ -769,19 +758,19 @@ static int ucode_probe ( struct image *image ) {
* the image, and do not want to have a microcode image
* erroneously treated as a PXE boot executable.
*/
- if ( ( header.intel.hver == INTEL_UCODE_HVER ) &&
- ( header.intel.lver == INTEL_UCODE_LVER ) &&
- ( ( header.intel.date.century == 0x19 ) ||
- ( ( header.intel.date.century >= 0x20 ) &&
- ( header.intel.date.century <= 0x29 ) ) ) ) {
+ if ( ( header->intel.hver == INTEL_UCODE_HVER ) &&
+ ( header->intel.lver == INTEL_UCODE_LVER ) &&
+ ( ( header->intel.date.century == 0x19 ) ||
+ ( ( header->intel.date.century >= 0x20 ) &&
+ ( header->intel.date.century <= 0x29 ) ) ) ) {
DBGC ( image, "UCODE %s+%#04zx looks like an Intel update\n",
image->name, ( ( size_t ) 0 ) );
return 0;
}
/* Check for AMD update signature */
- if ( ( header.amd.magic == AMD_UCODE_MAGIC ) &&
- ( header.amd.type == AMD_UCODE_EQUIV_TYPE ) ) {
+ if ( ( header->amd.magic == AMD_UCODE_MAGIC ) &&
+ ( header->amd.type == AMD_UCODE_EQUIV_TYPE ) ) {
DBGC ( image, "UCODE %s+%#04zx looks like an AMD update\n",
image->name, ( ( size_t ) 0 ) );
return 0;
diff --git a/src/arch/x86/include/basemem.h b/src/arch/x86/include/basemem.h
index 01c2ea917..9ac918ac0 100644
--- a/src/arch/x86/include/basemem.h
+++ b/src/arch/x86/include/basemem.h
@@ -27,9 +27,4 @@ static inline unsigned int get_fbms ( void ) {
extern void set_fbms ( unsigned int new_fbms );
-/* Actually in hidemem.c, but putting it here avoids polluting the
- * architecture-independent include/hidemem.h.
- */
-extern void hide_basemem ( void );
-
#endif /* _BASEMEM_H */
diff --git a/src/arch/x86/include/bios_disks.h b/src/arch/x86/include/bios_disks.h
deleted file mode 100644
index 0dd7c4ebb..000000000
--- a/src/arch/x86/include/bios_disks.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef BIOS_DISKS_H
-#define BIOS_DISKS_H
-
-#include "dev.h"
-
-/*
- * Constants
- *
- */
-
-#define BIOS_DISK_MAX_NAME_LEN 6
-
-struct bios_disk_sector {
- char data[512];
-};
-
-/*
- * The location of a BIOS disk
- *
- */
-struct bios_disk_loc {
- uint8_t drive;
-};
-
-/*
- * A physical BIOS disk device
- *
- */
-struct bios_disk_device {
- char name[BIOS_DISK_MAX_NAME_LEN];
- uint8_t drive;
- uint8_t type;
-};
-
-/*
- * A BIOS disk driver, with a valid device ID range and naming
- * function.
- *
- */
-struct bios_disk_driver {
- void ( *fill_drive_name ) ( char *buf, uint8_t drive );
- uint8_t min_drive;
- uint8_t max_drive;
-};
-
-/*
- * Define a BIOS disk driver
- *
- */
-#define BIOS_DISK_DRIVER( _name, _fill_drive_name, _min_drive, _max_drive ) \
- static struct bios_disk_driver _name = { \
- .fill_drive_name = _fill_drive_name, \
- .min_drive = _min_drive, \
- .max_drive = _max_drive, \
- }
-
-/*
- * Functions in bios_disks.c
- *
- */
-
-
-/*
- * bios_disk bus global definition
- *
- */
-extern struct bus_driver bios_disk_driver;
-
-#endif /* BIOS_DISKS_H */
diff --git a/src/arch/x86/include/bits/acpi.h b/src/arch/x86/include/bits/acpi.h
index a6ff90804..287bdafeb 100644
--- a/src/arch/x86/include/bits/acpi.h
+++ b/src/arch/x86/include/bits/acpi.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/rsdp.h>
diff --git a/src/arch/x86/include/bits/bigint.h b/src/arch/x86/include/bits/bigint.h
index a6bc2ca1d..21cffa0cf 100644
--- a/src/arch/x86/include/bits/bigint.h
+++ b/src/arch/x86/include/bits/bigint.h
@@ -7,6 +7,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <string.h>
@@ -32,10 +33,12 @@ bigint_init_raw ( uint32_t *value0, unsigned int size,
long discard_c;
/* Copy raw data in reverse order, padding with zeros */
- __asm__ __volatile__ ( "\n1:\n\t"
+ __asm__ __volatile__ ( "jecxz 2f\n\t"
+ "\n1:\n\t"
"movb -1(%3,%1), %%al\n\t"
"stosb\n\t"
"loop 1b\n\t"
+ "\n2:\n\t"
"xorl %%eax, %%eax\n\t"
"mov %4, %1\n\t"
"rep stosb\n\t"
@@ -52,8 +55,9 @@ bigint_init_raw ( uint32_t *value0, unsigned int size,
* @v addend0 Element 0 of big integer to add
* @v value0 Element 0 of big integer to be added to
* @v size Number of elements
+ * @ret carry Carry flag
*/
-static inline __attribute__ (( always_inline )) void
+static inline __attribute__ (( always_inline )) int
bigint_add_raw ( const uint32_t *addend0, uint32_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
@@ -61,17 +65,20 @@ bigint_add_raw ( const uint32_t *addend0, uint32_t *value0,
long index;
void *discard_S;
long discard_c;
+ int carry;
__asm__ __volatile__ ( "xor %0, %0\n\t" /* Zero %0 and clear CF */
"\n1:\n\t"
"lodsl\n\t"
- "adcl %%eax, (%4,%0,4)\n\t"
+ "adcl %%eax, (%5,%0,4)\n\t"
"inc %0\n\t" /* Does not affect CF */
"loop 1b\n\t"
: "=&r" ( index ), "=&S" ( discard_S ),
- "=&c" ( discard_c ), "+m" ( *value )
+ "=&c" ( discard_c ), "=@ccc" ( carry ),
+ "+m" ( *value )
: "r" ( value0 ), "1" ( addend0 ), "2" ( size )
: "eax" );
+ return carry;
}
/**
@@ -80,8 +87,9 @@ bigint_add_raw ( const uint32_t *addend0, uint32_t *value0,
* @v subtrahend0 Element 0 of big integer to subtract
* @v value0 Element 0 of big integer to be subtracted from
* @v size Number of elements
+ * @ret borrow Borrow flag
*/
-static inline __attribute__ (( always_inline )) void
+static inline __attribute__ (( always_inline )) int
bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
@@ -89,61 +97,71 @@ bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0,
long index;
void *discard_S;
long discard_c;
+ int borrow;
__asm__ __volatile__ ( "xor %0, %0\n\t" /* Zero %0 and clear CF */
"\n1:\n\t"
"lodsl\n\t"
- "sbbl %%eax, (%4,%0,4)\n\t"
+ "sbbl %%eax, (%5,%0,4)\n\t"
"inc %0\n\t" /* Does not affect CF */
"loop 1b\n\t"
: "=&r" ( index ), "=&S" ( discard_S ),
- "=&c" ( discard_c ), "+m" ( *value )
+ "=&c" ( discard_c ), "=@ccc" ( borrow ),
+ "+m" ( *value )
: "r" ( value0 ), "1" ( subtrahend0 ),
"2" ( size )
: "eax" );
+ return borrow;
}
/**
- * Rotate big integer left
+ * Shift big integer left
*
* @v value0 Element 0 of big integer
* @v size Number of elements
+ * @ret out Bit shifted out
*/
-static inline __attribute__ (( always_inline )) void
-bigint_rol_raw ( uint32_t *value0, unsigned int size ) {
+static inline __attribute__ (( always_inline )) int
+bigint_shl_raw ( uint32_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
long index;
long discard_c;
+ int out;
__asm__ __volatile__ ( "xor %0, %0\n\t" /* Zero %0 and clear CF */
"\n1:\n\t"
- "rcll $1, (%3,%0,4)\n\t"
+ "rcll $1, (%4,%0,4)\n\t"
"inc %0\n\t" /* Does not affect CF */
"loop 1b\n\t"
: "=&r" ( index ), "=&c" ( discard_c ),
- "+m" ( *value )
+ "=@ccc" ( out ), "+m" ( *value )
: "r" ( value0 ), "1" ( size ) );
+ return out;
}
/**
- * Rotate big integer right
+ * Shift big integer right
*
* @v value0 Element 0 of big integer
* @v size Number of elements
+ * @ret out Bit shifted out
*/
-static inline __attribute__ (( always_inline )) void
-bigint_ror_raw ( uint32_t *value0, unsigned int size ) {
+static inline __attribute__ (( always_inline )) int
+bigint_shr_raw ( uint32_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
long discard_c;
+ int out;
__asm__ __volatile__ ( "clc\n\t"
"\n1:\n\t"
- "rcrl $1, -4(%2,%0,4)\n\t"
+ "rcrl $1, -4(%3,%0,4)\n\t"
"loop 1b\n\t"
- : "=&c" ( discard_c ), "+m" ( *value )
+ : "=&c" ( discard_c ), "=@ccc" ( out ),
+ "+m" ( *value )
: "r" ( value0 ), "0" ( size ) );
+ return out;
}
/**
@@ -196,25 +214,6 @@ bigint_is_geq_raw ( const uint32_t *value0, const uint32_t *reference0,
}
/**
- * Test if bit is set in big integer
- *
- * @v value0 Element 0 of big integer
- * @v size Number of elements
- * @v bit Bit to test
- * @ret is_set Bit is set
- */
-static inline __attribute__ (( always_inline )) int
-bigint_bit_is_set_raw ( const uint32_t *value0, unsigned int size,
- unsigned int bit ) {
- const bigint_t ( size ) __attribute__ (( may_alias )) *value =
- ( ( const void * ) value0 );
- unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) );
- unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) );
-
- return ( value->element[index] & ( 1 << subindex ) );
-}
-
-/**
* Find highest bit set in big integer
*
* @v value0 Element 0 of big integer
@@ -312,20 +311,45 @@ bigint_done_raw ( const uint32_t *value0, unsigned int size __unused,
long discard_c;
/* Copy raw data in reverse order */
- __asm__ __volatile__ ( "\n1:\n\t"
+ __asm__ __volatile__ ( "jecxz 2f\n\t"
+ "\n1:\n\t"
"movb -1(%3,%1), %%al\n\t"
"stosb\n\t"
"loop 1b\n\t"
+ "\n2:\n\t"
: "=&D" ( discard_D ), "=&c" ( discard_c ),
"+m" ( *out_bytes )
: "r" ( value0 ), "0" ( out ), "1" ( len )
: "eax" );
}
-extern void bigint_multiply_raw ( const uint32_t *multiplicand0,
- unsigned int multiplicand_size,
- const uint32_t *multiplier0,
- unsigned int multiplier_size,
- uint32_t *value0 );
+/**
+ * Multiply big integer elements
+ *
+ * @v multiplicand Multiplicand element
+ * @v multiplier Multiplier element
+ * @v result Result element
+ * @v carry Carry element
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_multiply_one ( const uint32_t multiplicand, const uint32_t multiplier,
+ uint32_t *result, uint32_t *carry ) {
+ uint32_t discard_a;
+
+ __asm__ __volatile__ ( /* Perform multiplication */
+ "mull %3\n\t"
+ /* Accumulate carry */
+ "addl %5, %0\n\t"
+ "adcl $0, %1\n\t"
+ /* Accumulate result */
+ "addl %0, %2\n\t"
+ "adcl $0, %1\n\t"
+ : "=&a" ( discard_a ),
+ "=&d" ( *carry ),
+ "+m" ( *result )
+ : "g" ( multiplicand ),
+ "0" ( multiplier ),
+ "r" ( *carry ) );
+}
#endif /* _BITS_BIGINT_H */
diff --git a/src/arch/x86/include/bits/bitops.h b/src/arch/x86/include/bits/bitops.h
index f697b8c8f..cdbc3b0a2 100644
--- a/src/arch/x86/include/bits/bitops.h
+++ b/src/arch/x86/include/bits/bitops.h
@@ -14,6 +14,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
diff --git a/src/arch/x86/include/bits/endian.h b/src/arch/x86/include/bits/endian.h
index 85718cfdd..72279117d 100644
--- a/src/arch/x86/include/bits/endian.h
+++ b/src/arch/x86/include/bits/endian.h
@@ -2,6 +2,7 @@
#define _BITS_ENDIAN_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#define __BYTE_ORDER __LITTLE_ENDIAN
diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h
index 78b3dea1c..e7aec6f39 100644
--- a/src/arch/x86/include/bits/errfile.h
+++ b/src/arch/x86/include/bits/errfile.h
@@ -2,14 +2,14 @@
#define _BITS_ERRFILE_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @addtogroup errfile Error file identifiers
* @{
*/
-#define ERRFILE_memtop_umalloc ( ERRFILE_ARCH | ERRFILE_CORE | 0x00000000 )
-#define ERRFILE_memmap ( ERRFILE_ARCH | ERRFILE_CORE | 0x00010000 )
+#define ERRFILE_int15 ( ERRFILE_ARCH | ERRFILE_CORE | 0x00010000 )
#define ERRFILE_pnpbios ( ERRFILE_ARCH | ERRFILE_CORE | 0x00020000 )
#define ERRFILE_bios_smbios ( ERRFILE_ARCH | ERRFILE_CORE | 0x00030000 )
#define ERRFILE_biosint ( ERRFILE_ARCH | ERRFILE_CORE | 0x00040000 )
@@ -42,7 +42,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_comboot_resolv ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00090000 )
#define ERRFILE_comboot_call ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000a0000 )
#define ERRFILE_sdi ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000b0000 )
-#define ERRFILE_initrd ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000c0000 )
#define ERRFILE_pxe_call ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000d0000 )
#define ERRFILE_ucode ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000e0000 )
diff --git a/src/arch/x86/include/bits/io.h b/src/arch/x86/include/bits/io.h
index 95673ad8d..cde0b6829 100644
--- a/src/arch/x86/include/bits/io.h
+++ b/src/arch/x86/include/bits/io.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** Page shift */
#define PAGE_SHIFT 12
diff --git a/src/arch/x86/include/bits/iomap.h b/src/arch/x86/include/bits/iomap.h
index d6fff257e..d524bd805 100644
--- a/src/arch/x86/include/bits/iomap.h
+++ b/src/arch/x86/include/bits/iomap.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/iomap_pages.h>
diff --git a/src/arch/x86/include/bits/memmap.h b/src/arch/x86/include/bits/memmap.h
new file mode 100644
index 000000000..e68550fb8
--- /dev/null
+++ b/src/arch/x86/include/bits/memmap.h
@@ -0,0 +1,15 @@
+#ifndef _BITS_MEMMAP_H
+#define _BITS_MEMMAP_H
+
+/** @file
+ *
+ * x86-specific system memory map API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <ipxe/int15.h>
+
+#endif /* _BITS_MEMMAP_H */
diff --git a/src/arch/x86/include/bits/nap.h b/src/arch/x86/include/bits/nap.h
index 7103b94c0..52c8d81ba 100644
--- a/src/arch/x86/include/bits/nap.h
+++ b/src/arch/x86/include/bits/nap.h
@@ -8,8 +8,16 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/bios_nap.h>
-#include <ipxe/efi/efix86_nap.h>
-#endif /* _BITS_MAP_H */
+/**
+ * Sleep until next CPU interrupt
+ *
+ */
+static inline __attribute__ (( always_inline )) void cpu_halt ( void ) {
+ __asm__ __volatile__ ( "hlt" );
+}
+
+#endif /* _BITS_NAP_H */
diff --git a/src/arch/x86/include/bits/ns16550.h b/src/arch/x86/include/bits/ns16550.h
new file mode 100644
index 000000000..dbb1cd51c
--- /dev/null
+++ b/src/arch/x86/include/bits/ns16550.h
@@ -0,0 +1,60 @@
+#ifndef _BITS_NS16550_H
+#define _BITS_NS16550_H
+
+/** @file
+ *
+ * 16550-compatible UART
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <ipxe/io.h>
+
+/**
+ * Write to UART register
+ *
+ * @v ns16550 16550 UART
+ * @v address Register address
+ * @v data Data
+ */
+static inline __attribute__ (( always_inline )) void
+ns16550_write ( struct ns16550_uart *ns16550, unsigned int address,
+ uint8_t data ) {
+
+ iowrite8 ( data, ( ns16550->base + address ) );
+}
+
+/**
+ * Read from UART register
+ *
+ * @v ns16550 16550 UART
+ * @v address Register address
+ * @ret data Data
+ */
+static inline __attribute__ (( always_inline )) uint8_t
+ns16550_read ( struct ns16550_uart *ns16550, unsigned int address ) {
+
+ return ioread8 ( ns16550->base + address );
+}
+
+/* Fixed ISA serial port base addresses */
+#define COM1_BASE 0x3f8
+#define COM2_BASE 0x2f8
+#define COM3_BASE 0x3e8
+#define COM4_BASE 0x2e8
+
+/* Fixed ISA serial ports */
+extern struct uart com1;
+extern struct uart com2;
+extern struct uart com3;
+extern struct uart com4;
+
+/* Fixed ISA serial port names */
+#define COM1 &com1
+#define COM2 &com2
+#define COM3 &com3
+#define COM4 &com4
+
+#endif /* _BITS_NS16550_H */
diff --git a/src/arch/x86/include/bits/pci_io.h b/src/arch/x86/include/bits/pci_io.h
index a074d3370..b6c01e5c4 100644
--- a/src/arch/x86/include/bits/pci_io.h
+++ b/src/arch/x86/include/bits/pci_io.h
@@ -8,9 +8,9 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/pcibios.h>
#include <ipxe/pcidirect.h>
-#include <ipxe/pcicloud.h>
#endif /* _BITS_PCI_IO_H */
diff --git a/src/arch/x86/include/bits/reboot.h b/src/arch/x86/include/bits/reboot.h
index e702dd3d0..8d8d0b40e 100644
--- a/src/arch/x86/include/bits/reboot.h
+++ b/src/arch/x86/include/bits/reboot.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/bios_reboot.h>
diff --git a/src/arch/x86/include/bits/sanboot.h b/src/arch/x86/include/bits/sanboot.h
index 1b9924e64..ff7b88d14 100644
--- a/src/arch/x86/include/bits/sanboot.h
+++ b/src/arch/x86/include/bits/sanboot.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/bios_sanboot.h>
diff --git a/src/arch/x86/include/bits/smbios.h b/src/arch/x86/include/bits/smbios.h
index 9977c87ac..2be98d887 100644
--- a/src/arch/x86/include/bits/smbios.h
+++ b/src/arch/x86/include/bits/smbios.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/bios_smbios.h>
diff --git a/src/arch/x86/include/bits/string.h b/src/arch/x86/include/bits/string.h
index c26fe30d5..8b2b3070b 100644
--- a/src/arch/x86/include/bits/string.h
+++ b/src/arch/x86/include/bits/string.h
@@ -25,6 +25,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/arch/x86/include/bits/tcpip.h b/src/arch/x86/include/bits/tcpip.h
index 0ac55b1a0..52d032427 100644
--- a/src/arch/x86/include/bits/tcpip.h
+++ b/src/arch/x86/include/bits/tcpip.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
extern uint16_t tcpip_continue_chksum ( uint16_t partial, const void *data,
size_t len );
diff --git a/src/arch/x86/include/bits/time.h b/src/arch/x86/include/bits/time.h
index 556d96f64..a4aa8cc6e 100644
--- a/src/arch/x86/include/bits/time.h
+++ b/src/arch/x86/include/bits/time.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/rtc_time.h>
diff --git a/src/arch/x86/include/bits/uaccess.h b/src/arch/x86/include/bits/uaccess.h
deleted file mode 100644
index e9e7e5af5..000000000
--- a/src/arch/x86/include/bits/uaccess.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef _BITS_UACCESS_H
-#define _BITS_UACCESS_H
-
-/** @file
- *
- * x86-specific user access API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <librm.h>
-
-#endif /* _BITS_UACCESS_H */
diff --git a/src/arch/x86/include/bits/uart.h b/src/arch/x86/include/bits/uart.h
deleted file mode 100644
index e09cd3f4c..000000000
--- a/src/arch/x86/include/bits/uart.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef _BITS_UART_H
-#define _BITS_UART_H
-
-/** @file
- *
- * 16550-compatible UART
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <stdint.h>
-#include <ipxe/io.h>
-
-/**
- * Write to UART register
- *
- * @v uart UART
- * @v addr Register address
- * @v data Data
- */
-static inline __attribute__ (( always_inline )) void
-uart_write ( struct uart *uart, unsigned int addr, uint8_t data ) {
- outb ( data, ( uart->base + addr ) );
-}
-
-/**
- * Read from UART register
- *
- * @v uart UART
- * @v addr Register address
- * @ret data Data
- */
-static inline __attribute__ (( always_inline )) uint8_t
-uart_read ( struct uart *uart, unsigned int addr ) {
- return inb ( uart->base + addr );
-}
-
-extern int uart_select ( struct uart *uart, unsigned int port );
-
-#endif /* _BITS_UART_H */
diff --git a/src/arch/x86/include/bits/umalloc.h b/src/arch/x86/include/bits/umalloc.h
deleted file mode 100644
index 5d1f554d8..000000000
--- a/src/arch/x86/include/bits/umalloc.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef _BITS_UMALLOC_H
-#define _BITS_UMALLOC_H
-
-/** @file
- *
- * x86-specific user memory allocation API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <ipxe/memtop_umalloc.h>
-
-#endif /* _BITS_UMALLOC_H */
diff --git a/src/arch/x86/include/bits/xen.h b/src/arch/x86/include/bits/xen.h
index 3433cea1f..313bec254 100644
--- a/src/arch/x86/include/bits/xen.h
+++ b/src/arch/x86/include/bits/xen.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/* Hypercall registers */
#ifdef __x86_64__
diff --git a/src/arch/x86/include/initrd.h b/src/arch/x86/include/initrd.h
deleted file mode 100644
index 2fb9d3d3a..000000000
--- a/src/arch/x86/include/initrd.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef _INITRD_H
-#define _INITRD_H
-
-/** @file
- *
- * Initial ramdisk (initrd) reshuffling
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <ipxe/uaccess.h>
-
-/** Minimum free space required to reshuffle initrds
- *
- * Chosen to avoid absurdly long reshuffling times
- */
-#define INITRD_MIN_FREE_LEN ( 512 * 1024 )
-
-extern void initrd_reshuffle ( userptr_t bottom );
-extern int initrd_reshuffle_check ( size_t len, userptr_t bottom );
-
-#endif /* _INITRD_H */
diff --git a/src/arch/x86/include/ipxe/bios_nap.h b/src/arch/x86/include/ipxe/bios_nap.h
index c9b82c1e5..7d94b3c4a 100644
--- a/src/arch/x86/include/ipxe/bios_nap.h
+++ b/src/arch/x86/include/ipxe/bios_nap.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#ifdef NAP_PCBIOS
#define NAP_PREFIX_pcbios
diff --git a/src/arch/x86/include/ipxe/bios_reboot.h b/src/arch/x86/include/ipxe/bios_reboot.h
index 3f6df9073..bd1bb42cc 100644
--- a/src/arch/x86/include/ipxe/bios_reboot.h
+++ b/src/arch/x86/include/ipxe/bios_reboot.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#ifdef REBOOT_PCBIOS
#define REBOOT_PREFIX_pcbios
diff --git a/src/arch/x86/include/ipxe/bios_sanboot.h b/src/arch/x86/include/ipxe/bios_sanboot.h
index 85d698039..d28339e4e 100644
--- a/src/arch/x86/include/ipxe/bios_sanboot.h
+++ b/src/arch/x86/include/ipxe/bios_sanboot.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#ifdef SANBOOT_PCBIOS
#define SANBOOT_PREFIX_pcbios
diff --git a/src/arch/x86/include/ipxe/bios_smbios.h b/src/arch/x86/include/ipxe/bios_smbios.h
index 9f7f9c8ff..1815e3617 100644
--- a/src/arch/x86/include/ipxe/bios_smbios.h
+++ b/src/arch/x86/include/ipxe/bios_smbios.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#ifdef SMBIOS_PCBIOS
#define SMBIOS_PREFIX_pcbios
diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h
index 90d1bf01d..1851a859b 100644
--- a/src/arch/x86/include/ipxe/cpuid.h
+++ b/src/arch/x86/include/ipxe/cpuid.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
@@ -33,6 +34,9 @@ struct x86_features {
/** CPUID extended function */
#define CPUID_EXTENDED 0x80000000UL
+/** CPUID hypervisor function */
+#define CPUID_HYPERVISOR 0x40000000UL
+
/** Get vendor ID and largest standard function */
#define CPUID_VENDOR_ID 0x00000000UL
diff --git a/src/arch/x86/include/ipxe/efi/efix86_nap.h b/src/arch/x86/include/ipxe/efi/efix86_nap.h
deleted file mode 100644
index 1a391c9b6..000000000
--- a/src/arch/x86/include/ipxe/efi/efix86_nap.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef _IPXE_EFIX86_NAP_H
-#define _IPXE_EFIX86_NAP_H
-
-/** @file
- *
- * EFI CPU sleeping
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#ifdef NAP_EFIX86
-#define NAP_PREFIX_efix86
-#else
-#define NAP_PREFIX_efix86 __efix86_
-#endif
-
-#endif /* _IPXE_EFIX86_NAP_H */
diff --git a/src/arch/x86/include/ipxe/int15.h b/src/arch/x86/include/ipxe/int15.h
new file mode 100644
index 000000000..590c0e9a7
--- /dev/null
+++ b/src/arch/x86/include/ipxe/int15.h
@@ -0,0 +1,22 @@
+#ifndef _IPXE_INT15_H
+#define _IPXE_INT15_H
+
+/** @file
+ *
+ * INT15-based memory map
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#ifdef MEMMAP_INT15
+#define MEMMAP_PREFIX_int15
+#else
+#define MEMMAP_PREFIX_int15 __int15_
+#endif
+
+extern void int15_intercept ( int intercept );
+extern void hide_basemem ( void );
+
+#endif /* _IPXE_INT15_H */
diff --git a/src/arch/x86/include/ipxe/iomap_pages.h b/src/arch/x86/include/ipxe/iomap_pages.h
index 18e0a3002..e74dabd90 100644
--- a/src/arch/x86/include/ipxe/iomap_pages.h
+++ b/src/arch/x86/include/ipxe/iomap_pages.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#ifdef IOMAP_PAGES
#define IOMAP_PREFIX_pages
diff --git a/src/arch/x86/include/ipxe/memtop_umalloc.h b/src/arch/x86/include/ipxe/memtop_umalloc.h
deleted file mode 100644
index dee055d16..000000000
--- a/src/arch/x86/include/ipxe/memtop_umalloc.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef _IPXE_MEMTOP_UMALLOC_H
-#define _IPXE_MEMTOP_UMALLOC_H
-
-/** @file
- *
- * External memory allocation
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#ifdef UMALLOC_MEMTOP
-#define UMALLOC_PREFIX_memtop
-#else
-#define UMALLOC_PREFIX_memtop __memtop_
-#endif
-
-#endif /* _IPXE_MEMTOP_UMALLOC_H */
diff --git a/src/arch/x86/include/ipxe/pcibios.h b/src/arch/x86/include/ipxe/pcibios.h
index 3caea1cfe..2fd03198e 100644
--- a/src/arch/x86/include/ipxe/pcibios.h
+++ b/src/arch/x86/include/ipxe/pcibios.h
@@ -10,6 +10,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#ifdef PCIAPI_PCBIOS
#define PCIAPI_PREFIX_pcbios
@@ -33,6 +34,17 @@ extern int pcibios_write ( struct pci_device *pci, uint32_t command,
uint32_t value );
/**
+ * Check if PCI bus probing is allowed
+ *
+ * @v pci PCI device
+ * @ret ok Bus probing is allowed
+ */
+static inline __always_inline int
+PCIAPI_INLINE ( pcbios, pci_can_probe ) ( struct pci_device *pci __unused ) {
+ return 1;
+}
+
+/**
* Read byte from PCI configuration space via PCI BIOS
*
* @v pci PCI device
@@ -145,6 +157,4 @@ PCIAPI_INLINE ( pcbios, pci_ioremap ) ( struct pci_device *pci __unused,
return ioremap ( bus_addr, len );
}
-extern struct pci_api pcibios_api;
-
#endif /* _IPXE_PCIBIOS_H */
diff --git a/src/arch/x86/include/ipxe/pcicloud.h b/src/arch/x86/include/ipxe/pcicloud.h
deleted file mode 100644
index 52268908c..000000000
--- a/src/arch/x86/include/ipxe/pcicloud.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef _IPXE_PCICLOUD_H
-#define _IPXE_PCICLOUD_H
-
-/** @file
- *
- * Cloud VM PCI configuration space access
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#ifdef PCIAPI_CLOUD
-#define PCIAPI_PREFIX_cloud
-#else
-#define PCIAPI_PREFIX_cloud __cloud_
-#endif
-
-#endif /* _IPXE_PCICLOUD_H */
diff --git a/src/arch/x86/include/ipxe/pcidirect.h b/src/arch/x86/include/ipxe/pcidirect.h
index 98c6a2bbb..5863b4d16 100644
--- a/src/arch/x86/include/ipxe/pcidirect.h
+++ b/src/arch/x86/include/ipxe/pcidirect.h
@@ -2,6 +2,7 @@
#define _PCIDIRECT_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <ipxe/io.h>
@@ -26,6 +27,17 @@ struct pci_device;
extern void pcidirect_prepare ( struct pci_device *pci, int where );
/**
+ * Check if PCI bus probing is allowed
+ *
+ * @v pci PCI device
+ * @ret ok Bus probing is allowed
+ */
+static inline __always_inline int
+PCIAPI_INLINE ( direct, pci_can_probe ) ( struct pci_device *pci __unused ) {
+ return 1;
+}
+
+/**
* Find next PCI bus:dev.fn address range in system
*
* @v busdevfn Starting PCI bus:dev.fn address
@@ -155,6 +167,4 @@ PCIAPI_INLINE ( direct, pci_ioremap ) ( struct pci_device *pci __unused,
return ioremap ( bus_addr, len );
}
-extern struct pci_api pcidirect_api;
-
#endif /* _PCIDIRECT_H */
diff --git a/src/arch/x86/include/ipxe/rsdp.h b/src/arch/x86/include/ipxe/rsdp.h
index 14afcd774..f371d9a20 100644
--- a/src/arch/x86/include/ipxe/rsdp.h
+++ b/src/arch/x86/include/ipxe/rsdp.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#ifdef ACPI_RSDP
#define ACPI_PREFIX_rsdp
@@ -20,9 +21,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
* @v signature Requested table signature
* @v index Requested index of table with this signature
- * @ret table Table, or UNULL if not found
+ * @ret table Table, or NULL if not found
*/
-static inline __attribute__ (( always_inline )) userptr_t
+static inline __attribute__ (( always_inline )) const struct acpi_header *
ACPI_INLINE ( rsdp, acpi_find ) ( uint32_t signature, unsigned int index ) {
return acpi_find_via_rsdt ( signature, index );
diff --git a/src/arch/x86/include/ipxe/rtc_time.h b/src/arch/x86/include/ipxe/rtc_time.h
index cb8c7f49e..49c6313ed 100644
--- a/src/arch/x86/include/ipxe/rtc_time.h
+++ b/src/arch/x86/include/ipxe/rtc_time.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#ifdef TIME_RTC
#define TIME_PREFIX_rtc
diff --git a/src/arch/x86/include/ipxe/x86_io.h b/src/arch/x86/include/ipxe/x86_io.h
index eeb3f8454..164b57e92 100644
--- a/src/arch/x86/include/ipxe/x86_io.h
+++ b/src/arch/x86/include/ipxe/x86_io.h
@@ -16,6 +16,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#ifdef IOAPI_X86
#define IOAPI_PREFIX_x86
diff --git a/src/arch/x86/include/libkir.h b/src/arch/x86/include/libkir.h
index 1f5b13504..76766b6c2 100644
--- a/src/arch/x86/include/libkir.h
+++ b/src/arch/x86/include/libkir.h
@@ -194,7 +194,7 @@ copy_from_user ( void *dest, userptr_t buffer, off_t offset, size_t len ) {
* @ret buffer User buffer
*/
static inline __attribute__ (( always_inline )) userptr_t
-real_to_user ( unsigned int segment, unsigned int offset ) {
+real_to_virt ( unsigned int segment, unsigned int offset ) {
return ( ( segment << 16 ) | offset );
}
@@ -210,7 +210,7 @@ real_to_user ( unsigned int segment, unsigned int offset ) {
*/
static inline __attribute__ (( always_inline )) userptr_t
virt_to_user ( void * virtual ) {
- return real_to_user ( rm_ds, ( intptr_t ) virtual );
+ return real_to_virt ( rm_ds, ( intptr_t ) virtual );
}
/* TEXT16_CODE: declare a fragment of code that resides in .text16 */
diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h
index 84b345d3a..666be0438 100644
--- a/src/arch/x86/include/librm.h
+++ b/src/arch/x86/include/librm.h
@@ -64,12 +64,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#else /* ASSEMBLY */
-#ifdef UACCESS_LIBRM
-#define UACCESS_PREFIX_librm
-#else
-#define UACCESS_PREFIX_librm __librm_
-#endif
-
/**
* Call C function from real-mode code
*
@@ -79,114 +73,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
"pushl $( " _S2 ( VIRTUAL ( function ) ) " )\n\t" \
"call virt_call\n\t"
-/* Variables in librm.S */
-extern const unsigned long virt_offset;
-
-/**
- * Convert physical address to user pointer
- *
- * @v phys_addr Physical address
- * @ret userptr User pointer
- */
-static inline __always_inline userptr_t
-UACCESS_INLINE ( librm, phys_to_user ) ( unsigned long phys_addr ) {
-
- /* In a 64-bit build, any valid physical address is directly
- * usable as a virtual address, since the low 4GB is
- * identity-mapped.
- */
- if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) )
- return phys_addr;
-
- /* In a 32-bit build, subtract virt_offset */
- return ( phys_addr - virt_offset );
-}
-
-/**
- * Convert user buffer to physical address
- *
- * @v userptr User pointer
- * @v offset Offset from user pointer
- * @ret phys_addr Physical address
- */
-static inline __always_inline unsigned long
-UACCESS_INLINE ( librm, user_to_phys ) ( userptr_t userptr, off_t offset ) {
- unsigned long addr = ( userptr + offset );
-
- /* In a 64-bit build, any virtual address in the low 4GB is
- * directly usable as a physical address, since the low 4GB is
- * identity-mapped.
- */
- if ( ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) &&
- ( addr <= 0xffffffffUL ) )
- return addr;
-
- /* In a 32-bit build or in a 64-bit build with a virtual
- * address above 4GB: add virt_offset
- */
- return ( addr + virt_offset );
-}
-
-static inline __always_inline userptr_t
-UACCESS_INLINE ( librm, virt_to_user ) ( volatile const void *addr ) {
- return trivial_virt_to_user ( addr );
-}
-
-static inline __always_inline void *
-UACCESS_INLINE ( librm, user_to_virt ) ( userptr_t userptr, off_t offset ) {
- return trivial_user_to_virt ( userptr, offset );
-}
-
-static inline __always_inline userptr_t
-UACCESS_INLINE ( librm, userptr_add ) ( userptr_t userptr, off_t offset ) {
- return trivial_userptr_add ( userptr, offset );
-}
-
-static inline __always_inline off_t
-UACCESS_INLINE ( librm, userptr_sub ) ( userptr_t userptr,
- userptr_t subtrahend ) {
- return trivial_userptr_sub ( userptr, subtrahend );
-}
-
-static inline __always_inline void
-UACCESS_INLINE ( librm, memcpy_user ) ( userptr_t dest, off_t dest_off,
- userptr_t src, off_t src_off,
- size_t len ) {
- trivial_memcpy_user ( dest, dest_off, src, src_off, len );
-}
-
-static inline __always_inline void
-UACCESS_INLINE ( librm, memmove_user ) ( userptr_t dest, off_t dest_off,
- userptr_t src, off_t src_off,
- size_t len ) {
- trivial_memmove_user ( dest, dest_off, src, src_off, len );
-}
-
-static inline __always_inline int
-UACCESS_INLINE ( librm, memcmp_user ) ( userptr_t first, off_t first_off,
- userptr_t second, off_t second_off,
- size_t len ) {
- return trivial_memcmp_user ( first, first_off, second, second_off, len);
-}
-
-static inline __always_inline void
-UACCESS_INLINE ( librm, memset_user ) ( userptr_t buffer, off_t offset,
- int c, size_t len ) {
- trivial_memset_user ( buffer, offset, c, len );
-}
-
-static inline __always_inline size_t
-UACCESS_INLINE ( librm, strlen_user ) ( userptr_t buffer, off_t offset ) {
- return trivial_strlen_user ( buffer, offset );
-}
-
-static inline __always_inline off_t
-UACCESS_INLINE ( librm, memchr_user ) ( userptr_t buffer, off_t offset,
- int c, size_t len ) {
- return trivial_memchr_user ( buffer, offset, c, len );
-}
-
-
/******************************************************************************
*
* Access to variables in .data16 and .text16
@@ -244,8 +130,8 @@ extern const uint16_t __text16 ( rm_cs );
extern const uint16_t __text16 ( rm_ds );
#define rm_ds __use_text16 ( rm_ds )
-extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size );
-extern void remove_user_from_rm_stack ( userptr_t data, size_t size );
+extern uint16_t copy_to_rm_stack ( const void *data, size_t size );
+extern void remove_from_rm_stack ( void *data, size_t size );
/* CODE_DEFAULT: restore default .code32/.code64 directive */
#ifdef __x86_64__
@@ -460,6 +346,10 @@ enum page_flags {
/** The I/O space page table */
extern struct page_table io_pages;
+/** Maximum number of I/O pages */
+#define IO_PAGE_COUNT \
+ ( sizeof ( io_pages.page ) / sizeof ( io_pages.page[0] ) )
+
/** I/O page size
*
* We choose to use 2MB pages for I/O space, to minimise the number of
@@ -479,7 +369,8 @@ extern char __text16_array ( sipi, [] );
#define sipi __use_text16 ( sipi )
/** Length of startup IPI real-mode handler */
-extern char sipi_len[];
+extern size_t ABS_SYMBOL ( sipi_len );
+#define sipi_len ABS_VALUE ( sipi_len )
/** Startup IPI real-mode handler copy of real-mode data segment */
extern uint16_t __text16 ( sipi_ds );
diff --git a/src/arch/x86/include/pic8259.h b/src/arch/x86/include/pic8259.h
index dbec5fd2c..0dc59cf27 100644
--- a/src/arch/x86/include/pic8259.h
+++ b/src/arch/x86/include/pic8259.h
@@ -47,9 +47,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/* Macros to enable/disable IRQs */
#define IMR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_IMR : PIC2_IMR )
#define IMR_BIT(x) ( 1 << ( (x) % IRQ_PIC_CUTOFF ) )
-#define irq_enabled(x) ( ( inb ( IMR_REG(x) ) & IMR_BIT(x) ) == 0 )
-#define enable_irq(x) outb ( inb( IMR_REG(x) ) & ~IMR_BIT(x), IMR_REG(x) )
-#define disable_irq(x) outb ( inb( IMR_REG(x) ) | IMR_BIT(x), IMR_REG(x) )
/* Macros for acknowledging IRQs */
#define ICR_REG( irq ) ( (irq) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR )
@@ -63,6 +60,50 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define IRQ_MAX 15
#define IRQ_NONE -1U
+/**
+ * Check if interrupt is enabled
+ *
+ * @v irq Interrupt number
+ * @ret enabled Interrupt is currently enabled
+ */
+static inline __attribute__ (( always_inline )) int
+irq_enabled ( unsigned int irq ) {
+ int imr = inb ( IMR_REG ( irq ) );
+ int mask = IMR_BIT ( irq );
+
+ return ( ( imr & mask ) == 0 );
+}
+
+/**
+ * Enable interrupt
+ *
+ * @v irq Interrupt number
+ * @ret enabled Interrupt was previously enabled
+ */
+static inline __attribute__ (( always_inline )) int
+enable_irq ( unsigned int irq ) {
+ int imr = inb ( IMR_REG ( irq ) );
+ int mask = IMR_BIT ( irq );
+
+ outb ( ( imr & ~mask ), IMR_REG ( irq ) );
+ return ( ( imr & mask ) == 0 );
+}
+
+/**
+ * Disable interrupt
+ *
+ * @v irq Interrupt number
+ * @ret enabled Interrupt was previously enabled
+ */
+static inline __attribute__ (( always_inline )) int
+disable_irq ( unsigned int irq ) {
+ int imr = inb ( IMR_REG ( irq ) );
+ int mask = IMR_BIT ( irq );
+
+ outb ( ( imr | mask ), IMR_REG ( irq ) );
+ return ( ( imr & mask ) == 0 );
+}
+
/* Function prototypes
*/
void send_eoi ( unsigned int irq );
diff --git a/src/arch/x86/include/pxe.h b/src/arch/x86/include/pxe.h
index 54649b504..8e7aa1ce7 100644
--- a/src/arch/x86/include/pxe.h
+++ b/src/arch/x86/include/pxe.h
@@ -85,8 +85,6 @@ struct pxe_api_call {
* @ret exit PXE API call exit code
*/
PXENV_EXIT_t ( * entry ) ( union u_PXENV_ANY *params );
- /** Length of parameters */
- uint16_t params_len;
/** Opcode */
uint16_t opcode;
};
@@ -112,7 +110,6 @@ struct pxe_api_call {
( union u_PXENV_ANY *params ) ) _entry ) \
: ( ( PXENV_EXIT_t ( * ) \
( union u_PXENV_ANY *params ) ) _entry ) ), \
- .params_len = sizeof ( _params_type ), \
.opcode = _opcode, \
}
diff --git a/src/arch/x86/include/realmode.h b/src/arch/x86/include/realmode.h
index 4defd3b97..7baec56ca 100644
--- a/src/arch/x86/include/realmode.h
+++ b/src/arch/x86/include/realmode.h
@@ -2,7 +2,9 @@
#define REALMODE_H
#include <stdint.h>
+#include <string.h>
#include <registers.h>
+#include <librm.h>
#include <ipxe/uaccess.h>
/*
@@ -65,15 +67,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
/**
- * Convert segment:offset address to user buffer
+ * Convert segment:offset address to virtual address
*
* @v segment Real-mode segment
* @v offset Real-mode offset
- * @ret buffer User buffer
+ * @ret virt Virtual address
*/
-static inline __always_inline userptr_t
-real_to_user ( unsigned int segment, unsigned int offset ) {
- return ( phys_to_user ( ( segment << 4 ) + offset ) );
+static inline __always_inline void *
+real_to_virt ( unsigned int segment, unsigned int offset ) {
+ return ( phys_to_virt ( ( segment << 4 ) + offset ) );
}
/**
@@ -87,7 +89,7 @@ real_to_user ( unsigned int segment, unsigned int offset ) {
static inline __always_inline void
copy_to_real ( unsigned int dest_seg, unsigned int dest_off,
void *src, size_t n ) {
- copy_to_user ( real_to_user ( dest_seg, dest_off ), 0, src, n );
+ memcpy ( real_to_virt ( dest_seg, dest_off ), src, n );
}
/**
@@ -101,7 +103,7 @@ copy_to_real ( unsigned int dest_seg, unsigned int dest_off,
static inline __always_inline void
copy_from_real ( void *dest, unsigned int src_seg,
unsigned int src_off, size_t n ) {
- copy_from_user ( dest, real_to_user ( src_seg, src_off ), 0, n );
+ memcpy ( dest, real_to_virt ( src_seg, src_off ), n );
}
/**
diff --git a/src/arch/x86/interface/pcbios/acpi_timer.c b/src/arch/x86/interface/pcbios/acpi_timer.c
index 2e4047e38..e1523578b 100644
--- a/src/arch/x86/interface/pcbios/acpi_timer.c
+++ b/src/arch/x86/interface/pcbios/acpi_timer.c
@@ -102,20 +102,19 @@ static void acpi_udelay ( unsigned long usecs ) {
* @ret rc Return status code
*/
static int acpi_timer_probe ( void ) {
- struct acpi_fadt fadtab;
- userptr_t fadt;
+ const struct acpi_fadt *fadt;
unsigned int pm_tmr_blk;
/* Locate FADT */
- fadt = acpi_table ( FADT_SIGNATURE, 0 );
+ fadt = container_of ( acpi_table ( FADT_SIGNATURE, 0 ),
+ struct acpi_fadt, acpi );
if ( ! fadt ) {
DBGC ( &acpi_timer, "ACPI could not find FADT\n" );
return -ENOENT;
}
/* Read FADT */
- copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
- pm_tmr_blk = le32_to_cpu ( fadtab.pm_tmr_blk );
+ pm_tmr_blk = le32_to_cpu ( fadt->pm_tmr_blk );
if ( ! pm_tmr_blk ) {
DBGC ( &acpi_timer, "ACPI has no timer\n" );
return -ENOENT;
diff --git a/src/arch/x86/interface/pcbios/acpipwr.c b/src/arch/x86/interface/pcbios/acpipwr.c
index f08b4af25..cb82ef1b4 100644
--- a/src/arch/x86/interface/pcbios/acpipwr.c
+++ b/src/arch/x86/interface/pcbios/acpipwr.c
@@ -23,6 +23,7 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <byteswap.h>
@@ -62,8 +63,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* uglier hacks I have ever implemented, but it's still prettier than
* the ACPI specification itself.
*/
-static int acpi_extract_sx ( userptr_t zsdt, size_t len, size_t offset,
- void *data ) {
+static int acpi_extract_sx ( const struct acpi_header *zsdt, size_t len,
+ size_t offset, void *data ) {
unsigned int *sx = data;
uint8_t bytes[4];
uint8_t *byte;
@@ -77,7 +78,8 @@ static int acpi_extract_sx ( userptr_t zsdt, size_t len, size_t offset,
}
/* Read first four bytes of value */
- copy_from_user ( bytes, zsdt, offset, sizeof ( bytes ) );
+ memcpy ( bytes, ( ( ( const void * ) zsdt ) + offset ),
+ sizeof ( bytes ) );
DBGC ( colour, "ACPI found \\_Sx containing %02x:%02x:%02x:%02x\n",
bytes[0], bytes[1], bytes[2], bytes[3] );
@@ -111,8 +113,7 @@ static int acpi_extract_sx ( userptr_t zsdt, size_t len, size_t offset,
* @ret rc Return status code
*/
int acpi_poweroff ( void ) {
- struct acpi_fadt fadtab;
- userptr_t fadt;
+ const struct acpi_fadt *fadt;
unsigned int pm1a_cnt_blk;
unsigned int pm1b_cnt_blk;
unsigned int pm1a_cnt;
@@ -123,16 +124,16 @@ int acpi_poweroff ( void ) {
int rc;
/* Locate FADT */
- fadt = acpi_table ( FADT_SIGNATURE, 0 );
+ fadt = container_of ( acpi_table ( FADT_SIGNATURE, 0 ),
+ struct acpi_fadt, acpi );
if ( ! fadt ) {
DBGC ( colour, "ACPI could not find FADT\n" );
return -ENOENT;
}
/* Read FADT */
- copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
- pm1a_cnt_blk = le32_to_cpu ( fadtab.pm1a_cnt_blk );
- pm1b_cnt_blk = le32_to_cpu ( fadtab.pm1b_cnt_blk );
+ pm1a_cnt_blk = le32_to_cpu ( fadt->pm1a_cnt_blk );
+ pm1b_cnt_blk = le32_to_cpu ( fadt->pm1b_cnt_blk );
pm1a_cnt = ( pm1a_cnt_blk + ACPI_PM1_CNT );
pm1b_cnt = ( pm1b_cnt_blk + ACPI_PM1_CNT );
diff --git a/src/arch/x86/interface/pcbios/basemem.c b/src/arch/x86/interface/pcbios/basemem.c
index 6a46081aa..8f3a30e92 100644
--- a/src/arch/x86/interface/pcbios/basemem.c
+++ b/src/arch/x86/interface/pcbios/basemem.c
@@ -27,7 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <realmode.h>
#include <bios.h>
#include <basemem.h>
-#include <ipxe/hidemem.h>
+#include <ipxe/memmap.h>
/** @file
*
diff --git a/src/arch/x86/interface/pcbios/bios_cachedhcp.c b/src/arch/x86/interface/pcbios/bios_cachedhcp.c
index bea803d6e..60191c9c5 100644
--- a/src/arch/x86/interface/pcbios/bios_cachedhcp.c
+++ b/src/arch/x86/interface/pcbios/bios_cachedhcp.c
@@ -24,6 +24,7 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
+#include <string.h>
#include <ipxe/init.h>
#include <ipxe/cachedhcp.h>
#include <realmode.h>
@@ -60,7 +61,7 @@ static void cachedhcp_init ( void ) {
/* Record cached DHCPACK */
if ( ( rc = cachedhcp_record ( &cached_dhcpack, 0,
- phys_to_user ( cached_dhcpack_phys ),
+ phys_to_virt ( cached_dhcpack_phys ),
sizeof ( BOOTPLAYER_t ) ) ) != 0 ) {
DBGC ( colour, "CACHEDHCP could not record DHCPACK: %s\n",
strerror ( rc ) );
@@ -73,5 +74,6 @@ static void cachedhcp_init ( void ) {
/** Cached DHCPACK initialisation function */
struct init_fn cachedhcp_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "cachedhcp",
.initialise = cachedhcp_init,
};
diff --git a/src/arch/x86/interface/pcbios/bios_mp.c b/src/arch/x86/interface/pcbios/bios_mp.c
index 9e1179ccd..4525a60cf 100644
--- a/src/arch/x86/interface/pcbios/bios_mp.c
+++ b/src/arch/x86/interface/pcbios/bios_mp.c
@@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#include <registers.h>
+#include <librm.h>
#include <ipxe/uaccess.h>
#include <ipxe/timer.h>
#include <ipxe/msr.h>
@@ -92,9 +93,9 @@ static void bios_mp_exec_boot ( mp_func_t func, void *opaque ) {
"pushl %k1\n\t"
"call *%k0\n\t"
"addl $8, %%esp\n\t" )
- : : "r" ( mp_address ( mp_call ) ),
- "r" ( mp_address ( func ) ),
- "r" ( mp_address ( opaque ) ) );
+ : : "R" ( mp_address ( mp_call ) ),
+ "R" ( mp_address ( func ) ),
+ "R" ( mp_address ( opaque ) ) );
}
/**
diff --git a/src/arch/x86/interface/pcbios/bios_reboot.c b/src/arch/x86/interface/pcbios/bios_reboot.c
index 071173f19..c7f25405f 100644
--- a/src/arch/x86/interface/pcbios/bios_reboot.c
+++ b/src/arch/x86/interface/pcbios/bios_reboot.c
@@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
+#include <string.h>
#include <ipxe/reboot.h>
#include <realmode.h>
#include <bios.h>
@@ -38,14 +39,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* Reboot system
*
- * @v warm Perform a warm reboot
+ * @v flags Reboot flags
*/
-static void bios_reboot ( int warm ) {
- uint16_t flag;
+static void bios_reboot ( int flags ) {
+ uint16_t type;
/* Configure BIOS for cold/warm reboot */
- flag = ( warm ? BDA_REBOOT_WARM : 0 );
- put_real ( flag, BDA_SEG, BDA_REBOOT );
+ type = ( ( flags & REBOOT_WARM ) ? BDA_REBOOT_WARM : 0 );
+ put_real ( type, BDA_SEG, BDA_REBOOT );
/* Jump to system reset vector */
__asm__ __volatile__ ( REAL_CODE ( "ljmp $0xf000, $0xfff0" ) : );
diff --git a/src/arch/x86/interface/pcbios/bios_smbios.c b/src/arch/x86/interface/pcbios/bios_smbios.c
index 366679d36..aaec1eea1 100644
--- a/src/arch/x86/interface/pcbios/bios_smbios.c
+++ b/src/arch/x86/interface/pcbios/bios_smbios.c
@@ -45,19 +45,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @ret rc Return status code
*/
static int bios_find_smbios2 ( struct smbios *smbios ) {
- struct smbios_entry entry;
- int rc;
+ const struct smbios_entry *entry;
/* Scan through BIOS segment to find SMBIOS 32-bit entry point */
- if ( ( rc = find_smbios_entry ( real_to_user ( BIOS_SEG, 0 ), 0x10000,
- &entry ) ) != 0 )
- return rc;
+ entry = find_smbios_entry ( real_to_virt ( BIOS_SEG, 0 ), 0x10000 );
+ if ( ! entry )
+ return -ENOENT;
/* Fill in entry point descriptor structure */
- smbios->address = phys_to_user ( entry.smbios_address );
- smbios->len = entry.smbios_len;
- smbios->count = entry.smbios_count;
- smbios->version = SMBIOS_VERSION ( entry.major, entry.minor );
+ smbios->address = phys_to_virt ( entry->smbios_address );
+ smbios->len = entry->smbios_len;
+ smbios->count = entry->smbios_count;
+ smbios->version = SMBIOS_VERSION ( entry->major, entry->minor );
return 0;
}
@@ -69,26 +68,25 @@ static int bios_find_smbios2 ( struct smbios *smbios ) {
* @ret rc Return status code
*/
static int bios_find_smbios3 ( struct smbios *smbios ) {
- struct smbios3_entry entry;
- int rc;
+ const struct smbios3_entry *entry;
/* Scan through BIOS segment to find SMBIOS 64-bit entry point */
- if ( ( rc = find_smbios3_entry ( real_to_user ( BIOS_SEG, 0 ), 0x10000,
- &entry ) ) != 0 )
- return rc;
+ entry = find_smbios3_entry ( real_to_virt ( BIOS_SEG, 0 ), 0x10000 );
+ if ( ! entry )
+ return -ENOENT;
/* Check that address is accessible */
- if ( entry.smbios_address > ~( ( physaddr_t ) 0 ) ) {
+ if ( entry->smbios_address > ~( ( physaddr_t ) 0 ) ) {
DBG ( "SMBIOS3 at %08llx is inaccessible\n",
- ( ( unsigned long long ) entry.smbios_address ) );
+ ( ( unsigned long long ) entry->smbios_address ) );
return -ENOTSUP;
}
/* Fill in entry point descriptor structure */
- smbios->address = phys_to_user ( entry.smbios_address );
- smbios->len = entry.smbios_len;
+ smbios->address = phys_to_virt ( entry->smbios_address );
+ smbios->len = entry->smbios_len;
smbios->count = 0;
- smbios->version = SMBIOS_VERSION ( entry.major, entry.minor );
+ smbios->version = SMBIOS_VERSION ( entry->major, entry->minor );
return 0;
}
diff --git a/src/arch/x86/interface/pcbios/biosint.c b/src/arch/x86/interface/pcbios/biosint.c
index 667e9ed81..f5e54ede8 100644
--- a/src/arch/x86/interface/pcbios/biosint.c
+++ b/src/arch/x86/interface/pcbios/biosint.c
@@ -1,3 +1,4 @@
+#include <string.h>
#include <errno.h>
#include <realmode.h>
#include <biosint.h>
diff --git a/src/arch/x86/interface/pcbios/e820mangler.S b/src/arch/x86/interface/pcbios/e820mangler.S
index ef5dc2754..b9e891dff 100644
--- a/src/arch/x86/interface/pcbios/e820mangler.S
+++ b/src/arch/x86/interface/pcbios/e820mangler.S
@@ -564,6 +564,8 @@ int15_88:
int15:
/* See if we want to intercept this call */
pushfw
+ cmpb $0, %cs:int15_intercept_flag
+ je 3f
cmpw $0xe820, %ax
jne 1f
cmpl $SMAP, %edx
@@ -587,3 +589,9 @@ int15:
int15_vector:
.long 0
.size int15_vector, . - int15_vector
+
+ .section ".text16.data", "aw", @progbits
+ .globl int15_intercept_flag
+int15_intercept_flag:
+ .byte 1
+ .size int15_intercept_flag, . - int15_intercept_flag
diff --git a/src/arch/x86/interface/pcbios/hidemem.c b/src/arch/x86/interface/pcbios/hidemem.c
index 1a3022c5d..2b85459b6 100644
--- a/src/arch/x86/interface/pcbios/hidemem.c
+++ b/src/arch/x86/interface/pcbios/hidemem.c
@@ -22,6 +22,7 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+#include <string.h>
#include <assert.h>
#include <realmode.h>
#include <biosint.h>
@@ -29,7 +30,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <fakee820.h>
#include <ipxe/init.h>
#include <ipxe/io.h>
-#include <ipxe/hidemem.h>
+#include <ipxe/uheap.h>
+#include <ipxe/memmap.h>
/** Set to true if you want to test a fake E820 map */
#define FAKE_E820 0
@@ -72,13 +74,17 @@ extern void int15();
extern struct segoff __text16 ( int15_vector );
#define int15_vector __use_text16 ( int15_vector )
+/** INT 15 interception flag */
+extern uint8_t __text16 ( int15_intercept_flag );
+#define int15_intercept_flag __use_text16 ( int15_intercept_flag )
+
/* The linker defines these symbols for us */
extern char _textdata[];
extern char _etextdata[];
-extern char _text16_memsz[];
-#define _text16_memsz ( ( size_t ) _text16_memsz )
-extern char _data16_memsz[];
-#define _data16_memsz ( ( size_t ) _data16_memsz )
+extern size_t ABS_SYMBOL ( _text16_memsz );
+#define _text16_memsz ABS_VALUE ( _text16_memsz )
+extern size_t ABS_SYMBOL ( _data16_memsz );
+#define _data16_memsz ABS_VALUE ( _data16_memsz )
/**
* Hide region of memory from system memory map
@@ -113,21 +119,43 @@ void hide_basemem ( void ) {
}
/**
- * Hide umalloc() region
+ * Hide .text and .data
+ *
+ */
+void hide_textdata ( void ) {
+ hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ),
+ virt_to_phys ( _etextdata ) );
+}
+
+/**
+ * Synchronise in-use regions with the externally visible system memory map
*
*/
-void hide_umalloc ( physaddr_t start, physaddr_t end ) {
- assert ( end <= virt_to_phys ( _textdata ) );
+static void int15_sync ( void ) {
+ physaddr_t start;
+ physaddr_t end;
+
+ /* Besides our fixed base memory and textdata regions, we
+ * support hiding only a single in-use memory region (the
+ * umalloc region), which must be placed before the hidden
+ * textdata region (even if zero-length).
+ */
+ start = uheap_start;
+ end = uheap_end;
+ if ( start == end )
+ start = end = virt_to_phys ( _textdata );
hide_region ( &hidemem_umalloc, start, end );
}
/**
- * Hide .text and .data
+ * Set INT 15 interception flag
*
+ * @v intercept Intercept INT 15 calls to modify memory map
*/
-void hide_textdata ( void ) {
- hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ),
- virt_to_phys ( _etextdata ) );
+void int15_intercept ( int intercept ) {
+
+ /* Set flag for INT 15 handler */
+ int15_intercept_flag = intercept;
}
/**
@@ -137,26 +165,25 @@ void hide_textdata ( void ) {
* returned by the BIOS.
*/
static void hide_etherboot ( void ) {
- struct memory_map memmap;
unsigned int rm_ds_top;
unsigned int rm_cs_top;
unsigned int fbms;
/* Dump memory map before mangling */
DBG ( "Hiding iPXE from system memory map\n" );
- get_memmap ( &memmap );
+ memmap_dump_all ( 1 );
/* Hook in fake E820 map, if we're testing one */
if ( FAKE_E820 ) {
DBG ( "Hooking in fake E820 map\n" );
fake_e820();
- get_memmap ( &memmap );
+ memmap_dump_all ( 1 );
}
/* Initialise the hidden regions */
hide_basemem();
- hide_umalloc ( virt_to_phys ( _textdata ), virt_to_phys ( _textdata ) );
hide_textdata();
+ int15_sync();
/* Some really moronic BIOSes bring up the PXE stack via the
* UNDI loader entry point and then don't bother to unload it
@@ -183,7 +210,7 @@ static void hide_etherboot ( void ) {
/* Dump memory map after mangling */
DBG ( "Hidden iPXE from system memory map\n" );
- get_memmap ( &memmap );
+ memmap_dump_all ( 1 );
}
/**
@@ -193,7 +220,6 @@ static void hide_etherboot ( void ) {
* possible.
*/
static void unhide_etherboot ( int flags __unused ) {
- struct memory_map memmap;
int rc;
/* If we have more than one hooked interrupt at this point, it
@@ -224,7 +250,7 @@ static void unhide_etherboot ( int flags __unused ) {
/* Dump memory map after unhiding */
DBG ( "Unhidden iPXE from system memory map\n" );
- get_memmap ( &memmap );
+ memmap_dump_all ( 1 );
}
/** Hide Etherboot startup function */
@@ -233,3 +259,5 @@ struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = {
.startup = hide_etherboot,
.shutdown = unhide_etherboot,
};
+
+PROVIDE_MEMMAP ( int15, memmap_sync, int15_sync );
diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c
index 372d40ba3..3fb25d261 100644
--- a/src/arch/x86/interface/pcbios/int13.c
+++ b/src/arch/x86/interface/pcbios/int13.c
@@ -25,17 +25,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <limits.h>
#include <byteswap.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/blockdev.h>
-#include <ipxe/io.h>
#include <ipxe/acpi.h>
#include <ipxe/sanboot.h>
#include <ipxe/device.h>
#include <ipxe/pci.h>
#include <ipxe/eltorito.h>
+#include <ipxe/memmap.h>
#include <realmode.h>
#include <bios.h>
#include <biosint.h>
@@ -181,8 +182,7 @@ static int int13_parse_eltorito ( struct san_device *sandev, void *scratch ) {
int rc;
/* Read boot record volume descriptor */
- if ( ( rc = sandev_read ( sandev, ELTORITO_LBA, 1,
- virt_to_user ( boot ) ) ) != 0 ) {
+ if ( ( rc = sandev_read ( sandev, ELTORITO_LBA, 1, boot ) ) != 0 ) {
DBGC ( sandev->drive, "INT13 drive %02x could not read El "
"Torito boot record volume descriptor: %s\n",
sandev->drive, strerror ( rc ) );
@@ -228,7 +228,7 @@ static int int13_guess_geometry_hdd ( struct san_device *sandev, void *scratch,
int rc;
/* Read partition table */
- if ( ( rc = sandev_read ( sandev, 0, 1, virt_to_user ( mbr ) ) ) != 0 ) {
+ if ( ( rc = sandev_read ( sandev, 0, 1, mbr ) ) != 0 ) {
DBGC ( sandev->drive, "INT13 drive %02x could not read "
"partition table to guess geometry: %s\n",
sandev->drive, strerror ( rc ) );
@@ -517,12 +517,12 @@ static int int13_rw_sectors ( struct san_device *sandev,
int ( * sandev_rw ) ( struct san_device *sandev,
uint64_t lba,
unsigned int count,
- userptr_t buffer ) ) {
+ void *buffer ) ) {
struct int13_data *int13 = sandev->priv;
unsigned int cylinder, head, sector;
unsigned long lba;
unsigned int count;
- userptr_t buffer;
+ void *buffer;
int rc;
/* Validate blocksize */
@@ -549,7 +549,7 @@ static int int13_rw_sectors ( struct san_device *sandev,
lba = ( ( ( ( cylinder * int13->heads ) + head )
* int13->sectors_per_track ) + sector - 1 );
count = ix86->regs.al;
- buffer = real_to_user ( ix86->segs.es, ix86->regs.bx );
+ buffer = real_to_virt ( ix86->segs.es, ix86->regs.bx );
DBGC2 ( sandev->drive, "C/H/S %d/%d/%d = LBA %08lx <-> %04x:%04x "
"(count %d)\n", cylinder, head, sector, lba, ix86->segs.es,
@@ -710,12 +710,12 @@ static int int13_extended_rw ( struct san_device *sandev,
int ( * sandev_rw ) ( struct san_device *sandev,
uint64_t lba,
unsigned int count,
- userptr_t buffer ) ) {
+ void *buffer ) ) {
struct int13_disk_address addr;
uint8_t bufsize;
uint64_t lba;
unsigned long count;
- userptr_t buffer;
+ void *buffer;
int rc;
/* Extended reads are not allowed on floppy drives.
@@ -743,11 +743,11 @@ static int int13_extended_rw ( struct san_device *sandev,
if ( ( addr.count == 0xff ) ||
( ( addr.buffer.segment == 0xffff ) &&
( addr.buffer.offset == 0xffff ) ) ) {
- buffer = phys_to_user ( addr.buffer_phys );
+ buffer = phys_to_virt ( addr.buffer_phys );
DBGC2 ( sandev->drive, "%08llx",
( ( unsigned long long ) addr.buffer_phys ) );
} else {
- buffer = real_to_user ( addr.buffer.segment,
+ buffer = real_to_virt ( addr.buffer.segment,
addr.buffer.offset );
DBGC2 ( sandev->drive, "%04x:%04x", addr.buffer.segment,
addr.buffer.offset );
@@ -1058,7 +1058,7 @@ static int int13_cdrom_read_boot_catalog ( struct san_device *sandev,
/* Read from boot catalog */
if ( ( rc = sandev_read ( sandev, start, command.count,
- phys_to_user ( command.buffer ) ) ) != 0 ) {
+ phys_to_virt ( command.buffer ) ) ) != 0 ) {
DBGC ( sandev->drive, "INT13 drive %02x could not read boot "
"catalog: %s\n", sandev->drive, strerror ( rc ) );
return -INT13_STATUS_READ_ERROR;
@@ -1455,8 +1455,8 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) {
"catalog (status %04x)\n", drive, status );
return -EIO;
}
- copy_from_user ( &catalog, phys_to_user ( eltorito_cmd.buffer ), 0,
- sizeof ( catalog ) );
+ memcpy ( &catalog, phys_to_virt ( eltorito_cmd.buffer ),
+ sizeof ( catalog ) );
/* Sanity checks */
if ( catalog.valid.platform_id != ELTORITO_PLATFORM_X86 ) {
@@ -1523,7 +1523,6 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) {
*/
static int int13_boot ( unsigned int drive,
struct san_boot_config *config __unused ) {
- struct memory_map memmap;
struct segoff address;
int rc;
@@ -1537,7 +1536,7 @@ static int int13_boot ( unsigned int drive,
* many problems that turn out to be memory-map related that
* it's worth doing.
*/
- get_memmap ( &memmap );
+ memmap_dump_all ( 1 );
/* Jump to boot sector */
if ( ( rc = call_bootsector ( address.segment, address.offset,
diff --git a/src/arch/x86/interface/pcbios/int13con.c b/src/arch/x86/interface/pcbios/int13con.c
index 8106cd153..925228874 100644
--- a/src/arch/x86/interface/pcbios/int13con.c
+++ b/src/arch/x86/interface/pcbios/int13con.c
@@ -288,6 +288,7 @@ static void int13con_init ( void ) {
* INT13 console initialisation function
*/
struct init_fn int13con_init_fn __init_fn ( INIT_CONSOLE ) = {
+ .name = "int13con",
.initialise = int13con_init,
};
diff --git a/src/arch/x86/interface/pcbios/memmap.c b/src/arch/x86/interface/pcbios/int15.c
index daae382b8..73bdbbe4a 100644
--- a/src/arch/x86/interface/pcbios/memmap.c
+++ b/src/arch/x86/interface/pcbios/int15.c
@@ -24,11 +24,14 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
+#include <string.h>
#include <errno.h>
+#include <assert.h>
#include <realmode.h>
#include <bios.h>
#include <memsizes.h>
#include <ipxe/io.h>
+#include <ipxe/memmap.h>
/**
* @file
@@ -151,7 +154,7 @@ static unsigned int extmemsize_88 ( void ) {
* @ret extmem Extended memory size, in kB
*
* Note that this is only an approximation; for an accurate picture,
- * use the E820 memory map obtained via get_memmap();
+ * use the E820 memory map obtained via memmap_describe();
*/
unsigned int extmemsize ( void ) {
unsigned int extmem_e801;
@@ -166,12 +169,13 @@ unsigned int extmemsize ( void ) {
/**
* Get e820 memory map
*
- * @v memmap Memory map to fill in
+ * @v region Memory region of interest to be updated
* @ret rc Return status code
*/
-static int meme820 ( struct memory_map *memmap ) {
- struct memory_region *region = memmap->regions;
- struct memory_region *prev_region = NULL;
+static int meme820 ( struct memmap_region *region ) {
+ unsigned int count = 0;
+ uint64_t start = 0;
+ uint64_t len = 0;
uint32_t next = 0;
uint32_t smap;
uint32_t size;
@@ -225,13 +229,6 @@ static int meme820 ( struct memory_map *memmap ) {
break;
}
- /* If first region is not RAM, assume map is invalid */
- if ( ( memmap->count == 0 ) &&
- ( e820buf.type != E820_TYPE_RAM ) ) {
- DBG ( "INT 15,e820 failed, first entry not RAM\n" );
- return -EINVAL;
- }
-
DBG ( "INT 15,e820 region [%llx,%llx) type %d",
e820buf.start, ( e820buf.start + e820buf.len ),
( int ) e820buf.type );
@@ -258,27 +255,36 @@ static int meme820 ( struct memory_map *memmap ) {
continue;
}
- region->start = e820buf.start;
- region->end = e820buf.start + e820buf.len;
-
/* Check for adjacent regions and merge them */
- if ( prev_region && ( region->start == prev_region->end ) ) {
- prev_region->end = region->end;
+ if ( e820buf.start == ( start + len ) ) {
+ len += e820buf.len;
} else {
- prev_region = region;
- region++;
- memmap->count++;
+ start = e820buf.start;
+ len = e820buf.len;
}
- if ( memmap->count >= ( sizeof ( memmap->regions ) /
- sizeof ( memmap->regions[0] ) ) ) {
- DBG ( "INT 15,e820 too many regions returned\n" );
- /* Not a fatal error; what we've got so far at
- * least represents valid regions of memory,
- * even if we couldn't get them all.
- */
- break;
+ /* Sanity check: first region (base memory) should
+ * start at address zero.
+ */
+ if ( ( count == 0 ) && ( start != 0 ) ) {
+ DBG ( "INT 15,e820 region 0 starts at %llx (expected "
+ "0); assuming insane\n", start );
+ return -EINVAL;
+ }
+
+ /* Sanity check: second region (extended memory)
+ * should start at address 0x100000.
+ */
+ if ( ( count == 1 ) && ( start != 0x100000 ) ) {
+ DBG ( "INT 15,e820 region 1 starts at %llx (expected "
+ "100000); assuming insane\n", start );
+ return -EINVAL;
}
+
+ /* Update region of interest */
+ memmap_update ( region, start, len, MEMMAP_FL_MEMORY, "e820" );
+ count++;
+
} while ( next != 0 );
/* Sanity checks. Some BIOSes report complete garbage via INT
@@ -287,19 +293,9 @@ static int meme820 ( struct memory_map *memmap ) {
* region (starting at 0) and at least one high memory region
* (starting at 0x100000).
*/
- if ( memmap->count < 2 ) {
+ if ( count < 2 ) {
DBG ( "INT 15,e820 returned only %d regions; assuming "
- "insane\n", memmap->count );
- return -EINVAL;
- }
- if ( memmap->regions[0].start != 0 ) {
- DBG ( "INT 15,e820 region 0 starts at %llx (expected 0); "
- "assuming insane\n", memmap->regions[0].start );
- return -EINVAL;
- }
- if ( memmap->regions[1].start != 0x100000 ) {
- DBG ( "INT 15,e820 region 1 starts at %llx (expected 100000); "
- "assuming insane\n", memmap->regions[0].start );
+ "insane\n", count );
return -EINVAL;
}
@@ -307,37 +303,52 @@ static int meme820 ( struct memory_map *memmap ) {
}
/**
- * Get memory map
+ * Describe memory region from system memory map
*
- * @v memmap Memory map to fill in
+ * @v min Minimum address
+ * @v hide Hide in-use regions from the memory map
+ * @v region Region descriptor to fill in
*/
-void x86_get_memmap ( struct memory_map *memmap ) {
- unsigned int basemem, extmem;
+static void int15_describe ( uint64_t min, int hide,
+ struct memmap_region *region ) {
+ unsigned int basemem;
+ unsigned int extmem;
+ uint64_t inaccessible;
int rc;
- DBG ( "Fetching system memory map\n" );
+ /* Initialise region */
+ memmap_init ( min, region );
- /* Clear memory map */
- memset ( memmap, 0, sizeof ( *memmap ) );
+ /* Mark addresses above 4GB as inaccessible: we have no way to
+ * access them either in a 32-bit build or in a 64-bit build
+ * (since the 64-bit build identity-maps only the 32-bit
+ * address space).
+ */
+ inaccessible = ( 1ULL << 32 );
+ memmap_update ( region, inaccessible, -inaccessible,
+ MEMMAP_FL_INACCESSIBLE, NULL );
- /* Get base and extended memory sizes */
- basemem = basememsize();
- DBG ( "FBMS base memory size %d kB [0,%x)\n",
- basemem, ( basemem * 1024 ) );
- extmem = extmemsize();
-
- /* Try INT 15,e820 first */
- if ( ( rc = meme820 ( memmap ) ) == 0 ) {
+ /* Enable/disable INT 15 interception as applicable */
+ int15_intercept ( hide );
+
+ /* Try INT 15,e820 first, falling back to constructing a map
+ * from basemem and extmem sizes
+ */
+ if ( ( rc = meme820 ( region ) ) == 0 ) {
DBG ( "Obtained system memory map via INT 15,e820\n" );
- return;
+ } else {
+ basemem = basememsize();
+ DBG ( "FBMS base memory size %d kB [0,%x)\n",
+ basemem, ( basemem * 1024 ) );
+ extmem = extmemsize();
+ memmap_update ( region, 0, ( basemem * 1024 ),
+ MEMMAP_FL_MEMORY, "basemem" );
+ memmap_update ( region, 0x100000, ( extmem * 1024 ),
+ MEMMAP_FL_MEMORY, "extmem" );
}
- /* Fall back to constructing a map from basemem and extmem sizes */
- DBG ( "INT 15,e820 failed; constructing map\n" );
- memmap->regions[0].end = ( basemem * 1024 );
- memmap->regions[1].start = 0x100000;
- memmap->regions[1].end = 0x100000 + ( extmem * 1024 );
- memmap->count = 2;
+ /* Restore INT 15 interception */
+ int15_intercept ( 1 );
}
-PROVIDE_IOAPI ( x86, get_memmap, x86_get_memmap );
+PROVIDE_MEMMAP ( int15, memmap_describe, int15_describe );
diff --git a/src/arch/x86/interface/pcbios/memtop_umalloc.c b/src/arch/x86/interface/pcbios/memtop_umalloc.c
deleted file mode 100644
index 1d3f40a1c..000000000
--- a/src/arch/x86/interface/pcbios/memtop_umalloc.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-/**
- * @file
- *
- * External memory allocation
- *
- */
-
-#include <limits.h>
-#include <errno.h>
-#include <ipxe/uaccess.h>
-#include <ipxe/hidemem.h>
-#include <ipxe/io.h>
-#include <ipxe/memblock.h>
-#include <ipxe/umalloc.h>
-
-/** Maximum usable address for external allocated memory */
-#define EM_MAX_ADDRESS 0xffffffffUL
-
-/** Alignment of external allocated memory */
-#define EM_ALIGN ( 4 * 1024 )
-
-/** Equivalent of NOWHERE for user pointers */
-#define UNOWHERE ( ~UNULL )
-
-/** An external memory block */
-struct external_memory {
- /** Size of this memory block (excluding this header) */
- size_t size;
- /** Block is currently in use */
- int used;
-};
-
-/** Top of heap */
-static userptr_t top = UNULL;
-
-/** Bottom of heap (current lowest allocated block) */
-static userptr_t bottom = UNULL;
-
-/** Remaining space on heap */
-static size_t heap_size;
-
-/**
- * Find largest usable memory region
- *
- * @ret start Start of region
- * @ret len Length of region
- */
-size_t largest_memblock ( userptr_t *start ) {
- struct memory_map memmap;
- struct memory_region *region;
- physaddr_t max = EM_MAX_ADDRESS;
- physaddr_t region_start;
- physaddr_t region_end;
- size_t region_len;
- unsigned int i;
- size_t len = 0;
-
- /* Avoid returning uninitialised data on error */
- *start = UNULL;
-
- /* Scan through all memory regions */
- get_memmap ( &memmap );
- for ( i = 0 ; i < memmap.count ; i++ ) {
- region = &memmap.regions[i];
- DBG ( "Considering [%llx,%llx)\n", region->start, region->end );
-
- /* Truncate block to maximum physical address */
- if ( region->start > max ) {
- DBG ( "...starts after maximum address %lx\n", max );
- continue;
- }
- region_start = region->start;
- if ( region->end > max ) {
- DBG ( "...end truncated to maximum address %lx\n", max);
- region_end = 0; /* =max, given the wraparound */
- } else {
- region_end = region->end;
- }
- region_len = ( region_end - region_start );
-
- /* Use largest block */
- if ( region_len > len ) {
- DBG ( "...new best block found\n" );
- *start = phys_to_user ( region_start );
- len = region_len;
- }
- }
-
- return len;
-}
-
-/**
- * Initialise external heap
- *
- */
-static void init_eheap ( void ) {
- userptr_t base;
-
- heap_size = largest_memblock ( &base );
- bottom = top = userptr_add ( base, heap_size );
- DBG ( "External heap grows downwards from %lx (size %zx)\n",
- user_to_phys ( top, 0 ), heap_size );
-}
-
-/**
- * Collect free blocks
- *
- */
-static void ecollect_free ( void ) {
- struct external_memory extmem;
- size_t len;
-
- /* Walk the free list and collect empty blocks */
- while ( bottom != top ) {
- copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
- sizeof ( extmem ) );
- if ( extmem.used )
- break;
- DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
- user_to_phys ( bottom, extmem.size ) );
- len = ( extmem.size + sizeof ( extmem ) );
- bottom = userptr_add ( bottom, len );
- heap_size += len;
- }
-}
-
-/**
- * Reallocate external memory
- *
- * @v old_ptr Memory previously allocated by umalloc(), or UNULL
- * @v new_size Requested size
- * @ret new_ptr Allocated memory, or UNULL
- *
- * Calling realloc() with a new size of zero is a valid way to free a
- * memory block.
- */
-static userptr_t memtop_urealloc ( userptr_t ptr, size_t new_size ) {
- struct external_memory extmem;
- userptr_t new = ptr;
- size_t align;
-
- /* (Re)initialise external memory allocator if necessary */
- if ( bottom == top )
- init_eheap();
-
- /* Get block properties into extmem */
- if ( ptr && ( ptr != UNOWHERE ) ) {
- /* Determine old size */
- copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
- sizeof ( extmem ) );
- } else {
- /* Create a zero-length block */
- if ( heap_size < sizeof ( extmem ) ) {
- DBG ( "EXTMEM out of space\n" );
- return UNULL;
- }
- ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
- heap_size -= sizeof ( extmem );
- DBG ( "EXTMEM allocating [%lx,%lx)\n",
- user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
- extmem.size = 0;
- }
- extmem.used = ( new_size > 0 );
-
- /* Expand/shrink block if possible */
- if ( ptr == bottom ) {
- /* Update block */
- new = userptr_add ( ptr, - ( new_size - extmem.size ) );
- align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
- new_size += align;
- new = userptr_add ( new, -align );
- if ( new_size > ( heap_size + extmem.size ) ) {
- DBG ( "EXTMEM out of space\n" );
- return UNULL;
- }
- DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
- user_to_phys ( ptr, 0 ),
- user_to_phys ( ptr, extmem.size ),
- user_to_phys ( new, 0 ),
- user_to_phys ( new, new_size ));
- memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
- extmem.size : new_size ) );
- bottom = new;
- heap_size -= ( new_size - extmem.size );
- extmem.size = new_size;
- } else {
- /* Cannot expand; can only pretend to shrink */
- if ( new_size > extmem.size ) {
- /* Refuse to expand */
- DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
- user_to_phys ( ptr, 0 ),
- user_to_phys ( ptr, extmem.size ) );
- return UNULL;
- }
- }
-
- /* Write back block properties */
- copy_to_user ( new, -sizeof ( extmem ), &extmem,
- sizeof ( extmem ) );
-
- /* Collect any free blocks and update hidden memory region */
- ecollect_free();
- hide_umalloc ( user_to_phys ( bottom, ( ( bottom == top ) ?
- 0 : -sizeof ( extmem ) ) ),
- user_to_phys ( top, 0 ) );
-
- return ( new_size ? new : UNOWHERE );
-}
-
-PROVIDE_UMALLOC ( memtop, urealloc, memtop_urealloc );
diff --git a/src/arch/x86/interface/pcbios/pcibios.c b/src/arch/x86/interface/pcbios/pcibios.c
index 7b7a769e3..6b88ee907 100644
--- a/src/arch/x86/interface/pcbios/pcibios.c
+++ b/src/arch/x86/interface/pcbios/pcibios.c
@@ -120,6 +120,7 @@ int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){
return ( status >> 8 );
}
+PROVIDE_PCIAPI_INLINE ( pcbios, pci_can_probe );
PROVIDE_PCIAPI ( pcbios, pci_discover, pcibios_discover );
PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_byte );
PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_word );
@@ -128,5 +129,4 @@ PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_byte );
PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_word );
PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_dword );
PROVIDE_PCIAPI_INLINE ( pcbios, pci_ioremap );
-
-struct pci_api pcibios_api = PCIAPI_RUNTIME ( pcbios );
+PROVIDE_PCIAPI_RUNTIME ( pcbios, PCIAPI_PRIORITY_PCBIOS );
diff --git a/src/arch/x86/interface/pcbios/pcicloud.c b/src/arch/x86/interface/pcbios/pcicloud.c
deleted file mode 100644
index 98ba38b31..000000000
--- a/src/arch/x86/interface/pcbios/pcicloud.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <stdint.h>
-#include <ipxe/init.h>
-#include <ipxe/pci.h>
-#include <ipxe/ecam.h>
-#include <ipxe/pcibios.h>
-#include <ipxe/pcidirect.h>
-#include <ipxe/pcicloud.h>
-
-/** @file
- *
- * Cloud VM PCI configuration space access
- *
- */
-
-/** Selected PCI configuration space access API */
-static struct pci_api *pcicloud = &ecam_api;
-
-/**
- * Find next PCI bus:dev.fn address range in system
- *
- * @v busdevfn Starting PCI bus:dev.fn address
- * @v range PCI bus:dev.fn address range to fill in
- */
-static void pcicloud_discover ( uint32_t busdevfn, struct pci_range *range ) {
-
- pcicloud->pci_discover ( busdevfn, range );
-}
-
-/**
- * Read byte from PCI configuration space
- *
- * @v pci PCI device
- * @v where Location within PCI configuration space
- * @v value Value read
- * @ret rc Return status code
- */
-static int pcicloud_read_config_byte ( struct pci_device *pci,
- unsigned int where, uint8_t *value ) {
-
- return pcicloud->pci_read_config_byte ( pci, where, value );
-}
-
-/**
- * Read 16-bit word from PCI configuration space
- *
- * @v pci PCI device
- * @v where Location within PCI configuration space
- * @v value Value read
- * @ret rc Return status code
- */
-static int pcicloud_read_config_word ( struct pci_device *pci,
- unsigned int where, uint16_t *value ) {
-
- return pcicloud->pci_read_config_word ( pci, where, value );
-}
-
-/**
- * Read 32-bit dword from PCI configuration space
- *
- * @v pci PCI device
- * @v where Location within PCI configuration space
- * @v value Value read
- * @ret rc Return status code
- */
-static int pcicloud_read_config_dword ( struct pci_device *pci,
- unsigned int where, uint32_t *value ) {
-
- return pcicloud->pci_read_config_dword ( pci, where, value );
-}
-
-/**
- * Write byte to PCI configuration space
- *
- * @v pci PCI device
- * @v where Location within PCI configuration space
- * @v value Value to be written
- * @ret rc Return status code
- */
-static int pcicloud_write_config_byte ( struct pci_device *pci,
- unsigned int where, uint8_t value ) {
-
- return pcicloud->pci_write_config_byte ( pci, where, value );
-}
-
-/**
- * Write 16-bit word to PCI configuration space
- *
- * @v pci PCI device
- * @v where Location within PCI configuration space
- * @v value Value to be written
- * @ret rc Return status code
- */
-static int pcicloud_write_config_word ( struct pci_device *pci,
- unsigned int where, uint16_t value ) {
-
- return pcicloud->pci_write_config_word ( pci, where, value );
-}
-
-/**
- * Write 32-bit dword to PCI configuration space
- *
- * @v pci PCI device
- * @v where Location within PCI configuration space
- * @v value Value to be written
- * @ret rc Return status code
- */
-static int pcicloud_write_config_dword ( struct pci_device *pci,
- unsigned int where, uint32_t value ) {
-
- return pcicloud->pci_write_config_dword ( pci, where, value );
-}
-
-/**
- * Map PCI bus address as an I/O address
- *
- * @v bus_addr PCI bus address
- * @v len Length of region
- * @ret io_addr I/O address, or NULL on error
- */
-static void * pcicloud_ioremap ( struct pci_device *pci,
- unsigned long bus_addr, size_t len ) {
-
- return pcicloud->pci_ioremap ( pci, bus_addr, len );
-}
-
-PROVIDE_PCIAPI ( cloud, pci_discover, pcicloud_discover );
-PROVIDE_PCIAPI ( cloud, pci_read_config_byte, pcicloud_read_config_byte );
-PROVIDE_PCIAPI ( cloud, pci_read_config_word, pcicloud_read_config_word );
-PROVIDE_PCIAPI ( cloud, pci_read_config_dword, pcicloud_read_config_dword );
-PROVIDE_PCIAPI ( cloud, pci_write_config_byte, pcicloud_write_config_byte );
-PROVIDE_PCIAPI ( cloud, pci_write_config_word, pcicloud_write_config_word );
-PROVIDE_PCIAPI ( cloud, pci_write_config_dword, pcicloud_write_config_dword );
-PROVIDE_PCIAPI ( cloud, pci_ioremap, pcicloud_ioremap );
-
-/**
- * Initialise cloud VM PCI configuration space access
- *
- */
-static void pcicloud_init ( void ) {
- static struct pci_api *apis[] = {
- &ecam_api, &pcibios_api, &pcidirect_api
- };
- struct pci_device pci;
- uint32_t busdevfn;
- unsigned int i;
- int rc;
-
- /* Select first API that successfully discovers a PCI device */
- for ( i = 0 ; i < ( sizeof ( apis ) / sizeof ( apis[0] ) ) ; i++ ) {
- pcicloud = apis[i];
- busdevfn = 0;
- if ( ( rc = pci_find_next ( &pci, &busdevfn ) ) == 0 ) {
- DBGC ( pcicloud, "PCICLOUD selected %s API (found "
- PCI_FMT ")\n", pcicloud->name,
- PCI_ARGS ( &pci ) );
- return;
- }
- }
-
- /* Fall back to using final attempted API if no devices found */
- pcicloud = apis[ i - 1 ];
- DBGC ( pcicloud, "PCICLOUD selected %s API (nothing detected)\n",
- pcicloud->name );
-}
-
-/** Cloud VM PCI configuration space access initialisation function */
-struct init_fn pcicloud_init_fn __init_fn ( INIT_EARLY ) = {
- .initialise = pcicloud_init,
-};
diff --git a/src/arch/x86/interface/pcbios/rsdp.c b/src/arch/x86/interface/pcbios/rsdp.c
index 3c67b7525..6913be552 100644
--- a/src/arch/x86/interface/pcbios/rsdp.c
+++ b/src/arch/x86/interface/pcbios/rsdp.c
@@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#include <stdint.h>
+#include <string.h>
#include <realmode.h>
#include <bios.h>
#include <ipxe/acpi.h>
@@ -53,50 +54,51 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
* @v start Start address to search
* @v len Length to search
- * @ret rsdt ACPI root system description table, or UNULL
+ * @ret rsdt ACPI root system description table, or NULL
*/
-static userptr_t rsdp_find_rsdt_range ( userptr_t start, size_t len ) {
+static const struct acpi_rsdt * rsdp_find_rsdt_range ( const void *start,
+ size_t len ) {
static const char signature[8] = RSDP_SIGNATURE;
- struct acpi_rsdp rsdp;
- userptr_t rsdt;
+ const struct acpi_rsdp *rsdp;
+ const struct acpi_rsdt *rsdt;
size_t offset;
uint8_t sum;
unsigned int i;
/* Search for RSDP */
- for ( offset = 0 ; ( ( offset + sizeof ( rsdp ) ) < len ) ;
+ for ( offset = 0 ; ( ( offset + sizeof ( *rsdp ) ) < len ) ;
offset += RSDP_STRIDE ) {
/* Check signature and checksum */
- copy_from_user ( &rsdp, start, offset, sizeof ( rsdp ) );
- if ( memcmp ( rsdp.signature, signature,
+ rsdp = ( start + offset );
+ if ( memcmp ( rsdp->signature, signature,
sizeof ( signature ) ) != 0 )
continue;
- for ( sum = 0, i = 0 ; i < sizeof ( rsdp ) ; i++ )
- sum += *( ( ( uint8_t * ) &rsdp ) + i );
+ for ( sum = 0, i = 0 ; i < sizeof ( *rsdp ) ; i++ )
+ sum += *( ( ( uint8_t * ) rsdp ) + i );
if ( sum != 0 )
continue;
/* Extract RSDT */
- rsdt = phys_to_user ( le32_to_cpu ( rsdp.rsdt ) );
+ rsdt = phys_to_virt ( le32_to_cpu ( rsdp->rsdt ) );
DBGC ( rsdt, "RSDT %#08lx found via RSDP %#08lx\n",
- user_to_phys ( rsdt, 0 ),
- user_to_phys ( start, offset ) );
+ virt_to_phys ( rsdt ),
+ ( virt_to_phys ( start ) + offset ) );
return rsdt;
}
- return UNULL;
+ return NULL;
}
/**
* Locate ACPI root system description table
*
- * @ret rsdt ACPI root system description table, or UNULL
+ * @ret rsdt ACPI root system description table, or NULL
*/
-static userptr_t rsdp_find_rsdt ( void ) {
- static userptr_t rsdt;
+static const struct acpi_rsdt * rsdp_find_rsdt ( void ) {
+ static const struct acpi_rsdt *rsdt;
+ const void *ebda;
uint16_t ebda_seg;
- userptr_t ebda;
size_t ebda_len;
/* Return existing RSDT if already found */
@@ -106,7 +108,7 @@ static userptr_t rsdp_find_rsdt ( void ) {
/* Search EBDA */
get_real ( ebda_seg, BDA_SEG, BDA_EBDA );
if ( ebda_seg < RSDP_EBDA_END_SEG ) {
- ebda = real_to_user ( ebda_seg, 0 );
+ ebda = real_to_virt ( ebda_seg, 0 );
ebda_len = ( ( RSDP_EBDA_END_SEG - ebda_seg ) * 16 );
rsdt = rsdp_find_rsdt_range ( ebda, ebda_len );
if ( rsdt )
@@ -114,12 +116,12 @@ static userptr_t rsdp_find_rsdt ( void ) {
}
/* Search fixed BIOS area */
- rsdt = rsdp_find_rsdt_range ( phys_to_user ( RSDP_BIOS_START ),
+ rsdt = rsdp_find_rsdt_range ( phys_to_virt ( RSDP_BIOS_START ),
RSDP_BIOS_LEN );
if ( rsdt )
return rsdt;
- return UNULL;
+ return NULL;
}
PROVIDE_ACPI ( rsdp, acpi_find_rsdt, rsdp_find_rsdt );
diff --git a/src/arch/x86/interface/pcbios/rtc_entropy.c b/src/arch/x86/interface/pcbios/rtc_entropy.c
index 8f47ff6b8..7c98019b6 100644
--- a/src/arch/x86/interface/pcbios/rtc_entropy.c
+++ b/src/arch/x86/interface/pcbios/rtc_entropy.c
@@ -53,6 +53,12 @@ extern void rtc_isr ( void );
/** Previous RTC interrupt handler */
static struct segoff rtc_old_handler;
+/** Previous RTC interrupt enabled state */
+static uint8_t rtc_irq_enabled;
+
+/** Previous RTC periodic interrupt enabled state */
+static uint8_t rtc_int_enabled;
+
/** Flag set by RTC interrupt handler */
extern volatile uint8_t __text16 ( rtc_flag );
#define rtc_flag __use_text16 ( rtc_flag )
@@ -107,8 +113,9 @@ static void rtc_unhook_isr ( void ) {
/**
* Enable RTC interrupts
*
+ * @ret enabled Periodic interrupt was previously enabled
*/
-static void rtc_enable_int ( void ) {
+static int rtc_enable_int ( void ) {
uint8_t status_b;
/* Clear any stale pending interrupts via status register C */
@@ -124,6 +131,9 @@ static void rtc_enable_int ( void ) {
/* Re-enable NMI and reset to default address */
outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
+
+ /* Return previous state */
+ return ( status_b & RTC_STATUS_B_PIE );
}
/**
@@ -198,8 +208,11 @@ static int rtc_entropy_enable ( void ) {
/* Hook ISR and enable RTC interrupts */
rtc_hook_isr();
- enable_irq ( RTC_IRQ );
- rtc_enable_int();
+ rtc_irq_enabled = enable_irq ( RTC_IRQ );
+ rtc_int_enabled = rtc_enable_int();
+ DBGC ( &rtc_flag, "RTC had IRQ%d %sabled, interrupt %sabled\n",
+ RTC_IRQ, ( rtc_irq_enabled ? "en" : "dis" ),
+ ( rtc_int_enabled ? "en" : "dis" ) );
/* Check that RTC interrupts are working */
if ( ( rc = rtc_entropy_check() ) != 0 )
@@ -223,8 +236,10 @@ static int rtc_entropy_enable ( void ) {
return 0;
err_check:
- rtc_disable_int();
- disable_irq ( RTC_IRQ );
+ if ( ! rtc_int_enabled )
+ rtc_disable_int();
+ if ( ! rtc_irq_enabled )
+ disable_irq ( RTC_IRQ );
rtc_unhook_isr();
err_no_tsc:
return rc;
@@ -236,9 +251,11 @@ static int rtc_entropy_enable ( void ) {
*/
static void rtc_entropy_disable ( void ) {
- /* Disable RTC interrupts and unhook ISR */
- rtc_disable_int();
- disable_irq ( RTC_IRQ );
+ /* Restore RTC interrupt state and unhook ISR */
+ if ( ! rtc_int_enabled )
+ rtc_disable_int();
+ if ( ! rtc_irq_enabled )
+ disable_irq ( RTC_IRQ );
rtc_unhook_isr();
}
diff --git a/src/arch/x86/interface/pcbios/vesafb.c b/src/arch/x86/interface/pcbios/vesafb.c
index 3ca15271a..48394bdd9 100644
--- a/src/arch/x86/interface/pcbios/vesafb.c
+++ b/src/arch/x86/interface/pcbios/vesafb.c
@@ -103,7 +103,7 @@ struct vesafb {
/** Font definition */
struct fbcon_font font;
/** Character glyphs */
- struct segoff glyphs;
+ const uint8_t *glyphs;
/** Saved VGA mode */
uint8_t saved_mode;
};
@@ -140,11 +140,10 @@ static int vesafb_rc ( unsigned int status ) {
* Get character glyph
*
* @v character Unicode character
- * @v glyph Character glyph to fill in
+ * @ret glyph Character glyph
*/
-static void vesafb_glyph ( unsigned int character, uint8_t *glyph ) {
+static const uint8_t * vesafb_glyph ( unsigned int character ) {
unsigned int index;
- size_t offset;
/* Identify glyph */
if ( character < VESAFB_ASCII ) {
@@ -287,10 +286,8 @@ static void vesafb_glyph ( unsigned int character, uint8_t *glyph ) {
}
}
- /* Copy glyph from BIOS font table */
- offset = ( index * VESAFB_CHAR_HEIGHT );
- copy_from_real ( glyph, vesafb.glyphs.segment,
- ( vesafb.glyphs.offset + offset ), VESAFB_CHAR_HEIGHT);
+ /* Return glyph in BIOS font table */
+ return &vesafb.glyphs[ index * VESAFB_CHAR_HEIGHT ];
}
/**
@@ -298,6 +295,7 @@ static void vesafb_glyph ( unsigned int character, uint8_t *glyph ) {
*
*/
static void vesafb_font ( void ) {
+ struct segoff glyphs;
/* Get font information
*
@@ -318,12 +316,13 @@ static void vesafb_font ( void ) {
"movw %%es, %%cx\n\t"
"movw %%bp, %%dx\n\t"
"popw %%bp\n\t" /* gcc bug */ )
- : "=c" ( vesafb.glyphs.segment ),
- "=d" ( vesafb.glyphs.offset )
+ : "=c" ( glyphs.segment ),
+ "=d" ( glyphs.offset )
: "a" ( VBE_GET_FONT ),
"b" ( VESAFB_FONT ) );
DBGC ( &vbe_buf, "VESAFB has font %04x at %04x:%04x\n",
- VESAFB_FONT, vesafb.glyphs.segment, vesafb.glyphs.offset );
+ VESAFB_FONT, glyphs.segment, glyphs.offset );
+ vesafb.glyphs = real_to_virt ( glyphs.segment, glyphs.offset );
vesafb.font.height = VESAFB_CHAR_HEIGHT;
vesafb.font.glyph = vesafb_glyph;
}
@@ -338,8 +337,8 @@ static void vesafb_font ( void ) {
*/
static int vesafb_mode_list ( uint16_t **mode_numbers ) {
struct vbe_controller_info *controller = &vbe_buf.controller;
- userptr_t video_mode_ptr;
- uint16_t mode_number;
+ const uint16_t *video_mode_ptr;
+ const uint16_t *mode_number;
uint16_t status;
size_t len;
int rc;
@@ -375,20 +374,18 @@ static int vesafb_mode_list ( uint16_t **mode_numbers ) {
controller->video_mode_ptr.offset );
/* Calculate length of mode list */
- video_mode_ptr = real_to_user ( controller->video_mode_ptr.segment,
+ video_mode_ptr = real_to_virt ( controller->video_mode_ptr.segment,
controller->video_mode_ptr.offset );
- len = 0;
- do {
- copy_from_user ( &mode_number, video_mode_ptr, len,
- sizeof ( mode_number ) );
- len += sizeof ( mode_number );
- } while ( mode_number != VBE_MODE_END );
+ mode_number = video_mode_ptr;
+ while ( *(mode_number++) != VBE_MODE_END ) {}
+ len = ( ( ( const void * ) mode_number ) -
+ ( ( const void * ) video_mode_ptr ) );
/* Allocate and fill mode list */
*mode_numbers = malloc ( len );
if ( ! *mode_numbers )
return -ENOMEM;
- copy_from_user ( *mode_numbers, video_mode_ptr, 0, len );
+ memcpy ( *mode_numbers, video_mode_ptr, len );
return 0;
}
@@ -607,7 +604,7 @@ static int vesafb_init ( struct console_configuration *config ) {
}
/* Initialise frame buffer console */
- if ( ( rc = fbcon_init ( &vesafb.fbcon, phys_to_user ( vesafb.start ),
+ if ( ( rc = fbcon_init ( &vesafb.fbcon, phys_to_virt ( vesafb.start ),
&vesafb.pixel, &vesafb.map, &vesafb.font,
config ) ) != 0 )
goto err_fbcon_init;
diff --git a/src/arch/x86/interface/pxe/pxe_call.c b/src/arch/x86/interface/pxe/pxe_call.c
index 0e8d5c5a8..9a6a20dd3 100644
--- a/src/arch/x86/interface/pxe/pxe_call.c
+++ b/src/arch/x86/interface/pxe/pxe_call.c
@@ -55,12 +55,12 @@ extern void pxe_int_1a ( void );
static int int_1a_hooked = 0;
/** Real-mode code segment size */
-extern char _text16_memsz[];
-#define _text16_memsz ( ( size_t ) _text16_memsz )
+extern size_t ABS_SYMBOL ( _text16_memsz );
+#define _text16_memsz ABS_VALUE ( _text16_memsz )
/** Real-mode data segment size */
-extern char _data16_memsz[];
-#define _data16_memsz ( ( size_t ) _data16_memsz )
+extern size_t ABS_SYMBOL (_data16_memsz );
+#define _data16_memsz ABS_VALUE ( _data16_memsz )
/** PXENV_UNDI_TRANSMIT API call profiler */
static struct profiler pxe_api_tx_profiler __profiler =
@@ -144,10 +144,10 @@ static struct profiler * pxe_api_profiler ( unsigned int opcode ) {
*/
__asmcall void pxe_api_call ( struct i386_all_regs *ix86 ) {
uint16_t opcode = ix86->regs.bx;
- userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
struct profiler *profiler = pxe_api_profiler ( opcode );
+ union u_PXENV_ANY *params =
+ real_to_virt ( ix86->segs.es, ix86->regs.di );
struct pxe_api_call *call;
- union u_PXENV_ANY params;
PXENV_EXIT_t ret;
/* Start profiling */
@@ -160,17 +160,13 @@ __asmcall void pxe_api_call ( struct i386_all_regs *ix86 ) {
call = &pxenv_unknown_api;
}
- /* Copy parameter block from caller */
- copy_from_user ( &params, uparams, 0, call->params_len );
-
/* Set default status in case child routine fails to do so */
- params.Status = PXENV_STATUS_FAILURE;
+ params->Status = PXENV_STATUS_FAILURE;
/* Hand off to relevant API routine */
- ret = call->entry ( &params );
+ ret = call->entry ( params );
- /* Copy modified parameter block back to caller and return */
- copy_to_user ( uparams, 0, &params, call->params_len );
+ /* Return exit code in %ax */
ix86->regs.ax = ret;
/* Stop profiling, if applicable */
@@ -195,24 +191,20 @@ int pxe_api_call_weak ( struct i386_all_regs *ix86 ) {
* @ret ax PXE exit code
*/
__asmcall void pxe_loader_call ( struct i386_all_regs *ix86 ) {
- userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
- struct s_UNDI_LOADER params;
+ struct s_UNDI_LOADER *params =
+ real_to_virt ( ix86->segs.es, ix86->regs.di );
PXENV_EXIT_t ret;
- /* Copy parameter block from caller */
- copy_from_user ( &params, uparams, 0, sizeof ( params ) );
-
/* Fill in ROM segment address */
ppxe.UNDIROMID.segment = ix86->segs.ds;
/* Set default status in case child routine fails to do so */
- params.Status = PXENV_STATUS_FAILURE;
+ params->Status = PXENV_STATUS_FAILURE;
/* Call UNDI loader */
- ret = undi_loader ( &params );
+ ret = undi_loader ( params );
- /* Copy modified parameter block back to caller and return */
- copy_to_user ( uparams, 0, &params, sizeof ( params ) );
+ /* Return exit code in %ax */
ix86->regs.ax = ret;
}
@@ -265,6 +257,7 @@ static void pxe_init_structures ( void ) {
/** PXE structure initialiser */
struct init_fn pxe_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "pxe",
.initialise = pxe_init_structures,
};
diff --git a/src/arch/x86/interface/pxe/pxe_file.c b/src/arch/x86/interface/pxe/pxe_file.c
index 456ffb5fd..997667ccf 100644
--- a/src/arch/x86/interface/pxe/pxe_file.c
+++ b/src/arch/x86/interface/pxe/pxe_file.c
@@ -8,7 +8,6 @@
#include <stdio.h>
#include <errno.h>
#include <byteswap.h>
-#include <ipxe/uaccess.h>
#include <ipxe/posix_io.h>
#include <ipxe/features.h>
#include <pxe.h>
@@ -53,30 +52,20 @@ FEATURE ( FEATURE_MISC, "PXEXT", DHCP_EB_FEATURE_PXE_EXT, 2 );
*
*/
static PXENV_EXIT_t pxenv_file_open ( struct s_PXENV_FILE_OPEN *file_open ) {
- userptr_t filename;
- size_t filename_len;
+ const char *filename;
int fd;
DBG ( "PXENV_FILE_OPEN" );
- /* Copy name from external program, and open it */
- filename = real_to_user ( file_open->FileName.segment,
- file_open->FileName.offset );
- filename_len = strlen_user ( filename, 0 );
- {
- char uri_string[ filename_len + 1 ];
-
- copy_from_user ( uri_string, filename, 0,
- sizeof ( uri_string ) );
- DBG ( " %s", uri_string );
- fd = open ( uri_string );
- }
-
+ /* Open specified filename */
+ filename = real_to_virt ( file_open->FileName.segment,
+ file_open->FileName.offset );
+ DBG ( " %s", filename );
+ fd = open ( filename );
if ( fd < 0 ) {
file_open->Status = PXENV_STATUS ( fd );
return PXENV_EXIT_FAILURE;
}
-
DBG ( " as file %d", fd );
file_open->FileHandle = fd;
@@ -148,17 +137,17 @@ pxenv_file_select ( struct s_PXENV_FILE_SELECT *file_select ) {
*
*/
static PXENV_EXIT_t pxenv_file_read ( struct s_PXENV_FILE_READ *file_read ) {
- userptr_t buffer;
+ void *buffer;
ssize_t len;
DBG ( "PXENV_FILE_READ %d to %04x:%04x+%04x", file_read->FileHandle,
file_read->Buffer.segment, file_read->Buffer.offset,
file_read->BufferSize );
- buffer = real_to_user ( file_read->Buffer.segment,
+ buffer = real_to_virt ( file_read->Buffer.segment,
file_read->Buffer.offset );
- if ( ( len = read_user ( file_read->FileHandle, buffer, 0,
- file_read->BufferSize ) ) < 0 ) {
+ if ( ( len = read ( file_read->FileHandle, buffer,
+ file_read->BufferSize ) ) < 0 ) {
file_read->Status = PXENV_STATUS ( len );
return PXENV_EXIT_FAILURE;
}
@@ -210,27 +199,18 @@ pxenv_get_file_size ( struct s_PXENV_GET_FILE_SIZE *get_file_size ) {
*
*/
static PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec ) {
- userptr_t command;
- size_t command_len;
+ const char *command;
int rc;
DBG ( "PXENV_FILE_EXEC" );
- /* Copy name from external program, and exec it */
- command = real_to_user ( file_exec->Command.segment,
+ /* Execute specified command */
+ command = real_to_virt ( file_exec->Command.segment,
file_exec->Command.offset );
- command_len = strlen_user ( command, 0 );
- {
- char command_string[ command_len + 1 ];
-
- copy_from_user ( command_string, command, 0,
- sizeof ( command_string ) );
- DBG ( " %s", command_string );
-
- if ( ( rc = system ( command_string ) ) != 0 ) {
- file_exec->Status = PXENV_STATUS ( rc );
- return PXENV_EXIT_FAILURE;
- }
+ DBG ( " %s", command );
+ if ( ( rc = system ( command ) ) != 0 ) {
+ file_exec->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
}
file_exec->Status = PXENV_STATUS_SUCCESS;
@@ -251,23 +231,22 @@ static PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec ) {
*/
static PXENV_EXIT_t
pxenv_file_cmdline ( struct s_PXENV_FILE_CMDLINE *file_cmdline ) {
- userptr_t buffer;
- size_t max_len;
+ char *buffer;
size_t len;
DBG ( "PXENV_FILE_CMDLINE to %04x:%04x+%04x \"%s\"\n",
file_cmdline->Buffer.segment, file_cmdline->Buffer.offset,
file_cmdline->BufferSize, pxe_cmdline );
- buffer = real_to_user ( file_cmdline->Buffer.segment,
+ buffer = real_to_virt ( file_cmdline->Buffer.segment,
file_cmdline->Buffer.offset );
len = file_cmdline->BufferSize;
- max_len = ( pxe_cmdline ?
- ( strlen ( pxe_cmdline ) + 1 /* NUL */ ) : 0 );
- if ( len > max_len )
- len = max_len;
- copy_to_user ( buffer, 0, pxe_cmdline, len );
- file_cmdline->BufferSize = max_len;
+ if ( pxe_cmdline ) {
+ len = snprintf ( buffer, len, "%s", pxe_cmdline );
+ file_cmdline->BufferSize = ( len + 1 /* NUL */ );
+ } else {
+ file_cmdline->BufferSize = 0;
+ }
file_cmdline->Status = PXENV_STATUS_SUCCESS;
return PXENV_EXIT_SUCCESS;
diff --git a/src/arch/x86/interface/pxe/pxe_preboot.c b/src/arch/x86/interface/pxe/pxe_preboot.c
index 09e721b34..863aaae9a 100644
--- a/src/arch/x86/interface/pxe/pxe_preboot.c
+++ b/src/arch/x86/interface/pxe/pxe_preboot.c
@@ -33,7 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
-#include <ipxe/uaccess.h>
#include <ipxe/dhcp.h>
#include <ipxe/fakedhcp.h>
#include <ipxe/device.h>
@@ -48,7 +47,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include "pxe_call.h"
/* Avoid dragging in isapnp.o unnecessarily */
-uint16_t isapnp_read_port;
+uint16_t isapnp_read_port __attribute__ (( weak ));
/** Zero-based versions of PXENV_GET_CACHED_INFO::PacketType */
enum pxe_cached_info_indices {
@@ -184,7 +183,7 @@ pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO *get_cached_info ) {
union pxe_cached_info *info;
unsigned int idx;
size_t len;
- userptr_t buffer;
+ void *buffer;
DBGC ( &pxe_netdev, "PXENV_GET_CACHED_INFO %s to %04x:%04x+%x",
pxenv_get_cached_info_name ( get_cached_info->PacketType ),
@@ -243,9 +242,9 @@ pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO *get_cached_info ) {
len = sizeof ( *info );
if ( len < sizeof ( *info ) )
DBGC ( &pxe_netdev, " buffer may be too short" );
- buffer = real_to_user ( get_cached_info->Buffer.segment,
+ buffer = real_to_virt ( get_cached_info->Buffer.segment,
get_cached_info->Buffer.offset );
- copy_to_user ( buffer, 0, info, len );
+ memcpy ( buffer, info, len );
get_cached_info->BufferSize = len;
}
diff --git a/src/arch/x86/interface/pxe/pxe_tftp.c b/src/arch/x86/interface/pxe/pxe_tftp.c
index 3b4c6d847..aa675fd13 100644
--- a/src/arch/x86/interface/pxe/pxe_tftp.c
+++ b/src/arch/x86/interface/pxe/pxe_tftp.c
@@ -33,7 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdio.h>
#include <errno.h>
#include <byteswap.h>
-#include <ipxe/uaccess.h>
#include <ipxe/in.h>
#include <ipxe/tftp.h>
#include <ipxe/iobuf.h>
@@ -49,7 +48,7 @@ struct pxe_tftp_connection {
/** Data transfer interface */
struct interface xfer;
/** Data buffer */
- userptr_t buffer;
+ void *buffer;
/** Size of data buffer */
size_t size;
/** Starting offset of data buffer */
@@ -121,9 +120,8 @@ static int pxe_tftp_xfer_deliver ( struct pxe_tftp_connection *pxe_tftp,
( pxe_tftp->start + pxe_tftp->size ) );
rc = -ENOBUFS;
} else {
- copy_to_user ( pxe_tftp->buffer,
- ( pxe_tftp->offset - pxe_tftp->start ),
- iobuf->data, len );
+ memcpy ( ( pxe_tftp->buffer + pxe_tftp->offset -
+ pxe_tftp->start ), iobuf->data, len );
}
/* Calculate new buffer position */
@@ -378,14 +376,14 @@ static PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) {
tftp_read->Buffer.segment, tftp_read->Buffer.offset );
/* Read single block into buffer */
- pxe_tftp.buffer = real_to_user ( tftp_read->Buffer.segment,
+ pxe_tftp.buffer = real_to_virt ( tftp_read->Buffer.segment,
tftp_read->Buffer.offset );
pxe_tftp.size = pxe_tftp.blksize;
pxe_tftp.start = pxe_tftp.offset;
while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
( pxe_tftp.offset == pxe_tftp.start ) )
step();
- pxe_tftp.buffer = UNULL;
+ pxe_tftp.buffer = NULL;
tftp_read->BufferSize = ( pxe_tftp.offset - pxe_tftp.start );
tftp_read->PacketNumber = ++pxe_tftp.blkidx;
@@ -492,11 +490,11 @@ PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
}
/* Read entire file */
- pxe_tftp.buffer = phys_to_user ( tftp_read_file->Buffer );
+ pxe_tftp.buffer = phys_to_virt ( tftp_read_file->Buffer );
pxe_tftp.size = tftp_read_file->BufferSize;
while ( ( rc = pxe_tftp.rc ) == -EINPROGRESS )
step();
- pxe_tftp.buffer = UNULL;
+ pxe_tftp.buffer = NULL;
tftp_read_file->BufferSize = pxe_tftp.max_offset;
/* Close TFTP file */
diff --git a/src/arch/x86/interface/pxe/pxe_udp.c b/src/arch/x86/interface/pxe/pxe_udp.c
index a5d5eb77b..61c858dde 100644
--- a/src/arch/x86/interface/pxe/pxe_udp.c
+++ b/src/arch/x86/interface/pxe/pxe_udp.c
@@ -9,7 +9,6 @@
#include <ipxe/iobuf.h>
#include <ipxe/xfer.h>
#include <ipxe/udp.h>
-#include <ipxe/uaccess.h>
#include <ipxe/process.h>
#include <ipxe/netdevice.h>
#include <ipxe/malloc.h>
@@ -296,7 +295,7 @@ pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) {
};
size_t len;
struct io_buffer *iobuf;
- userptr_t buffer;
+ const void *buffer;
int rc;
DBG ( "PXENV_UDP_WRITE" );
@@ -328,9 +327,9 @@ pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) {
pxenv_udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
return PXENV_EXIT_FAILURE;
}
- buffer = real_to_user ( pxenv_udp_write->buffer.segment,
+ buffer = real_to_virt ( pxenv_udp_write->buffer.segment,
pxenv_udp_write->buffer.offset );
- copy_from_user ( iob_put ( iobuf, len ), buffer, 0, len );
+ memcpy ( iob_put ( iobuf, len ), buffer, len );
DBG ( " %04x:%04x+%x %d->%s:%d\n", pxenv_udp_write->buffer.segment,
pxenv_udp_write->buffer.offset, pxenv_udp_write->buffer_size,
@@ -400,7 +399,7 @@ static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
struct pxe_udp_pseudo_header *pshdr;
uint16_t d_port_wanted = pxenv_udp_read->d_port;
uint16_t d_port;
- userptr_t buffer;
+ void *buffer;
size_t len;
/* Try receiving a packet, if the queue is empty */
@@ -438,12 +437,12 @@ static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
}
/* Copy packet to buffer and record length */
- buffer = real_to_user ( pxenv_udp_read->buffer.segment,
+ buffer = real_to_virt ( pxenv_udp_read->buffer.segment,
pxenv_udp_read->buffer.offset );
len = iob_len ( iobuf );
if ( len > pxenv_udp_read->buffer_size )
len = pxenv_udp_read->buffer_size;
- copy_to_user ( buffer, 0, iobuf->data, len );
+ memcpy ( buffer, iobuf->data, len );
pxenv_udp_read->buffer_size = len;
/* Fill in source/dest information */
diff --git a/src/arch/x86/interface/syslinux/com32_call.c b/src/arch/x86/interface/syslinux/com32_call.c
index 19fdbaff9..a23f46436 100644
--- a/src/arch/x86/interface/syslinux/com32_call.c
+++ b/src/arch/x86/interface/syslinux/com32_call.c
@@ -49,9 +49,8 @@ void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physad
DBGC ( &com32_regs, "COM32 INT%x in %#08lx out %#08lx\n",
interrupt, inregs_phys, outregs_phys );
- memcpy_user ( virt_to_user( &com32_regs ), 0,
- phys_to_user ( inregs_phys ), 0,
- sizeof(com32sys_t) );
+ memcpy ( &com32_regs, phys_to_virt ( inregs_phys ),
+ sizeof ( com32sys_t ) );
com32_int_vector = interrupt;
@@ -108,9 +107,8 @@ void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physad
: : );
if ( outregs_phys ) {
- memcpy_user ( phys_to_user ( outregs_phys ), 0,
- virt_to_user( &com32_regs ), 0,
- sizeof(com32sys_t) );
+ memcpy ( phys_to_virt ( outregs_phys ),
+ &com32_regs, sizeof ( com32sys_t ) );
}
}
@@ -122,9 +120,8 @@ void __asmcall com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t
DBGC ( &com32_regs, "COM32 farcall %04x:%04x in %#08lx out %#08lx\n",
( proc >> 16 ), ( proc & 0xffff ), inregs_phys, outregs_phys );
- memcpy_user ( virt_to_user( &com32_regs ), 0,
- phys_to_user ( inregs_phys ), 0,
- sizeof(com32sys_t) );
+ memcpy ( &com32_regs, phys_to_virt ( inregs_phys ),
+ sizeof ( com32sys_t ) );
com32_farcall_proc = proc;
@@ -170,9 +167,8 @@ void __asmcall com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t
: : );
if ( outregs_phys ) {
- memcpy_user ( phys_to_user ( outregs_phys ), 0,
- virt_to_user( &com32_regs ), 0,
- sizeof(com32sys_t) );
+ memcpy ( phys_to_virt ( outregs_phys ),
+ &com32_regs, sizeof ( com32sys_t ) );
}
}
@@ -185,7 +181,7 @@ int __asmcall com32_cfarcall ( uint32_t proc, physaddr_t stack, size_t stacksz )
DBGC ( &com32_regs, "COM32 cfarcall %04x:%04x params %#08lx+%#zx\n",
( proc >> 16 ), ( proc & 0xffff ), stack, stacksz );
- copy_user_to_rm_stack ( phys_to_user ( stack ), stacksz );
+ copy_to_rm_stack ( phys_to_virt ( stack ), stacksz );
com32_farcall_proc = proc;
__asm__ __volatile__ (
@@ -194,7 +190,7 @@ int __asmcall com32_cfarcall ( uint32_t proc, physaddr_t stack, size_t stacksz )
:
: "ecx", "edx" );
- remove_user_from_rm_stack ( 0, stacksz );
+ remove_from_rm_stack ( NULL, stacksz );
return eax;
}
diff --git a/src/arch/x86/interface/syslinux/comboot_call.c b/src/arch/x86/interface/syslinux/comboot_call.c
index b75e8ef7c..c3e921075 100644
--- a/src/arch/x86/interface/syslinux/comboot_call.c
+++ b/src/arch/x86/interface/syslinux/comboot_call.c
@@ -37,6 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/posix_io.h>
#include <ipxe/process.h>
#include <ipxe/serial.h>
+#include <ipxe/ns16550.h>
#include <ipxe/init.h>
#include <ipxe/image.h>
#include <ipxe/version.h>
@@ -88,14 +89,9 @@ static uint16_t comboot_graphics_mode = 0;
* Print a string with a particular terminator
*/
static void print_user_string ( unsigned int segment, unsigned int offset, char terminator ) {
- int i = 0;
- char c;
- userptr_t str = real_to_user ( segment, offset );
- for ( ; ; ) {
- copy_from_user ( &c, str, i, 1 );
- if ( c == terminator ) break;
- putchar ( c );
- i++;
+ char *c;
+ for ( c = real_to_virt ( segment, offset ) ; *c != terminator ; c++ ) {
+ putchar ( *c );
}
}
@@ -109,26 +105,26 @@ static void shuffle ( unsigned int list_segment, unsigned int list_offset, unsig
unsigned int i;
/* Copy shuffle descriptor list so it doesn't get overwritten */
- copy_from_user ( shuf, real_to_user ( list_segment, list_offset ), 0,
- count * sizeof( comboot_shuffle_descriptor ) );
+ memcpy ( shuf, real_to_virt ( list_segment, list_offset ),
+ count * sizeof( comboot_shuffle_descriptor ) );
/* Do the copies */
for ( i = 0; i < count; i++ ) {
- userptr_t src_u = phys_to_user ( shuf[ i ].src );
- userptr_t dest_u = phys_to_user ( shuf[ i ].dest );
+ const void *src = phys_to_virt ( shuf[ i ].src );
+ void *dest = phys_to_virt ( shuf[ i ].dest );
if ( shuf[ i ].src == 0xFFFFFFFF ) {
/* Fill with 0 instead of copying */
- memset_user ( dest_u, 0, 0, shuf[ i ].len );
+ memset ( dest, 0, shuf[ i ].len );
} else if ( shuf[ i ].dest == 0xFFFFFFFF ) {
/* Copy new list of descriptors */
count = shuf[ i ].len / sizeof( comboot_shuffle_descriptor );
assert ( count <= COMBOOT_MAX_SHUFFLE_DESCRIPTORS );
- copy_from_user ( shuf, src_u, 0, shuf[ i ].len );
+ memcpy ( shuf, src, shuf[ i ].len );
i = -1;
} else {
/* Regular copy */
- memmove_user ( dest_u, 0, src_u, 0, shuf[ i ].len );
+ memmove ( dest, src, shuf[ i ].len );
}
}
}
@@ -164,7 +160,7 @@ void comboot_force_text_mode ( void ) {
/**
* Fetch kernel and optional initrd
*/
-static int comboot_fetch_kernel ( char *kernel_file, char *cmdline ) {
+static int comboot_fetch_kernel ( const char *kernel_file, char *cmdline ) {
struct image *kernel;
struct image *initrd;
char *initrd_file;
@@ -258,8 +254,8 @@ static __asmcall __used void int21 ( struct i386_all_regs *ix86 ) {
break;
case 0x04: /* Write Character to Serial Port */
- if ( serial_console.base ) {
- uart_transmit ( &serial_console, ix86->regs.dl );
+ if ( serial_console ) {
+ uart_transmit ( serial_console, ix86->regs.dl );
ix86->flags &= ~CF;
}
break;
@@ -346,10 +342,8 @@ static __asmcall __used void int22 ( struct i386_all_regs *ix86 ) {
case 0x0003: /* Run command */
{
- userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx );
- int len = strlen_user ( cmd_u, 0 );
- char cmd[len + 1];
- copy_from_user ( cmd, cmd_u, 0, len + 1 );
+ const char *cmd = real_to_virt ( ix86->segs.es,
+ ix86->regs.bx );
DBG ( "COMBOOT: executing command '%s'\n", cmd );
system ( cmd );
DBG ( "COMBOOT: exiting after executing command...\n" );
@@ -370,11 +364,8 @@ static __asmcall __used void int22 ( struct i386_all_regs *ix86 ) {
case 0x0006: /* Open file */
{
int fd;
- userptr_t file_u = real_to_user ( ix86->segs.es, ix86->regs.si );
- int len = strlen_user ( file_u, 0 );
- char file[len + 1];
-
- copy_from_user ( file, file_u, 0, len + 1 );
+ const char *file = real_to_virt ( ix86->segs.es,
+ ix86->regs.si );
if ( file[0] == '\0' ) {
DBG ( "COMBOOT: attempted open with empty file name\n" );
@@ -410,7 +401,8 @@ static __asmcall __used void int22 ( struct i386_all_regs *ix86 ) {
int len = ix86->regs.cx * COMBOOT_FILE_BLOCKSZ;
int rc;
fd_set fds;
- userptr_t buf = real_to_user ( ix86->segs.es, ix86->regs.bx );
+ void *buf = real_to_virt ( ix86->segs.es,
+ ix86->regs.bx );
/* Wait for data ready to read */
FD_ZERO ( &fds );
@@ -418,7 +410,7 @@ static __asmcall __used void int22 ( struct i386_all_regs *ix86 ) {
select ( &fds, 1 );
- rc = read_user ( fd, buf, 0, len );
+ rc = read ( fd, buf, len );
if ( rc < 0 ) {
DBG ( "COMBOOT: read failed\n" );
ix86->regs.si = 0;
@@ -454,9 +446,11 @@ static __asmcall __used void int22 ( struct i386_all_regs *ix86 ) {
break;
case 0x000B: /* Get Serial Console Configuration */
- if ( serial_console.base ) {
- ix86->regs.dx = ( ( intptr_t ) serial_console.base );
- ix86->regs.cx = serial_console.divisor;
+ if ( serial_console ) {
+ struct ns16550_uart *comport = serial_console->priv;
+
+ ix86->regs.dx = ( ( intptr_t ) comport->base );
+ ix86->regs.cx = comport->divisor;
ix86->regs.bx = 0;
ix86->flags &= ~CF;
}
@@ -483,13 +477,10 @@ static __asmcall __used void int22 ( struct i386_all_regs *ix86 ) {
case 0x0010: /* Resolve hostname */
{
- userptr_t hostname_u = real_to_user ( ix86->segs.es, ix86->regs.bx );
- int len = strlen_user ( hostname_u, 0 );
- char hostname[len];
+ const char *hostname = real_to_virt ( ix86->segs.es,
+ ix86->regs.bx );
struct in_addr addr;
- copy_from_user ( hostname, hostname_u, 0, len + 1 );
-
/* TODO:
* "If the hostname does not contain a dot (.), the
* local domain name is automatically appended."
@@ -526,8 +517,8 @@ static __asmcall __used void int22 ( struct i386_all_regs *ix86 ) {
"lret\n\t"
)
:
- : "r" ( ix86->segs.ds ),
- "r" ( ix86->regs.ebp ),
+ : "R" ( ix86->segs.ds ),
+ "R" ( ix86->regs.ebp ),
"d" ( ix86->regs.ebx ),
"S" ( ix86->regs.esi ) );
@@ -549,15 +540,10 @@ static __asmcall __used void int22 ( struct i386_all_regs *ix86 ) {
case 0x0016: /* Run kernel image */
{
- userptr_t file_u = real_to_user ( ix86->segs.ds, ix86->regs.si );
- userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx );
- int file_len = strlen_user ( file_u, 0 );
- int cmd_len = strlen_user ( cmd_u, 0 );
- char file[file_len + 1];
- char cmd[cmd_len + 1];
-
- copy_from_user ( file, file_u, 0, file_len + 1 );
- copy_from_user ( cmd, cmd_u, 0, cmd_len + 1 );
+ const char *file = real_to_virt ( ix86->segs.ds,
+ ix86->regs.si );
+ char *cmd = real_to_virt ( ix86->segs.es,
+ ix86->regs.bx );
DBG ( "COMBOOT: run kernel %s %s\n", file, cmd );
comboot_fetch_kernel ( file, cmd );
@@ -595,9 +581,9 @@ static __asmcall __used void int22 ( struct i386_all_regs *ix86 ) {
shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx );
/* Copy initial register values to .text16 */
- memcpy_user ( real_to_user ( rm_cs, (unsigned) __from_text16 ( &comboot_initial_regs ) ), 0,
- real_to_user ( ix86->segs.ds, ix86->regs.si ), 0,
- sizeof(syslinux_rm_regs) );
+ memcpy ( real_to_virt ( rm_cs, (unsigned) __from_text16 ( &comboot_initial_regs ) ),
+ real_to_virt ( ix86->segs.ds, ix86->regs.si ),
+ sizeof(syslinux_rm_regs) );
/* Load initial register values */
__asm__ __volatile__ (
@@ -702,4 +688,4 @@ void unhook_comboot_interrupts ( ) {
}
/* Avoid dragging in serial console support unconditionally */
-struct uart serial_console __attribute__ (( weak ));
+struct uart *serial_console __attribute__ (( weak ));
diff --git a/src/arch/x86/interface/vmware/guestinfo.c b/src/arch/x86/interface/vmware/guestinfo.c
index 4134515c1..c181d96e9 100644
--- a/src/arch/x86/interface/vmware/guestinfo.c
+++ b/src/arch/x86/interface/vmware/guestinfo.c
@@ -200,6 +200,7 @@ static void guestinfo_init ( void ) {
/** GuestInfo settings initialiser */
struct init_fn guestinfo_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "guestinfo",
.initialise = guestinfo_init,
};
diff --git a/src/arch/x86/interface/vmware/vmconsole.c b/src/arch/x86/interface/vmware/vmconsole.c
index f7df4f75b..3b892c837 100644
--- a/src/arch/x86/interface/vmware/vmconsole.c
+++ b/src/arch/x86/interface/vmware/vmconsole.c
@@ -134,5 +134,6 @@ static void vmconsole_init ( void ) {
* VMware logfile console initialisation function
*/
struct init_fn vmconsole_init_fn __init_fn ( INIT_CONSOLE ) = {
+ .name = "vmconsole",
.initialise = vmconsole_init,
};
diff --git a/src/arch/x86/prefix/lkrnprefix.S b/src/arch/x86/prefix/lkrnprefix.S
index c8a04c9d7..19d30141b 100644
--- a/src/arch/x86/prefix/lkrnprefix.S
+++ b/src/arch/x86/prefix/lkrnprefix.S
@@ -104,6 +104,7 @@ hardware_subarch:
hardware_subarch_data:
.byte 0, 0, 0, 0, 0, 0, 0, 0
+ .section ".prefix.data", "aw", @progbits
version_string:
.asciz VERSION
@@ -113,6 +114,7 @@ version_string:
*
*/
+ .section ".prefix", "ax", @progbits
setup:
/* Fix up code segment */
pushw %ds
diff --git a/src/arch/x86/prefix/pxeprefix.S b/src/arch/x86/prefix/pxeprefix.S
index 5181ef618..067af99fa 100644
--- a/src/arch/x86/prefix/pxeprefix.S
+++ b/src/arch/x86/prefix/pxeprefix.S
@@ -1,6 +1,7 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#define PXENV_UNDI_SHUTDOWN 0x0005
+#define PXENV_UNDI_GET_INFORMATION 0x000c
#define PXENV_UNDI_GET_NIC_TYPE 0x0012
#define PXENV_UNDI_GET_IFACE_INFO 0x0013
#define PXENV_STOP_UNDI 0x0015
@@ -325,18 +326,37 @@ print_structure_information:
*****************************************************************************
*/
get_physical_device:
+ /* Allow for devices that fail to set the physical device type */
+ movb $0xeb, %al
+ movb %al, ( pxe_parameter_structure + 0x02 )
/* Issue PXENV_UNDI_GET_NIC_TYPE */
movw $PXENV_UNDI_GET_NIC_TYPE, %bx
call pxe_call
jnc 1f
call print_pxe_error
+ movw $10f, %si
+ call print_message
jmp no_physical_device
1: /* Determine physical device type */
+ movw $10f, %si
+ call print_message
movb ( pxe_parameter_structure + 0x02 ), %al
cmpb $2, %al
je pci_physical_device
+ cmpb $0xeb, %al
+ je probably_pci_physical_device
jmp no_physical_device
+ .section ".prefix.data", "aw", @progbits
+10: .asciz " UNDI device is "
+ .previous
+probably_pci_physical_device:
+ /* Device did not write the type byte: assume PCI */
+ movw $10f, %si
+ call print_message
+ .section ".prefix.data", "aw", @progbits
+10: .asciz "probably "
+ .previous
pci_physical_device:
/* Record PCI bus:dev.fn and vendor/device IDs */
movl ( pxe_parameter_structure + 0x03 ), %eax
@@ -346,9 +366,17 @@ pci_physical_device:
movw $10f, %si
call print_message
call print_pci_busdevfn
+ movb $( ' ' ), %al
+ call print_character
+ movw pci_vendor, %ax
+ call print_hex_word
+ movb $( ':' ), %al
+ call print_character
+ movw pci_device, %ax
+ call print_hex_word
jmp 99f
.section ".prefix.data", "aw", @progbits
-10: .asciz " UNDI device is PCI "
+10: .asciz "PCI "
.previous
no_physical_device:
@@ -356,12 +384,39 @@ no_physical_device:
movw $10f, %si
call print_message
.section ".prefix.data", "aw", @progbits
-10: .asciz " Unable to determine UNDI physical device"
+10: .asciz "unknown"
.previous
99:
/*****************************************************************************
+ * Get IRQ number
+ *****************************************************************************
+ */
+get_irq:
+ /* Issue PXENV_UNDI_GET_INFORMATION */
+ movw $PXENV_UNDI_GET_INFORMATION, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+ jmp 99f
+1: /* Check for a valid IRQ number */
+ movw ( pxe_parameter_structure + 0x04 ), %ax
+ testw %ax, %ax
+ jz 99f
+ cmpw $15, %ax
+ ja 99f
+ /* Store IRQ number */
+ movw %ax, undi_irq
+ movw $10f, %si
+ call print_message
+ call print_word
+ .section ".prefix.data", "aw", @progbits
+10: .asciz ", IRQ "
+ .previous
+99:
+
+/*****************************************************************************
* Determine interface type
*****************************************************************************
*/
@@ -452,6 +507,30 @@ pxe_cmdline:
.previous
/*****************************************************************************
+ * Ensure NIC interrupt is disabled
+ *****************************************************************************
+ */
+disable_irq:
+ /* Check for a recorded IRQ number */
+ movw undi_irq, %cx
+ testw %cx, %cx
+ jz 99f
+ /* Calculate IMR */
+ movw %cx, %dx
+ shlw $4, %dx
+ andb $0x80, %dl
+ orb $0x21, %dl
+ /* Calculate mask value */
+ movb $0x01, %bl
+ andb $0x07, %cl
+ shlb %cl, %bl
+ /* Mask interrupt */
+ inb %dx, %al
+ orb %bl, %al
+ outb %al, %dx
+99:
+
+/*****************************************************************************
* Leave NIC in a safe state
*****************************************************************************
*/
@@ -740,6 +819,8 @@ undi_data_segoff:
undi_data_size: .word 0
undi_data_segment: .word 0
+undi_irq: .word 0
+
pxe_hacks: .word 0
/* The following fields are part of a struct undi_device */
diff --git a/src/arch/x86/prefix/unlzma.S b/src/arch/x86/prefix/unlzma.S
index e4d1e190d..6ab3222e2 100644
--- a/src/arch/x86/prefix/unlzma.S
+++ b/src/arch/x86/prefix/unlzma.S
@@ -45,7 +45,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
.section ".note.GNU-stack", "", @progbits
.code32
- .arch i486
+ .arch i386
.section ".prefix.lib", "ax", @progbits
#ifdef CODE16
@@ -962,7 +962,9 @@ decompress:
ADDR32 lodsb /* discard initial byte */
print_hex_byte %al
ADDR32 lodsl
- bswapl %eax
+ xchgb %al, %ah
+ roll $16, %eax
+ xchgb %al, %ah
print_hex_dword %eax
print_character $('\n')
movl %eax, rc_code(%ebp)
diff --git a/src/arch/x86/scripts/pcbios.lds b/src/arch/x86/scripts/pcbios.lds
index e208b174b..f8c137283 100644
--- a/src/arch/x86/scripts/pcbios.lds
+++ b/src/arch/x86/scripts/pcbios.lds
@@ -130,8 +130,12 @@ SECTIONS {
. += 1; /* Prevent NULL being valid */
*(.text)
*(.text.*)
+ *(.srodata)
+ *(.srodata.*)
*(.rodata)
*(.rodata.*)
+ *(.sdata)
+ *(.sdata.*)
*(.data)
*(.data.*)
KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */
@@ -139,6 +143,8 @@ SECTIONS {
KEEP(*(.provided.*))
_mtextdata = .;
} .bss.textdata (NOLOAD) : AT ( _bss_textdata_lma ) {
+ *(.sbss)
+ *(.sbss.*)
*(.bss)
*(.bss.*)
*(COMMON)
diff --git a/src/arch/x86/transitions/librm_mgmt.c b/src/arch/x86/transitions/librm_mgmt.c
index b3820589c..89feec96a 100644
--- a/src/arch/x86/transitions/librm_mgmt.c
+++ b/src/arch/x86/transitions/librm_mgmt.c
@@ -58,35 +58,36 @@ static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" };
static struct profiler other_irq_profiler __profiler = { .name = "irq.other" };
/**
- * Allocate space on the real-mode stack and copy data there from a
- * user buffer
+ * Allocate space on the real-mode stack and copy data there
*
- * @v data User buffer
+ * @v data Stack data
* @v size Size of stack data
* @ret sp New value of real-mode stack pointer
*/
-uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
- userptr_t rm_stack;
+uint16_t copy_to_rm_stack ( const void *data, size_t size ) {
+ void *rm_stack;
+
rm_sp -= size;
- rm_stack = real_to_user ( rm_ss, rm_sp );
- memcpy_user ( rm_stack, 0, data, 0, size );
+ rm_stack = real_to_virt ( rm_ss, rm_sp );
+ memcpy ( rm_stack, data, size );
return rm_sp;
-};
+}
/**
- * Deallocate space on the real-mode stack, optionally copying back
- * data to a user buffer.
+ * Deallocate space on the real-mode stack, optionally copying back data
*
- * @v data User buffer
+ * @v data Stack data buffer, or NULL
* @v size Size of stack data
*/
-void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
+void remove_from_rm_stack ( void *data, size_t size ) {
+ const void *rm_stack;
+
if ( data ) {
- userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );
- memcpy_user ( rm_stack, 0, data, 0, size );
+ rm_stack = real_to_virt ( rm_ss, rm_sp );
+ memcpy ( data, rm_stack, size );
}
rm_sp += size;
-};
+}
/**
* Set interrupt vector
@@ -302,17 +303,14 @@ static void * ioremap_pages ( unsigned long bus_addr, size_t len ) {
/* Calculate number of pages required */
count = ( ( offset + len + IO_PAGE_SIZE - 1 ) / IO_PAGE_SIZE );
assert ( count != 0 );
- assert ( count < ( sizeof ( io_pages.page ) /
- sizeof ( io_pages.page[0] ) ) );
+ assert ( count <= IO_PAGE_COUNT );
/* Round up number of pages to a power of two */
- stride = ( 1 << ( fls ( count ) - 1 ) );
+ stride = ( 1 << fls ( count - 1 ) );
assert ( count <= stride );
/* Allocate pages */
- for ( first = 0 ; first < ( sizeof ( io_pages.page ) /
- sizeof ( io_pages.page[0] ) ) ;
- first += stride ) {
+ for ( first = 0 ; first < IO_PAGE_COUNT ; first += stride ) {
/* Calculate I/O address */
io_addr = ( IO_BASE + ( first * IO_PAGE_SIZE ) + offset );
@@ -365,6 +363,10 @@ static void iounmap_pages ( volatile const void *io_addr ) {
/* Calculate first page table entry */
first = ( ( io_addr - IO_BASE ) / IO_PAGE_SIZE );
+ /* Ignore unmappings outside of the I/O range */
+ if ( first >= IO_PAGE_COUNT )
+ return;
+
/* Clear page table entries */
for ( i = first ; ; i++ ) {
@@ -425,19 +427,9 @@ void setup_sipi ( unsigned int vector, uint32_t handler,
memcpy ( &sipi_regs, regs, sizeof ( sipi_regs ) );
/* Copy real-mode handler */
- copy_to_real ( ( vector << 8 ), 0, sipi, ( ( size_t ) sipi_len ) );
+ copy_to_real ( ( vector << 8 ), 0, sipi, sipi_len );
}
-PROVIDE_UACCESS_INLINE ( librm, phys_to_user );
-PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
-PROVIDE_UACCESS_INLINE ( librm, virt_to_user );
-PROVIDE_UACCESS_INLINE ( librm, user_to_virt );
-PROVIDE_UACCESS_INLINE ( librm, userptr_add );
-PROVIDE_UACCESS_INLINE ( librm, memcpy_user );
-PROVIDE_UACCESS_INLINE ( librm, memmove_user );
-PROVIDE_UACCESS_INLINE ( librm, memset_user );
-PROVIDE_UACCESS_INLINE ( librm, strlen_user );
-PROVIDE_UACCESS_INLINE ( librm, memchr_user );
PROVIDE_IOMAP_INLINE ( pages, io_to_bus );
PROVIDE_IOMAP ( pages, ioremap, ioremap_pages );
PROVIDE_IOMAP ( pages, iounmap, iounmap_pages );
diff --git a/src/arch/x86_64/Makefile b/src/arch/x86_64/Makefile
index b3064b752..276b96d3b 100644
--- a/src/arch/x86_64/Makefile
+++ b/src/arch/x86_64/Makefile
@@ -1,3 +1,7 @@
+# Specify compressor
+#
+ZBIN = $(ZBIN64)
+
# Code size reduction.
#
CFLAGS += -fstrength-reduce -fomit-frame-pointer
diff --git a/src/arch/x86_64/Makefile.efi b/src/arch/x86_64/Makefile.efi
index 0041bb8f0..f38375607 100644
--- a/src/arch/x86_64/Makefile.efi
+++ b/src/arch/x86_64/Makefile.efi
@@ -12,10 +12,6 @@ CFLAGS += -mno-red-zone
#
ELF2EFI = $(ELF2EFI64)
-# Specify EFI boot file
-#
-EFI_BOOT_FILE = bootx64.efi
-
# Include generic EFI Makefile
#
MAKEDEPS += arch/x86/Makefile.efi
diff --git a/src/arch/x86_64/include/bits/byteswap.h b/src/arch/x86_64/include/bits/byteswap.h
index d8c5098ef..7c48a27ca 100644
--- a/src/arch/x86_64/include/bits/byteswap.h
+++ b/src/arch/x86_64/include/bits/byteswap.h
@@ -10,6 +10,7 @@
#include <stdint.h>
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
static inline __attribute__ (( always_inline, const )) uint16_t
__bswap_variable_16 ( uint16_t x ) {
diff --git a/src/arch/x86_64/include/bits/compiler.h b/src/arch/x86_64/include/bits/compiler.h
index 1c04a7b30..99185b058 100644
--- a/src/arch/x86_64/include/bits/compiler.h
+++ b/src/arch/x86_64/include/bits/compiler.h
@@ -2,6 +2,7 @@
#define _BITS_COMPILER_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** Dummy relocation type */
#define RELOC_TYPE_NONE R_X86_64_NONE
diff --git a/src/arch/x86_64/include/gdbmach.h b/src/arch/x86_64/include/bits/gdbmach.h
index 367405fd6..367405fd6 100644
--- a/src/arch/x86_64/include/gdbmach.h
+++ b/src/arch/x86_64/include/bits/gdbmach.h
diff --git a/src/arch/x86_64/include/bits/profile.h b/src/arch/x86_64/include/bits/profile.h
index b7c74fbe7..c8e0a21f1 100644
--- a/src/arch/x86_64/include/bits/profile.h
+++ b/src/arch/x86_64/include/bits/profile.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
@@ -16,7 +17,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
* @ret timestamp Timestamp
*/
-static inline __attribute__ (( always_inline )) uint64_t
+static inline __attribute__ (( always_inline )) unsigned long
profile_timestamp ( void ) {
uint32_t eax;
uint32_t edx;
diff --git a/src/arch/x86_64/include/setjmp.h b/src/arch/x86_64/include/bits/setjmp.h
index 69835d9fa..adfb869ea 100644
--- a/src/arch/x86_64/include/setjmp.h
+++ b/src/arch/x86_64/include/bits/setjmp.h
@@ -1,5 +1,5 @@
-#ifndef _SETJMP_H
-#define _SETJMP_H
+#ifndef _BITS_SETJMP_H
+#define _BITS_SETJMP_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
@@ -25,10 +25,4 @@ typedef struct {
uint64_t r15;
} jmp_buf[1];
-extern int __asmcall __attribute__ (( returns_twice ))
-setjmp ( jmp_buf env );
-
-extern void __asmcall __attribute__ (( noreturn ))
-longjmp ( jmp_buf env, int val );
-
-#endif /* _SETJMP_H */
+#endif /* _BITS_SETJMP_H */
diff --git a/src/arch/x86_64/include/bits/stdint.h b/src/arch/x86_64/include/bits/stdint.h
index fe1f9946a..e75bed502 100644
--- a/src/arch/x86_64/include/bits/stdint.h
+++ b/src/arch/x86_64/include/bits/stdint.h
@@ -2,6 +2,7 @@
#define _BITS_STDINT_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
typedef __SIZE_TYPE__ size_t;
typedef signed long ssize_t;
diff --git a/src/arch/x86_64/include/bits/strings.h b/src/arch/x86_64/include/bits/strings.h
index 3b7911f3b..6da8f1350 100644
--- a/src/arch/x86_64/include/bits/strings.h
+++ b/src/arch/x86_64/include/bits/strings.h
@@ -2,6 +2,7 @@
#define _BITS_STRINGS_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* Find first (i.e. least significant) set bit
diff --git a/src/arch/x86_64/include/ipxe/efi/dhcparch.h b/src/arch/x86_64/include/ipxe/efi/dhcparch.h
index ccf0f46a0..f75bf9145 100644
--- a/src/arch/x86_64/include/ipxe/efi/dhcparch.h
+++ b/src/arch/x86_64/include/ipxe/efi/dhcparch.h
@@ -8,6 +8,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/dhcp.h>
diff --git a/src/arch/x86_64/include/limits.h b/src/arch/x86_64/include/limits.h
index a1374a17f..e75461acb 100644
--- a/src/arch/x86_64/include/limits.h
+++ b/src/arch/x86_64/include/limits.h
@@ -2,6 +2,7 @@
#define LIMITS_H 1
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/* Number of bits in a `char' */
#define CHAR_BIT 8