diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /arch/h8300 | |
parent | Initial commit. (diff) | |
download | linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip |
Adding upstream version 5.10.209.upstream/5.10.209
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/h8300')
94 files changed, 6012 insertions, 0 deletions
diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig new file mode 100644 index 000000000..7945de067 --- /dev/null +++ b/arch/h8300/Kconfig @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0 +config H8300 + def_bool y + select ARCH_32BIT_OFF_T + select ARCH_HAS_BINFMT_FLAT + select BINFMT_FLAT_ARGVP_ENVP_ON_STACK + select BINFMT_FLAT_OLD_ALWAYS_RAM + select GENERIC_ATOMIC64 + select HAVE_UID16 + select VIRT_TO_BUS + select GENERIC_IRQ_SHOW + select FRAME_POINTER + select GENERIC_CPU_DEVICES + select MODULES_USE_ELF_RELA + select GENERIC_CLOCKEVENTS + select COMMON_CLK + select ARCH_WANT_FRAME_POINTERS + select OF + select OF_IRQ + select OF_EARLY_FLATTREE + select TIMER_OF + select H8300_TMR8 + select HAVE_KERNEL_GZIP + select HAVE_KERNEL_LZO + select HAVE_ARCH_KGDB + select HAVE_ARCH_HASH + select CPU_NO_EFFICIENT_FFS + select SET_FS + select UACCESS_MEMCPY + +config CPU_BIG_ENDIAN + def_bool y + +config GENERIC_HWEIGHT + def_bool y + +config NO_IOPORT_MAP + def_bool y + +config GENERIC_CSUM + def_bool y + +config HZ + int + default 100 + +config NR_CPUS + int + default 1 + +source "arch/h8300/Kconfig.cpu" diff --git a/arch/h8300/Kconfig.cpu b/arch/h8300/Kconfig.cpu new file mode 100644 index 000000000..b5e14d513 --- /dev/null +++ b/arch/h8300/Kconfig.cpu @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: GPL-2.0 +config CPU_H8300H + bool + +config CPU_H8S + bool + +config H83069 + bool + select CPU_H8300H + select H8300_TMR16 + select RENESAS_H8300H_INTC + +config H8S2678 + bool + select CPU_H8S + select H8300_TPU + select RENESAS_H8S_INTC + +config RAMKERNEL + bool + +config ROMKERNEL + bool + +menu "Processor type and features" + +choice +prompt "H8/300 platform" + +config H8300_AE3068 + bool "AE-3068/69" + select H83069 + select RAMKERNEL + help + AKI-H8/3068F / AKI-H8/3069F Flashmicom LAN Board Support + More Information. (Japanese Only) + <http://akizukidenshi.com/catalog/default.aspx> + AE-3068/69 Evaluation Board Support + More Information. + <http://www.microtronique.com/ae3069lan.htm> + +config H8300_H8MAX + bool "H8MAX" + select H83069 + select RAMKERNEL + select HAVE_IDE + help + H8MAX Evaluation Board Support + More Information. (Japanese Only) + <http://strawberry-linux.com/h8/index.html> + +config H8300_KANEBEBE + bool "KaneBebe" + select H83069 + select RAMKERNEL + help + KaneBebe Evalition Board Support + More Information. (Japanese Only) + <http://www.nissin-tech.com/2009/10/uclinuxkane-bebe-h83069f.html> + +config H8300H_SIM + bool "H8/300H GDB Simulator" + select H83069 + select ROMKERNEL + help + GDB Simulator Support + More Information. + <http://sourceware.org/sid/> + +config H8S_EDOSK2674 + bool "EDOSK-2674" + select H8S2678 + select RAMKERNEL + help + Renesas EDOSK-2674 Evaluation Board Support + More Information. + <http://www.azpower.com/H8-uClinux/index.html> + <http://www.renesas.eu/products/tools/introductory_evaluation_tools/evaluation_development_os_kits/edosk2674r/edosk2674r_software_tools_root.jsp> + +config H8S_SIM + bool "H8S GDB Simulator" + select H8S2678 + select ROMKERNEL + help + GDB Simulator Support + More Information. + <http://sourceware.org/sid/> + +endchoice + +config H8300_BUILTIN_DTB + string "Builtin DTB" + default "" + +config OFFSET + hex "Load offset" + default 0 + +endmenu diff --git a/arch/h8300/Kconfig.debug b/arch/h8300/Kconfig.debug new file mode 100644 index 000000000..295942fe3 --- /dev/null +++ b/arch/h8300/Kconfig.debug @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +# dummy file, do not delete diff --git a/arch/h8300/Makefile b/arch/h8300/Makefile new file mode 100644 index 000000000..ba0f26cfa --- /dev/null +++ b/arch/h8300/Makefile @@ -0,0 +1,50 @@ +# +# arch/h8300/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# (C) Copyright 2002-2015 Yoshinori Sato <ysato@users.sourceforge.jp> +# + +KBUILD_DEFCONFIG := edosk2674_defconfig + +cflags-$(CONFIG_CPU_H8300H) := -mh +aflags-$(CONFIG_CPU_H8300H) := -mh -Wa,--mach=h8300h +ldflags-$(CONFIG_CPU_H8300H) := -mh8300helf_linux +cflags-$(CONFIG_CPU_H8S) := -ms +aflags-$(CONFIG_CPU_H8S) := -ms -Wa,--mach=h8300s +ldflags-$(CONFIG_CPU_H8S) := -mh8300self_linux + +KBUILD_CFLAGS += $(cflags-y) +KBUILD_CFLAGS += -mint32 -fno-builtin +KBUILD_CFLAGS += -D__linux__ +KBUILD_CFLAGS += -DUTS_SYSNAME=\"uClinux\" +KBUILD_AFLAGS += $(aflags-y) +KBUILD_LDFLAGS += $(ldflags-y) + +CHECKFLAGS += -msize-long + +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := $(call cc-cross-prefix, h8300-unknown-linux- h8300-linux-) +endif + +core-y += arch/$(ARCH)/kernel/ arch/$(ARCH)/mm/ +core-y += arch/$(ARCH)/boot/dts/ + +libs-y += arch/$(ARCH)/lib/ + +boot := arch/h8300/boot + +archclean: + $(Q)$(MAKE) $(clean)=$(boot) + +vmlinux.srec vmlinux.bin zImage uImage.bin: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +define archhelp + @echo 'vmlinux.bin - Create raw binary' + @echo 'vmlinux.srec - Create srec binary' + @echo 'zImage - Compressed kernel image' +endef diff --git a/arch/h8300/boot/Makefile b/arch/h8300/boot/Makefile new file mode 100644 index 000000000..8e62df20e --- /dev/null +++ b/arch/h8300/boot/Makefile @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0 +# arch/h8300/boot/Makefile + +targets := vmlinux.srec vmlinux.bin zImage +subdir- := compressed + +OBJCOPYFLAGS_vmlinux.srec := -Osrec +OBJCOPYFLAGS_vmlinux.bin := -Obinary +OBJCOPYFLAGS_zImage := -O binary -R .note -R .comment -R .stab -R .stabstr -S + +UIMAGE_LOADADDR = $(CONFIG_RAMBASE) +UIMAGE_ENTRYADDR = $(shell /bin/bash -c 'printf "0x%08x" \ + $$[$(CONFIG_RAMBASE) + $(CONFIG_OFFSET)]') + +$(obj)/vmlinux.srec $(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/zImage: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/compressed/vmlinux: FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ + +$(obj)/uImage.bin: $(obj)/vmlinux.bin + $(call if_changed,uimage,none) + +CLEAN_FILES += arch/$(ARCH)/vmlinux.bin arch/$(ARCH)/vmlinux.srec arch/$(ARCH)/uImage.bin diff --git a/arch/h8300/boot/compressed/Makefile b/arch/h8300/boot/compressed/Makefile new file mode 100644 index 000000000..5942793f7 --- /dev/null +++ b/arch/h8300/boot/compressed/Makefile @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# linux/arch/sh/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux +# + +targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o + +OBJECTS = $(obj)/head.o $(obj)/misc.o + +# +# IMAGE_OFFSET is the load offset of the compression loader +# Assign dummy values if these 2 variables are not defined, +# in order to suppress error message. +# +CONFIG_MEMORY_START ?= 0x00400000 +CONFIG_BOOT_LINK_OFFSET ?= 0x00280000 +IMAGE_OFFSET := $(shell printf "0x%08x" $$(($(CONFIG_MEMORY_START)+$(CONFIG_BOOT_LINK_OFFSET)))) + +LIBGCC := $(shell $(CROSS-COMPILE)$(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name 2>/dev/null) +LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -estartup -T $(obj)/vmlinux.lds \ + --defsym output=$(CONFIG_MEMORY_START) + +$(obj)/vmlinux: $(OBJECTS) $(obj)/piggy.o $(LIBGCC) FORCE + $(call if_changed,ld) + +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +suffix-$(CONFIG_KERNEL_GZIP) := gzip +suffix-$(CONFIG_KERNEL_LZO) := lzo + +$(obj)/vmlinux.bin.$(suffix-y): $(obj)/vmlinux.bin FORCE + $(call if_changed,$(suffix-y)) + +LDFLAGS_piggy.o := -r --format binary --oformat elf32-h8300-linux -T +OBJCOPYFLAGS := -O binary + +$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.$(suffix-y) FORCE + $(call if_changed,ld) + +CFLAGS_misc.o = -O0 diff --git a/arch/h8300/boot/compressed/head.S b/arch/h8300/boot/compressed/head.S new file mode 100644 index 000000000..11ef50957 --- /dev/null +++ b/arch/h8300/boot/compressed/head.S @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/arch/h8300/boot/compressed/head.S + * + * Copyright (C) 2006 Yoshinori Sato + */ + +#include <linux/linkage.h> + + .section .text..startup,"ax" + .global startup +startup: + mov.l #startup, sp + mov.l er0, er4 + mov.l #__sbss, er0 + mov.l #__ebss, er1 + sub.l er0, er1 + shlr er1 + shlr er1 + sub.l er2, er2 +1: + mov.l er2, @er0 + adds #4, er0 + dec.l #1, er1 + bne 1b + jsr @decompress_kernel + mov.l er4, er0 + jmp @output + + .align 9 +fake_headers_as_bzImage: + .word 0 + .ascii "HdrS" ; header signature + .word 0x0202 ; header version number (>= 0x0105) + ; or else old loadlin-1.5 will fail) + .word 0 ; default_switch + .word 0 ; SETUPSEG + .word 0x1000 + .word 0 ; pointing to kernel version string + .byte 0 ; = 0, old one (LILO, Loadlin, + ; 0xTV: T=0 for LILO + ; V = version + .byte 1 ; Load flags bzImage=1 + .word 0x8000 ; size to move, when setup is not + .long 0x100000 ; 0x100000 = default for big kernel + .long 0 ; address of loaded ramdisk image + .long 0 ; its size in bytes + + .end diff --git a/arch/h8300/boot/compressed/misc.c b/arch/h8300/boot/compressed/misc.c new file mode 100644 index 000000000..8915d8fe2 --- /dev/null +++ b/arch/h8300/boot/compressed/misc.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * arch/h8300/boot/compressed/misc.c + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * + * Adapted for h8300 by Yoshinori Sato 2006 + */ + +#include <linux/uaccess.h> + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +#undef memset +#undef memcpy +#define memzero(s, n) memset((s), (0), (n)) + +extern int _end; +static unsigned long free_mem_ptr; +static unsigned long free_mem_end_ptr; + +extern char input_data[]; +extern int input_len; +extern char output[]; + +#define HEAP_SIZE 0x10000 + +#ifdef CONFIG_KERNEL_GZIP +#include "../../../../lib/decompress_inflate.c" +#endif + +#ifdef CONFIG_KERNEL_LZO +#include "../../../../lib/decompress_unlzo.c" +#endif + +void *memset(void *s, int c, size_t n) +{ + int i; + char *ss = (char *)s; + + for (i = 0; i < n; i++) + ss[i] = c; + return s; +} + +void *memcpy(void *dest, const void *src, size_t n) +{ + int i; + char *d = (char *)dest, *s = (char *)src; + + for (i = 0; i < n; i++) + d[i] = s[i]; + return dest; +} + +static void error(char *x) +{ + while (1) + ; /* Halt */ +} + +void decompress_kernel(void) +{ + free_mem_ptr = (unsigned long)&_end; + free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; + + __decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error); +} diff --git a/arch/h8300/boot/compressed/vmlinux.lds b/arch/h8300/boot/compressed/vmlinux.lds new file mode 100644 index 000000000..7f7bb419c --- /dev/null +++ b/arch/h8300/boot/compressed/vmlinux.lds @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +SECTIONS +{ + .text : + { + __stext = . ; + __text = .; + *(.text..startup) + *(.text) + __etext = . ; + } + + .rodata : + { + *(.rodata) + } + . = ALIGN(0x4) ; + .data : + + { + . = ALIGN(0x4) ; + __sdata = . ; + ___data_start = . ; + *(.data.*) + } + . = ALIGN(0x4) ; + .bss : + { + __sbss = . ; + *(.bss*) + . = ALIGN(0x4) ; + __ebss = . ; + } + _end = . ; +} diff --git a/arch/h8300/boot/compressed/vmlinux.scr b/arch/h8300/boot/compressed/vmlinux.scr new file mode 100644 index 000000000..a08490360 --- /dev/null +++ b/arch/h8300/boot/compressed/vmlinux.scr @@ -0,0 +1,9 @@ +SECTIONS +{ + .data : { + input_len = .; + LONG(input_data_end - input_data) input_data = .; + *(.data) + input_data_end = .; + } +} diff --git a/arch/h8300/boot/dts/Makefile b/arch/h8300/boot/dts/Makefile new file mode 100644 index 000000000..69fcd8178 --- /dev/null +++ b/arch/h8300/boot/dts/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +ifneq '$(CONFIG_H8300_BUILTIN_DTB)' '""' +BUILTIN_DTB := $(patsubst "%",%,$(CONFIG_H8300_BUILTIN_DTB)).dtb.o +endif + +obj-y += $(BUILTIN_DTB) + +dtb-$(CONFIG_H8300H_SIM) := h8300h_sim.dtb +dtb-$(CONFIG_H8S_SIM) := h8s_sim.dtb +dtb-$(CONFIG_H8S_EDOSK2674) := edosk2674.dtb diff --git a/arch/h8300/boot/dts/edosk2674.dts b/arch/h8300/boot/dts/edosk2674.dts new file mode 100644 index 000000000..d1733805e --- /dev/null +++ b/arch/h8300/boot/dts/edosk2674.dts @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/ { + compatible = "renesas,edosk2674"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&h8intc>; + + chosen { + bootargs = "console=ttySC2,38400"; + stdout-path = &sci2; + }; + aliases { + serial0 = &sci0; + serial1 = &sci1; + serial2 = &sci2; + }; + + xclk: oscillator { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <33333333>; + clock-output-names = "xtal"; + }; + pllclk: pllclk { + compatible = "renesas,h8s2678-pll-clock"; + clocks = <&xclk>; + #clock-cells = <0>; + reg = <0xffff3b 1>, <0xffff45 1>; + }; + core_clk: core_clk { + compatible = "renesas,h8300-div-clock"; + clocks = <&pllclk>; + #clock-cells = <0>; + reg = <0xffff3b 1>; + renesas,width = <3>; + }; + fclk: fclk { + compatible = "fixed-factor-clock"; + clocks = <&core_clk>; + #clock-cells = <0>; + clock-div = <1>; + clock-mult = <1>; + }; + + memory@400000 { + device_type = "memory"; + reg = <0x400000 0x800000>; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu@0 { + compatible = "renesas,h8300"; + clock-frequency = <33333333>; + }; + }; + + h8intc: interrupt-controller@fffe00 { + compatible = "renesas,h8s-intc", "renesas,h8300-intc"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0xfffe00 24>; + }; + + bsc: memory-controller@fffec0 { + compatible = "renesas,h8s-bsc", "renesas,h8300-bsc"; + reg = <0xfffec0 24>; + }; + + tpu: timer@ffffe0 { + compatible = "renesas,tpu"; + reg = <0xffffe0 16>, <0xfffff0 12>; + clocks = <&fclk>; + clock-names = "fck"; + }; + + timer8: timer@ffffb0 { + compatible = "renesas,8bit-timer"; + reg = <0xffffb0 10>; + interrupts = <72 0>; + clocks = <&fclk>; + clock-names = "fck"; + }; + + sci0: serial@ffff78 { + compatible = "renesas,sci"; + reg = <0xffff78 8>; + interrupts = <88 0>, <89 0>, <90 0>, <91 0>; + clocks = <&fclk>; + clock-names = "fck"; + }; + sci1: serial@ffff80 { + compatible = "renesas,sci"; + reg = <0xffff80 8>; + interrupts = <92 0>, <93 0>, <94 0>, <95 0>; + clocks = <&fclk>; + clock-names = "fck"; + }; + sci2: serial@ffff88 { + compatible = "renesas,sci"; + reg = <0xffff88 8>; + interrupts = <96 0>, <97 0>, <98 0>, <99 0>; + clocks = <&fclk>; + clock-names = "fck"; + }; +}; diff --git a/arch/h8300/boot/dts/h8300h_sim.dts b/arch/h8300/boot/dts/h8300h_sim.dts new file mode 100644 index 000000000..595398b9d --- /dev/null +++ b/arch/h8300/boot/dts/h8300h_sim.dts @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/ { + compatible = "gnu,gdbsim"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&h8intc>; + + chosen { + bootargs = "earlyprintk=h8300-sim"; + stdout-path = <&sci0>; + }; + aliases { + serial0 = &sci0; + serial1 = &sci1; + }; + + xclk: oscillator { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <20000000>; + clock-output-names = "xtal"; + }; + core_clk: core_clk { + compatible = "renesas,h8300-div-clock"; + clocks = <&xclk>; + #clock-cells = <0>; + reg = <0xfee01b 2>; + renesas,width = <2>; + }; + fclk: fclk { + compatible = "fixed-factor-clock"; + clocks = <&core_clk>; + #clock-cells = <0>; + clock-div = <1>; + clock-mult = <1>; + }; + + memory@400000 { + device_type = "memory"; + reg = <0x400000 0x400000>; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu@0 { + compatible = "renesas,h8300"; + clock-frequency = <20000000>; + }; + }; + + h8intc: interrupt-controller@fee012 { + compatible = "renesas,h8300h-intc", "renesas,h8300-intc"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0xfee012 7>; + }; + + bsc: memory-controller@fee01e { + compatible = "renesas,h8300h-bsc", "renesas,h8300-bsc"; + reg = <0xfee01e 8>; + }; + + timer8: timer@ffff80 { + compatible = "renesas,8bit-timer"; + reg = <0xffff80 10>; + interrupts = <36 0>; + clocks = <&fclk>; + clock-names = "fck"; + }; + + timer16: timer@ffff68 { + compatible = "renesas,16bit-timer"; + reg = <0xffff68 8>, <0xffff60 8>; + interrupts = <26 0>; + renesas,channel = <0>; + clocks = <&fclk>; + clock-names = "fck"; + }; + + sci0: serial@ffffb0 { + compatible = "renesas,sci"; + reg = <0xffffb0 8>; + interrupts = <52 0>, <53 0>, <54 0>, <55 0>; + clocks = <&fclk>; + clock-names = "fck"; + }; + + sci1: serial@ffffb8 { + compatible = "renesas,sci"; + reg = <0xffffb8 8>; + interrupts = <56 0>, <57 0>, <58 0>, <59 0>; + clocks = <&fclk>; + clock-names = "fck"; + }; +}; diff --git a/arch/h8300/boot/dts/h8s_sim.dts b/arch/h8300/boot/dts/h8s_sim.dts new file mode 100644 index 000000000..932cc3c5a --- /dev/null +++ b/arch/h8300/boot/dts/h8s_sim.dts @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/ { + compatible = "gnu,gdbsim"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&h8intc>; + + chosen { + bootargs = "earlyprintk=h8300-sim"; + stdout-path = <&sci0>; + }; + aliases { + serial0 = &sci0; + serial1 = &sci1; + }; + + xclk: oscillator { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <33333333>; + clock-output-names = "xtal"; + }; + pllclk: pllclk { + compatible = "renesas,h8s2678-pll-clock"; + clocks = <&xclk>; + #clock-cells = <0>; + reg = <0xfee03b 2>, <0xfee045 2>; + }; + core_clk: core_clk { + compatible = "renesas,h8300-div-clock"; + clocks = <&pllclk>; + #clock-cells = <0>; + reg = <0xfee03b 2>; + renesas,width = <3>; + }; + fclk: fclk { + compatible = "fixed-factor-clock"; + clocks = <&core_clk>; + #clock-cells = <0>; + clock-div = <1>; + clock-mult = <1>; + }; + + memory@400000 { + device_type = "memory"; + reg = <0x400000 0x800000>; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu@0 { + compatible = "renesas,h8300"; + clock-frequency = <33333333>; + }; + }; + + h8intc: interrupt-controller@fffe00 { + compatible = "renesas,h8s-intc", "renesas,h8300-intc"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0xfffe00 24>; + }; + + bsc: memory-controller@fffec0 { + compatible = "renesas,h8s-bsc", "renesas,h8300-bsc"; + reg = <0xfffec0 24>; + }; + + tpu: timer@ffffe0 { + compatible = "renesas,tpu"; + reg = <0xffffe0 16>, <0xfffff0 12>; + clocks = <&fclk>; + clock-names = "fck"; + }; + + timer8: timer@ffffb0 { + compatible = "renesas,8bit-timer"; + reg = <0xffffb0 10>; + interrupts = <72 0>; + clocks = <&fclk>; + clock-names = "fck"; + }; + + sci0: serial@ffff78 { + compatible = "renesas,sci"; + reg = <0xffff78 8>; + interrupts = <88 0>, <89 0>, <90 0>, <91 0>; + clocks = <&fclk>; + clock-names = "fck"; + }; + sci1: serial@ffff80 { + compatible = "renesas,sci"; + reg = <0xffff80 8>; + interrupts = <92 0>, <93 0>, <94 0>, <95 0>; + clocks = <&fclk>; + clock-names = "fck"; + }; +}; diff --git a/arch/h8300/configs/edosk2674_defconfig b/arch/h8300/configs/edosk2674_defconfig new file mode 100644 index 000000000..23791dcf6 --- /dev/null +++ b/arch/h8300/configs/edosk2674_defconfig @@ -0,0 +1,48 @@ +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_USELIB is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_UID16 is not set +# CONFIG_SYSFS_SYSCALL is not set +# CONFIG_KALLSYMS is not set +# CONFIG_BASE_FULL is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +# CONFIG_SIGNALFD is not set +# CONFIG_TIMERFD is not set +# CONFIG_EVENTFD is not set +# CONFIG_AIO is not set +# CONFIG_ADVISE_SYSCALLS is not set +CONFIG_EMBEDDED=y +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_SLOB=y +# CONFIG_BLOCK is not set +CONFIG_H8S_SIM=y +CONFIG_H8300_BUILTIN_DTB="h8s_sim" +# CONFIG_BINFMT_SCRIPT is not set +CONFIG_BINFMT_FLAT=y +# CONFIG_COREDUMP is not set +# CONFIG_UEVENT_HELPER is not set +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set +# CONFIG_ALLOW_DEV_COREDUMP is not set +# CONFIG_INPUT is not set +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_UNIX98_PTYS is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_SH_SCI=y +CONFIG_SERIAL_SH_SCI_CONSOLE=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_FILE_LOCKING is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_PROC_FS is not set +# CONFIG_SYSFS is not set +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_MUST_CHECK is not set diff --git a/arch/h8300/configs/h8300h-sim_defconfig b/arch/h8300/configs/h8300h-sim_defconfig new file mode 100644 index 000000000..7fc9c2f0a --- /dev/null +++ b/arch/h8300/configs/h8300h-sim_defconfig @@ -0,0 +1,48 @@ +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_USELIB is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_UID16 is not set +# CONFIG_SYSFS_SYSCALL is not set +# CONFIG_KALLSYMS is not set +# CONFIG_BASE_FULL is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +# CONFIG_SIGNALFD is not set +# CONFIG_TIMERFD is not set +# CONFIG_EVENTFD is not set +# CONFIG_AIO is not set +# CONFIG_ADVISE_SYSCALLS is not set +CONFIG_EMBEDDED=y +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_SLOB=y +# CONFIG_BLOCK is not set +CONFIG_H8300H_SIM=y +CONFIG_H8300_BUILTIN_DTB="h8300h_sim" +# CONFIG_BINFMT_SCRIPT is not set +CONFIG_BINFMT_FLAT=y +# CONFIG_COREDUMP is not set +# CONFIG_UEVENT_HELPER is not set +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set +# CONFIG_ALLOW_DEV_COREDUMP is not set +# CONFIG_INPUT is not set +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_UNIX98_PTYS is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_SH_SCI=y +CONFIG_SERIAL_SH_SCI_EARLYCON=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_FILE_LOCKING is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_PROC_FS is not set +# CONFIG_SYSFS is not set +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_MUST_CHECK is not set diff --git a/arch/h8300/configs/h8s-sim_defconfig b/arch/h8300/configs/h8s-sim_defconfig new file mode 100644 index 000000000..23791dcf6 --- /dev/null +++ b/arch/h8300/configs/h8s-sim_defconfig @@ -0,0 +1,48 @@ +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_USELIB is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_UID16 is not set +# CONFIG_SYSFS_SYSCALL is not set +# CONFIG_KALLSYMS is not set +# CONFIG_BASE_FULL is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +# CONFIG_SIGNALFD is not set +# CONFIG_TIMERFD is not set +# CONFIG_EVENTFD is not set +# CONFIG_AIO is not set +# CONFIG_ADVISE_SYSCALLS is not set +CONFIG_EMBEDDED=y +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_SLOB=y +# CONFIG_BLOCK is not set +CONFIG_H8S_SIM=y +CONFIG_H8300_BUILTIN_DTB="h8s_sim" +# CONFIG_BINFMT_SCRIPT is not set +CONFIG_BINFMT_FLAT=y +# CONFIG_COREDUMP is not set +# CONFIG_UEVENT_HELPER is not set +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set +# CONFIG_ALLOW_DEV_COREDUMP is not set +# CONFIG_INPUT is not set +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_UNIX98_PTYS is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_SH_SCI=y +CONFIG_SERIAL_SH_SCI_CONSOLE=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_FILE_LOCKING is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_PROC_FS is not set +# CONFIG_SYSFS is not set +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_MUST_CHECK is not set diff --git a/arch/h8300/include/asm/Kbuild b/arch/h8300/include/asm/Kbuild new file mode 100644 index 000000000..60ee7f0d6 --- /dev/null +++ b/arch/h8300/include/asm/Kbuild @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +generic-y += asm-offsets.h +generic-y += extable.h +generic-y += kvm_para.h +generic-y += mcs_spinlock.h +generic-y += parport.h +generic-y += spinlock.h diff --git a/arch/h8300/include/asm/atomic.h b/arch/h8300/include/asm/atomic.h new file mode 100644 index 000000000..a990d151f --- /dev/null +++ b/arch/h8300/include/asm/atomic.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ARCH_H8300_ATOMIC__ +#define __ARCH_H8300_ATOMIC__ + +#include <linux/compiler.h> +#include <linux/types.h> +#include <asm/cmpxchg.h> +#include <asm/irqflags.h> + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + */ + +#define atomic_read(v) READ_ONCE((v)->counter) +#define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i)) + +#define ATOMIC_OP_RETURN(op, c_op) \ +static inline int atomic_##op##_return(int i, atomic_t *v) \ +{ \ + h8300flags flags; \ + int ret; \ + \ + flags = arch_local_irq_save(); \ + ret = v->counter c_op i; \ + arch_local_irq_restore(flags); \ + return ret; \ +} + +#define ATOMIC_FETCH_OP(op, c_op) \ +static inline int atomic_fetch_##op(int i, atomic_t *v) \ +{ \ + h8300flags flags; \ + int ret; \ + \ + flags = arch_local_irq_save(); \ + ret = v->counter; \ + v->counter c_op i; \ + arch_local_irq_restore(flags); \ + return ret; \ +} + +#define ATOMIC_OP(op, c_op) \ +static inline void atomic_##op(int i, atomic_t *v) \ +{ \ + h8300flags flags; \ + \ + flags = arch_local_irq_save(); \ + v->counter c_op i; \ + arch_local_irq_restore(flags); \ +} + +ATOMIC_OP_RETURN(add, +=) +ATOMIC_OP_RETURN(sub, -=) + +#define ATOMIC_OPS(op, c_op) \ + ATOMIC_OP(op, c_op) \ + ATOMIC_FETCH_OP(op, c_op) + +ATOMIC_OPS(and, &=) +ATOMIC_OPS(or, |=) +ATOMIC_OPS(xor, ^=) +ATOMIC_OPS(add, +=) +ATOMIC_OPS(sub, -=) + +#undef ATOMIC_OPS +#undef ATOMIC_OP_RETURN +#undef ATOMIC_OP + +static inline int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + h8300flags flags; + + flags = arch_local_irq_save(); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + arch_local_irq_restore(flags); + return ret; +} + +static inline int atomic_fetch_add_unless(atomic_t *v, int a, int u) +{ + int ret; + h8300flags flags; + + flags = arch_local_irq_save(); + ret = v->counter; + if (ret != u) + v->counter += a; + arch_local_irq_restore(flags); + return ret; +} +#define atomic_fetch_add_unless atomic_fetch_add_unless + +#endif /* __ARCH_H8300_ATOMIC __ */ diff --git a/arch/h8300/include/asm/bitops.h b/arch/h8300/include/asm/bitops.h new file mode 100644 index 000000000..7aa16c732 --- /dev/null +++ b/arch/h8300/include/asm/bitops.h @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_BITOPS_H +#define _H8300_BITOPS_H + +/* + * Copyright 1992, Linus Torvalds. + * Copyright 2002, Yoshinori Sato + */ + +#include <linux/compiler.h> + +#ifdef __KERNEL__ + +#ifndef _LINUX_BITOPS_H +#error only <linux/bitops.h> can be included directly +#endif + +/* + * Function prototypes to keep gcc -Wall happy + */ + +/* + * ffz = Find First Zero in word. Undefined if no zero exists, + * so code should check against ~0UL first.. + */ +static inline unsigned long ffz(unsigned long word) +{ + unsigned long result; + + result = -1; + __asm__("1:\n\t" + "shlr.l %1\n\t" + "adds #1,%0\n\t" + "bcs 1b" + : "=r"(result),"=r"(word) + : "0"(result), "1"(word)); + return result; +} + +#define H8300_GEN_BITOP(FNAME, OP) \ +static inline void FNAME(int nr, volatile unsigned long *addr) \ +{ \ + unsigned char *b_addr; \ + unsigned char bit = nr & 7; \ + \ + b_addr = (unsigned char *)addr + ((nr >> 3) ^ 3); \ + if (__builtin_constant_p(nr)) { \ + __asm__(OP " %1,%0" : "+WU"(*b_addr) : "i"(nr & 7)); \ + } else { \ + __asm__(OP " %s1,%0" : "+WU"(*b_addr) : "r"(bit)); \ + } \ +} + +H8300_GEN_BITOP(set_bit, "bset") +H8300_GEN_BITOP(clear_bit, "bclr") +H8300_GEN_BITOP(change_bit, "bnot") +#define __set_bit(nr, addr) set_bit((nr), (addr)) +#define __clear_bit(nr, addr) clear_bit((nr), (addr)) +#define __change_bit(nr, addr) change_bit((nr), (addr)) + +#undef H8300_GEN_BITOP + +static inline int test_bit(int nr, const volatile unsigned long *addr) +{ + int ret = 0; + unsigned char *b_addr; + unsigned char bit = nr & 7; + + b_addr = (unsigned char *)addr + ((nr >> 3) ^ 3); + if (__builtin_constant_p(nr)) { + __asm__("bld %Z2,%1\n\t" + "rotxl %0\n\t" + : "=r"(ret) + : "WU"(*b_addr), "i"(nr & 7), "0"(ret) : "cc"); + } else { + __asm__("btst %w2,%1\n\t" + "beq 1f\n\t" + "inc.l #1,%0\n" + "1:" + : "=r"(ret) + : "WU"(*b_addr), "r"(bit), "0"(ret) : "cc"); + } + return ret; +} + +#define __test_bit(nr, addr) test_bit(nr, addr) + +#define H8300_GEN_TEST_BITOP(FNNAME, OP) \ +static inline int FNNAME(int nr, void *addr) \ +{ \ + int retval = 0; \ + char ccrsave; \ + unsigned char *b_addr; \ + unsigned char bit = nr & 7; \ + \ + b_addr = (unsigned char *)addr + ((nr >> 3) ^ 3); \ + if (__builtin_constant_p(nr)) { \ + __asm__("stc ccr,%s2\n\t" \ + "orc #0x80,ccr\n\t" \ + "bld %4,%1\n\t" \ + OP " %4,%1\n\t" \ + "rotxl.l %0\n\t" \ + "ldc %s2,ccr" \ + : "=r"(retval), "+WU" (*b_addr), "=&r"(ccrsave) \ + : "0"(retval), "i"(nr & 7) : "cc"); \ + } else { \ + __asm__("stc ccr,%t3\n\t" \ + "orc #0x80,ccr\n\t" \ + "btst %s3,%1\n\t" \ + OP " %s3,%1\n\t" \ + "beq 1f\n\t" \ + "inc.l #1,%0\n\t" \ + "1:\n" \ + "ldc %t3,ccr" \ + : "=r"(retval), "+WU" (*b_addr) \ + : "0" (retval), "r"(bit) : "cc"); \ + } \ + return retval; \ +} \ + \ +static inline int __ ## FNNAME(int nr, void *addr) \ +{ \ + int retval = 0; \ + unsigned char *b_addr; \ + unsigned char bit = nr & 7; \ + \ + b_addr = (unsigned char *)addr + ((nr >> 3) ^ 3); \ + if (__builtin_constant_p(nr)) { \ + __asm__("bld %3,%1\n\t" \ + OP " %3,%1\n\t" \ + "rotxl.l %0\n\t" \ + : "=r"(retval), "+WU"(*b_addr) \ + : "0" (retval), "i"(nr & 7)); \ + } else { \ + __asm__("btst %s3,%1\n\t" \ + OP " %s3,%1\n\t" \ + "beq 1f\n\t" \ + "inc.l #1,%0\n\t" \ + "1:" \ + : "=r"(retval), "+WU"(*b_addr) \ + : "0" (retval), "r"(bit)); \ + } \ + return retval; \ +} + +H8300_GEN_TEST_BITOP(test_and_set_bit, "bset") +H8300_GEN_TEST_BITOP(test_and_clear_bit, "bclr") +H8300_GEN_TEST_BITOP(test_and_change_bit, "bnot") +#undef H8300_GEN_TEST_BITOP + +#include <asm-generic/bitops/ffs.h> + +static inline unsigned long __ffs(unsigned long word) +{ + unsigned long result; + + result = -1; + __asm__("1:\n\t" + "shlr.l %1\n\t" + "adds #1,%0\n\t" + "bcc 1b" + : "=r" (result),"=r"(word) + : "0"(result), "1"(word)); + return result; +} + +#include <asm-generic/bitops/find.h> +#include <asm-generic/bitops/sched.h> +#include <asm-generic/bitops/hweight.h> +#include <asm-generic/bitops/lock.h> +#include <asm-generic/bitops/le.h> +#include <asm-generic/bitops/ext2-atomic.h> + +#endif /* __KERNEL__ */ + +#include <asm-generic/bitops/fls.h> +#include <asm-generic/bitops/__fls.h> +#include <asm-generic/bitops/fls64.h> + +#endif /* _H8300_BITOPS_H */ diff --git a/arch/h8300/include/asm/bug.h b/arch/h8300/include/asm/bug.h new file mode 100644 index 000000000..00fe5e966 --- /dev/null +++ b/arch/h8300/include/asm/bug.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_BUG_H +#define _H8300_BUG_H + +/* always true */ +#define is_valid_bugaddr(addr) (1) + +#include <asm-generic/bug.h> + +struct pt_regs; +extern void die(const char *str, struct pt_regs *fp, unsigned long err); + +#endif diff --git a/arch/h8300/include/asm/byteorder.h b/arch/h8300/include/asm/byteorder.h new file mode 100644 index 000000000..6eaa7ad5f --- /dev/null +++ b/arch/h8300/include/asm/byteorder.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __H8300_BYTEORDER_H__ +#define __H8300_BYTEORDER_H__ + +#include <linux/byteorder/big_endian.h> + +#endif diff --git a/arch/h8300/include/asm/cache.h b/arch/h8300/include/asm/cache.h new file mode 100644 index 000000000..4243eb319 --- /dev/null +++ b/arch/h8300/include/asm/cache.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ARCH_H8300_CACHE_H +#define __ARCH_H8300_CACHE_H + +/* bytes per L1 cache line */ +#define L1_CACHE_SHIFT 2 +#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) + +#define __cacheline_aligned +#define ____cacheline_aligned + +#endif diff --git a/arch/h8300/include/asm/cmpxchg.h b/arch/h8300/include/asm/cmpxchg.h new file mode 100644 index 000000000..c64bb38ce --- /dev/null +++ b/arch/h8300/include/asm/cmpxchg.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ARCH_H8300_CMPXCHG__ +#define __ARCH_H8300_CMPXCHG__ + +#include <linux/irqflags.h> + +#define xchg(ptr, x) \ + ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), \ + sizeof(*(ptr)))) + +struct __xchg_dummy { unsigned long a[100]; }; +#define __xg(x) ((volatile struct __xchg_dummy *)(x)) + +static inline unsigned long __xchg(unsigned long x, + volatile void *ptr, int size) +{ + unsigned long tmp, flags; + + local_irq_save(flags); + + switch (size) { + case 1: + __asm__ __volatile__ + ("mov.b %2,%0\n\t" + "mov.b %1,%2" + : "=&r" (tmp) : "r" (x), "m" (*__xg(ptr))); + break; + case 2: + __asm__ __volatile__ + ("mov.w %2,%0\n\t" + "mov.w %1,%2" + : "=&r" (tmp) : "r" (x), "m" (*__xg(ptr))); + break; + case 4: + __asm__ __volatile__ + ("mov.l %2,%0\n\t" + "mov.l %1,%2" + : "=&r" (tmp) : "r" (x), "m" (*__xg(ptr))); + break; + default: + tmp = 0; + } + local_irq_restore(flags); + return tmp; +} + +#include <asm-generic/cmpxchg-local.h> + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), \ + (unsigned long)(o), \ + (unsigned long)(n), \ + sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) + +#ifndef CONFIG_SMP +#include <asm-generic/cmpxchg.h> +#endif + +#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) + +#endif /* __ARCH_H8300_CMPXCHG__ */ diff --git a/arch/h8300/include/asm/elf.h b/arch/h8300/include/asm/elf.h new file mode 100644 index 000000000..029647cda --- /dev/null +++ b/arch/h8300/include/asm/elf.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_H8300_ELF_H +#define __ASM_H8300_ELF_H + +/* + * ELF register definitions.. + */ + +#include <asm/ptrace.h> +#include <asm/user.h> + +typedef unsigned long elf_greg_t; + +#define ELF_NGREG (sizeof(struct user_regs_struct) / sizeof(elf_greg_t)) +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; +typedef unsigned long elf_fpregset_t; + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(x) ((x)->e_machine == EM_H8_300) + +/* + * These are used to set parameters in the core dumps. + */ +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_H8_300 +#if defined(CONFIG_CPU_H8300H) +#define ELF_CORE_EFLAGS 0x810000 +#endif +#if defined(CONFIG_CPU_H8S) +#define ELF_CORE_EFLAGS 0x820000 +#endif + +#define ELF_PLAT_INIT(_r) do { (_r)->er1 = 0; } while (0) + +#define ELF_EXEC_PAGESIZE 4096 + +/* This is the location that an ET_DYN program is loaded if exec'ed. Typical + use of this is to invoke "./ld.so someprog" to test out a new version of + the loader. We need to make sure that it is out of the way of the program + that it will "exec", and that there is sufficient room for the brk. */ + +#define ELF_ET_DYN_BASE 0xD0000000UL + +/* This yields a mask that user programs can use to figure out what + instruction set this cpu supports. */ + +#define ELF_HWCAP (0) + +/* This yields a string that ld.so will use to load implementation + specific libraries for optimization. This is more specific in + intent than poking at uname or /proc/cpuinfo. */ + +#define ELF_PLATFORM (NULL) + +#define R_H8_NONE 0 +#define R_H8_DIR32 1 +#define R_H8_DIR32_28 2 +#define R_H8_DIR32_24 3 +#define R_H8_DIR32_16 4 +#define R_H8_DIR32U 6 +#define R_H8_DIR32U_28 7 +#define R_H8_DIR32U_24 8 +#define R_H8_DIR32U_20 9 +#define R_H8_DIR32U_16 10 +#define R_H8_DIR24 11 +#define R_H8_DIR24_20 12 +#define R_H8_DIR24_16 13 +#define R_H8_DIR24U 14 +#define R_H8_DIR24U_20 15 +#define R_H8_DIR24U_16 16 +#define R_H8_DIR16 17 +#define R_H8_DIR16U 18 +#define R_H8_DIR16S_32 19 +#define R_H8_DIR16S_28 20 +#define R_H8_DIR16S_24 21 +#define R_H8_DIR16S_20 22 +#define R_H8_DIR16S 23 +#define R_H8_DIR8 24 +#define R_H8_DIR8U 25 +#define R_H8_DIR8Z_32 26 +#define R_H8_DIR8Z_28 27 +#define R_H8_DIR8Z_24 28 +#define R_H8_DIR8Z_20 29 +#define R_H8_DIR8Z_16 30 +#define R_H8_PCREL16 31 +#define R_H8_PCREL8 32 +#define R_H8_BPOS 33 +#define R_H8_PCREL32 34 +#define R_H8_GOT32O 35 +#define R_H8_GOT16O 36 +#define R_H8_DIR16A8 59 +#define R_H8_DIR16R8 60 +#define R_H8_DIR24A8 61 +#define R_H8_DIR24R8 62 +#define R_H8_DIR32A16 63 +#define R_H8_ABS32 65 +#define R_H8_ABS32A16 127 + +#endif diff --git a/arch/h8300/include/asm/flat.h b/arch/h8300/include/asm/flat.h new file mode 100644 index 000000000..78070f924 --- /dev/null +++ b/arch/h8300/include/asm/flat.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * arch/h8300/asm/include/flat.h -- uClinux flat-format executables + */ + +#ifndef __H8300_FLAT_H__ +#define __H8300_FLAT_H__ + +#include <asm/unaligned.h> + +/* + * on the H8 a couple of the relocations have an instruction in the + * top byte. As there can only be 24bits of address space, we just + * always preserve that 8bits at the top, when it isn't an instruction + * is is 0 (davidm@snapgear.com) + */ + +#define flat_get_relocate_addr(rel) (rel & ~0x00000001) +static inline int flat_get_addr_from_rp(u32 __user *rp, u32 relval, u32 flags, + u32 *addr) +{ + u32 val = get_unaligned((__force u32 *)rp); + if (!(flags & FLAT_FLAG_GOTPIC)) + val &= 0x00ffffff; + *addr = val; + return 0; +} + +static inline int flat_put_addr_at_rp(u32 __user *rp, u32 addr, u32 rel) +{ + u32 *p = (__force u32 *)rp; + put_unaligned((addr & 0x00ffffff) | (*(char *)p << 24), p); + return 0; +} + +#endif /* __H8300_FLAT_H__ */ diff --git a/arch/h8300/include/asm/hash.h b/arch/h8300/include/asm/hash.h new file mode 100644 index 000000000..2960b4c9e --- /dev/null +++ b/arch/h8300/include/asm/hash.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_HASH_H +#define _ASM_HASH_H + +/* + * The later H8SX models have a 32x32-bit multiply, but the H8/300H + * and H8S have only 16x16->32. Since it's tolerably compact, this is + * basically an inlined version of the __mulsi3 code. Since the inputs + * are not expected to be small, it's also simplfied by skipping the + * early-out checks. + * + * (Since neither CPU has any multi-bit shift instructions, a + * shift-and-add version is a non-starter.) + * + * TODO: come up with an arch-specific version of the hashing in fs/namei.c, + * since that is heavily dependent on rotates. Which, as mentioned, suck + * horribly on H8. + */ + +#if defined(CONFIG_CPU_H300H) || defined(CONFIG_CPU_H8S) + +#define HAVE_ARCH__HASH_32 1 + +/* + * Multiply by k = 0x61C88647. Fitting this into three registers requires + * one extra instruction, but reducing register pressure will probably + * make that back and then some. + * + * GCC asm note: %e1 is the high half of operand %1, while %f1 is the + * low half. So if %1 is er4, then %e1 is e4 and %f1 is r4. + * + * This has been designed to modify x in place, since that's the most + * common usage, but preserve k, since hash_64() makes two calls in + * quick succession. + */ +static inline u32 __attribute_const__ __hash_32(u32 x) +{ + u32 temp; + + asm( "mov.w %e1,%f0" + "\n mulxu.w %f2,%0" /* klow * xhigh */ + "\n mov.w %f0,%e1" /* The extra instruction */ + "\n mov.w %f1,%f0" + "\n mulxu.w %e2,%0" /* khigh * xlow */ + "\n add.w %e1,%f0" + "\n mulxu.w %f2,%1" /* klow * xlow */ + "\n add.w %f0,%e1" + : "=&r" (temp), "=r" (x) + : "%r" (GOLDEN_RATIO_32), "1" (x)); + return x; +} + +#endif +#endif /* _ASM_HASH_H */ diff --git a/arch/h8300/include/asm/io.h b/arch/h8300/include/asm/io.h new file mode 100644 index 000000000..096d99580 --- /dev/null +++ b/arch/h8300/include/asm/io.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_IO_H +#define _H8300_IO_H + +#ifdef __KERNEL__ + +#include <linux/types.h> + +/* H8/300 internal I/O functions */ + +#define __raw_readb __raw_readb +static inline u8 __raw_readb(const volatile void __iomem *addr) +{ + return *(volatile u8 *)addr; +} + +#define __raw_readw __raw_readw +static inline u16 __raw_readw(const volatile void __iomem *addr) +{ + return *(volatile u16 *)addr; +} + +#define __raw_readl __raw_readl +static inline u32 __raw_readl(const volatile void __iomem *addr) +{ + return *(volatile u32 *)addr; +} + +#define __raw_writeb __raw_writeb +static inline void __raw_writeb(u8 b, const volatile void __iomem *addr) +{ + *(volatile u8 *)addr = b; +} + +#define __raw_writew __raw_writew +static inline void __raw_writew(u16 b, const volatile void __iomem *addr) +{ + *(volatile u16 *)addr = b; +} + +#define __raw_writel __raw_writel +static inline void __raw_writel(u32 b, const volatile void __iomem *addr) +{ + *(volatile u32 *)addr = b; +} + +static inline void ctrl_bclr(int b, void __iomem *addr) +{ + if (__builtin_constant_p(b)) + __asm__("bclr %1,%0" : "+WU"(*(u8 *)addr): "i"(b)); + else + __asm__("bclr %w1,%0" : "+WU"(*(u8 *)addr): "r"(b)); +} + +static inline void ctrl_bset(int b, void __iomem *addr) +{ + if (__builtin_constant_p(b)) + __asm__("bset %1,%0" : "+WU"(*(u8 *)addr): "i"(b)); + else + __asm__("bset %w1,%0" : "+WU"(*(u8 *)addr): "r"(b)); +} + +#include <asm-generic/io.h> + +#endif /* __KERNEL__ */ + +#endif /* _H8300_IO_H */ diff --git a/arch/h8300/include/asm/irq.h b/arch/h8300/include/asm/irq.h new file mode 100644 index 000000000..5fc5b436d --- /dev/null +++ b/arch/h8300/include/asm/irq.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_IRQ_H_ +#define _H8300_IRQ_H_ + +#include <linux/irqchip.h> + +#if defined(CONFIG_CPU_H8300H) +#define NR_IRQS 64 +#define IRQ_CHIP h8300h_irq_chip +#define EXT_IRQ0 12 +#define EXT_IRQS 6 +#elif defined(CONFIG_CPU_H8S) +#define NR_IRQS 128 +#define IRQ_CHIP h8s_irq_chip +#define EXT_IRQ0 16 +#define EXT_IRQS 16 +#endif + +static inline int irq_canonicalize(int irq) +{ + return irq; +} + +void h8300_init_ipr(void); +extern struct irq_chip h8300h_irq_chip; +extern struct irq_chip h8s_irq_chip; +#endif /* _H8300_IRQ_H_ */ diff --git a/arch/h8300/include/asm/irqflags.h b/arch/h8300/include/asm/irqflags.h new file mode 100644 index 000000000..48756b7f4 --- /dev/null +++ b/arch/h8300/include/asm/irqflags.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_IRQFLAGS_H +#define _H8300_IRQFLAGS_H + +#ifdef CONFIG_CPU_H8300H +typedef unsigned char h8300flags; + +static inline h8300flags arch_local_save_flags(void) +{ + h8300flags flags; + + __asm__ volatile ("stc ccr,%w0" : "=r" (flags)); + return flags; +} + +static inline void arch_local_irq_disable(void) +{ + __asm__ volatile ("orc #0xc0,ccr"); +} + +static inline void arch_local_irq_enable(void) +{ + __asm__ volatile ("andc #0x3f,ccr"); +} + +static inline h8300flags arch_local_irq_save(void) +{ + h8300flags flags; + + __asm__ volatile ("stc ccr,%w0\n\t" + "orc #0xc0,ccr" : "=r" (flags)); + return flags; +} + +static inline void arch_local_irq_restore(h8300flags flags) +{ + __asm__ volatile ("ldc %w0,ccr" : : "r" (flags) : "cc"); +} + +static inline int arch_irqs_disabled_flags(unsigned long flags) +{ + return (flags & 0xc0) == 0xc0; +} +#endif +#ifdef CONFIG_CPU_H8S +typedef unsigned short h8300flags; + +static inline h8300flags arch_local_save_flags(void) +{ + h8300flags flags; + + __asm__ volatile ("stc ccr,%w0\n\tstc exr,%x0" : "=r" (flags)); + return flags; +} + +static inline void arch_local_irq_disable(void) +{ + __asm__ volatile ("orc #0x80,ccr\n\t"); +} + +static inline void arch_local_irq_enable(void) +{ + __asm__ volatile ("andc #0x7f,ccr\n\t" + "andc #0xf0,exr\n\t"); +} + +static inline h8300flags arch_local_irq_save(void) +{ + h8300flags flags; + + __asm__ volatile ("stc ccr,%w0\n\t" + "stc exr,%x0\n\t" + "orc #0x80,ccr\n\t" + : "=r" (flags)); + return flags; +} + +static inline void arch_local_irq_restore(h8300flags flags) +{ + __asm__ volatile ("ldc %w0,ccr\n\t" + "ldc %x0,exr" + : : "r" (flags) : "cc"); +} + +static inline int arch_irqs_disabled_flags(h8300flags flags) +{ + return (flags & 0x0080) == 0x0080; +} + +#endif + +static inline int arch_irqs_disabled(void) +{ + return arch_irqs_disabled_flags(arch_local_save_flags()); +} + +#endif /* _H8300_IRQFLAGS_H */ diff --git a/arch/h8300/include/asm/kgdb.h b/arch/h8300/include/asm/kgdb.h new file mode 100644 index 000000000..726ff8fdf --- /dev/null +++ b/arch/h8300/include/asm/kgdb.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2015 Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#ifndef _ASM_H8300_KGDB_H +#define _ASM_H8300_KGDB_H + +#define CACHE_FLUSH_IS_SAFE 1 +#define BUFMAX 2048 + +enum regnames { + GDB_ER0, GDB_ER1, GDB_ER2, GDB_ER3, + GDB_ER4, GDB_ER5, GDB_ER6, GDB_SP, + GDB_CCR, GDB_PC, + GDB_CYCLLE, +#if defined(CONFIG_CPU_H8S) + GDB_EXR, +#endif + GDB_TICK, GDB_INST, +#if defined(CONFIG_CPU_H8S) + GDB_MACH, GDB_MACL, +#endif + /* do not change the last entry or anything below! */ + GDB_NUMREGBYTES, /* number of registers */ +}; + +#define GDB_SIZEOF_REG sizeof(u32) +#if defined(CONFIG_CPU_H8300H) +#define DBG_MAX_REG_NUM (13) +#elif defined(CONFIG_CPU_H8S) +#define DBG_MAX_REG_NUM (14) +#endif +#define NUMREGBYTES (DBG_MAX_REG_NUM * GDB_SIZEOF_REG) + +#define BREAK_INSTR_SIZE 2 +static inline void arch_kgdb_breakpoint(void) +{ + __asm__ __volatile__("trapa #2"); +} + +#endif /* _ASM_H8300_KGDB_H */ diff --git a/arch/h8300/include/asm/page.h b/arch/h8300/include/asm/page.h new file mode 100644 index 000000000..53e037544 --- /dev/null +++ b/arch/h8300/include/asm/page.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_PAGE_H +#define _H8300_PAGE_H + +#include <asm-generic/page.h> +#include <linux/types.h> + +#define MAP_NR(addr) (((uintptr_t)(addr)-PAGE_OFFSET) >> PAGE_SHIFT) + +#ifndef __ASSEMBLY__ +extern unsigned long rom_length; +extern unsigned long memory_start; +extern unsigned long memory_end; +extern unsigned long _ramend; +#endif + +#endif diff --git a/arch/h8300/include/asm/page_offset.h b/arch/h8300/include/asm/page_offset.h new file mode 100644 index 000000000..888576d7c --- /dev/null +++ b/arch/h8300/include/asm/page_offset.h @@ -0,0 +1,2 @@ + +#define PAGE_OFFSET_RAW 0x00000000 diff --git a/arch/h8300/include/asm/pgtable.h b/arch/h8300/include/asm/pgtable.h new file mode 100644 index 000000000..ea833a5d8 --- /dev/null +++ b/arch/h8300/include/asm/pgtable.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_PGTABLE_H +#define _H8300_PGTABLE_H +#include <asm-generic/pgtable-nopud.h> +extern void paging_init(void); +#define PAGE_NONE __pgprot(0) /* these mean nothing to NO_MM */ +#define PAGE_SHARED __pgprot(0) /* these mean nothing to NO_MM */ +#define PAGE_COPY __pgprot(0) /* these mean nothing to NO_MM */ +#define PAGE_READONLY __pgprot(0) /* these mean nothing to NO_MM */ +#define PAGE_KERNEL __pgprot(0) /* these mean nothing to NO_MM */ +#define __swp_type(x) (0) +#define __swp_offset(x) (0) +#define __swp_entry(typ, off) ((swp_entry_t) { ((typ) | ((off) << 7)) }) +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) +#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) +#define kern_addr_valid(addr) (1) +#define pgprot_writecombine(prot) (prot) +#define pgprot_noncached pgprot_writecombine + +static inline int pte_file(pte_t pte) { return 0; } +#define swapper_pg_dir ((pgd_t *) 0) +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +#define ZERO_PAGE(vaddr) (virt_to_page(0)) + +/* + * These would be in other places but having them here reduces the diffs. + */ +extern unsigned int kobjsize(const void *objp); +extern int is_in_rom(unsigned long); + +/* + * All 32bit addresses are effectively valid for vmalloc... + * Sort of meaningless for non-VM targets. + */ +#define VMALLOC_START 0 +#define VMALLOC_END 0xffffffff + +#define arch_enter_lazy_cpu_mode() do {} while (0) + +#endif /* _H8300_PGTABLE_H */ diff --git a/arch/h8300/include/asm/processor.h b/arch/h8300/include/asm/processor.h new file mode 100644 index 000000000..a060b41b2 --- /dev/null +++ b/arch/h8300/include/asm/processor.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * include/asm-h8300/processor.h + * + * Copyright (C) 2002 Yoshinori Sato + * + * Based on: linux/asm-m68nommu/processor.h + * + * Copyright (C) 1995 Hamish Macdonald + */ + +#ifndef __ASM_H8300_PROCESSOR_H +#define __ASM_H8300_PROCESSOR_H + +#include <linux/compiler.h> +#include <asm/segment.h> +#include <asm/ptrace.h> +#include <asm/current.h> + +static inline unsigned long rdusp(void) +{ + extern unsigned int _sw_usp; + + return _sw_usp; +} + +static inline void wrusp(unsigned long usp) +{ + extern unsigned int _sw_usp; + + _sw_usp = usp; +} + +/* + * User space process size: 3.75GB. This is hardcoded into a few places, + * so don't change it unless you know what you are doing. + */ +#define TASK_SIZE (0xFFFFFFFFUL) + +#ifdef __KERNEL__ +#define STACK_TOP TASK_SIZE +#define STACK_TOP_MAX STACK_TOP +#endif + +/* + * This decides where the kernel will search for a free chunk of vm + * space during mmap's. We won't be using it + */ +#define TASK_UNMAPPED_BASE 0 + +struct thread_struct { + unsigned long ksp; /* kernel stack pointer */ + unsigned long usp; /* user stack pointer */ + unsigned long ccr; /* saved status register */ + unsigned long esp0; /* points to SR of stack frame */ + struct { + unsigned short *addr; + unsigned short inst; + } breakinfo; +}; + +#define INIT_THREAD { \ + .ksp = sizeof(init_stack) + (unsigned long)init_stack, \ + .usp = 0, \ + .ccr = PS_S, \ + .esp0 = 0, \ + .breakinfo = { \ + .addr = (unsigned short *)-1, \ + .inst = 0 \ + } \ +} + +/* + * Do necessary setup to start up a newly executed thread. + * + * pass the data segment into user programs if it exists, + * it can't hurt anything as far as I can tell + */ +#if defined(CONFIG_CPU_H8300H) +#define start_thread(_regs, _pc, _usp) \ +do { \ + (_regs)->pc = (_pc); \ + (_regs)->ccr = 0x00; /* clear all flags */ \ + (_regs)->er5 = current->mm->start_data; /* GOT base */ \ + (_regs)->sp = ((unsigned long)(_usp)) - sizeof(unsigned long) * 3; \ +} while (0) +#endif +#if defined(CONFIG_CPU_H8S) +#define start_thread(_regs, _pc, _usp) \ +do { \ + (_regs)->pc = (_pc); \ + (_regs)->ccr = 0x00; /* clear kernel flag */ \ + (_regs)->exr = 0x78; /* enable all interrupts */ \ + (_regs)->er5 = current->mm->start_data; /* GOT base */ \ + /* 14 = space for retaddr(4), vector(4), er0(4) and exr(2) on stack */ \ + (_regs)->sp = ((unsigned long)(_usp)) - 14; \ +} while (0) +#endif + +/* Forward declaration, a strange C thing */ +struct task_struct; + +/* Free all resources held by a thread. */ +static inline void release_thread(struct task_struct *dead_task) +{ +} + +unsigned long get_wchan(struct task_struct *p); + +#define KSTK_EIP(tsk) \ + ({ \ + unsigned long eip = 0; \ + if ((tsk)->thread.esp0 > PAGE_SIZE && \ + MAP_NR((tsk)->thread.esp0) < max_mapnr) \ + eip = ((struct pt_regs *) (tsk)->thread.esp0)->pc; \ + eip; }) + +#define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->thread.usp) + +#define cpu_relax() barrier() + +#define HARD_RESET_NOW() ({ \ + local_irq_disable(); \ + asm("jmp @@0"); \ +}) + +#endif diff --git a/arch/h8300/include/asm/ptrace.h b/arch/h8300/include/asm/ptrace.h new file mode 100644 index 000000000..66d383848 --- /dev/null +++ b/arch/h8300/include/asm/ptrace.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_PTRACE_H +#define _H8300_PTRACE_H + +#include <uapi/asm/ptrace.h> + +struct task_struct; + +#ifndef __ASSEMBLY__ +#ifndef PS_S +#define PS_S (0x10) +#endif + +#if defined(CONFIG_CPU_H8300H) +#define H8300_REGS_NO 11 +#endif +#if defined(CONFIG_CPU_H8S) +#define H8300_REGS_NO 12 +#endif + +#define arch_has_single_step() (1) + +#define user_mode(regs) (!((regs)->ccr & PS_S)) +#define instruction_pointer(regs) ((regs)->pc) +#define profile_pc(regs) instruction_pointer(regs) +#define user_stack_pointer(regs) ((regs)->sp) +#define current_pt_regs() ((struct pt_regs *) \ + (THREAD_SIZE + (unsigned long)current_thread_info()) - 1) +#define signal_pt_regs() ((struct pt_regs *)current->thread.esp0) +#define current_user_stack_pointer() rdusp() +#define task_pt_regs(task) \ + ((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE) - 1) + +extern long h8300_get_reg(struct task_struct *task, int regno); +extern int h8300_put_reg(struct task_struct *task, int regno, + unsigned long data); + +#endif /* __ASSEMBLY__ */ +#endif /* _H8300_PTRACE_H */ diff --git a/arch/h8300/include/asm/segment.h b/arch/h8300/include/asm/segment.h new file mode 100644 index 000000000..37950725d --- /dev/null +++ b/arch/h8300/include/asm/segment.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_SEGMENT_H +#define _H8300_SEGMENT_H + +/* define constants */ +#define USER_DATA (1) +#ifndef __USER_DS +#define __USER_DS (USER_DATA) +#endif +#define USER_PROGRAM (2) +#define SUPER_DATA (3) +#ifndef __KERNEL_DS +#define __KERNEL_DS (SUPER_DATA) +#endif +#define SUPER_PROGRAM (4) + +#ifndef __ASSEMBLY__ + +typedef struct { + unsigned long seg; +} mm_segment_t; + +#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) +#define USER_DS MAKE_MM_SEG(__USER_DS) +#define KERNEL_DS MAKE_MM_SEG(__KERNEL_DS) + +/* + * Get/set the SFC/DFC registers for MOVES instructions + */ + +static inline mm_segment_t get_fs(void) +{ + return USER_DS; +} + +#define uaccess_kernel() (get_fs().seg == KERNEL_DS.seg) + +#endif /* __ASSEMBLY__ */ + +#endif /* _H8300_SEGMENT_H */ diff --git a/arch/h8300/include/asm/signal.h b/arch/h8300/include/asm/signal.h new file mode 100644 index 000000000..a42f32ca5 --- /dev/null +++ b/arch/h8300/include/asm/signal.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_SIGNAL_H +#define _H8300_SIGNAL_H + +#include <uapi/asm/signal.h> + +/* Most things should be clean enough to redefine this at will, if care + is taken to make libc match. */ + +#define _NSIG 64 +#define _NSIG_BPW 32 +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +typedef unsigned long old_sigset_t; /* at least 32 bits */ + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +#define __ARCH_HAS_SA_RESTORER +#include <asm/sigcontext.h> + +#endif /* _H8300_SIGNAL_H */ diff --git a/arch/h8300/include/asm/smp.h b/arch/h8300/include/asm/smp.h new file mode 100644 index 000000000..9e9bd7e58 --- /dev/null +++ b/arch/h8300/include/asm/smp.h @@ -0,0 +1 @@ +/* nothing required here yet */ diff --git a/arch/h8300/include/asm/string.h b/arch/h8300/include/asm/string.h new file mode 100644 index 000000000..78e45bb2f --- /dev/null +++ b/arch/h8300/include/asm/string.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_STRING_H_ +#define _H8300_STRING_H_ + +#ifdef __KERNEL__ /* only set these up for kernel code */ + +#include <asm/setup.h> +#include <asm/page.h> + +#define __HAVE_ARCH_MEMSET +extern void *memset(void *s, int c, size_t count); + +#define __HAVE_ARCH_MEMCPY +extern void *memcpy(void *d, const void *s, size_t count); + +#endif /* KERNEL */ + +#endif diff --git a/arch/h8300/include/asm/switch_to.h b/arch/h8300/include/asm/switch_to.h new file mode 100644 index 000000000..2b7e9555b --- /dev/null +++ b/arch/h8300/include/asm/switch_to.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_SWITCH_TO_H +#define _H8300_SWITCH_TO_H + +/* + * switch_to(n) should switch tasks to task ptr, first checking that + * ptr isn't the current task, in which case it does nothing. This + * also clears the TS-flag if the task we switched to has used the + * math co-processor latest. + */ +/* + * switch_to() saves the extra registers, that are not saved + * automatically by SAVE_SWITCH_STACK in resume(), ie. d0-d5 and + * a0-a1. Some of these are used by schedule() and its predecessors + * and so we might get see unexpected behaviors when a task returns + * with unexpected register values. + * + * syscall stores these registers itself and none of them are used + * by syscall after the function in the syscall has been called. + * + * Beware that resume now expects *next to be in d1 and the offset of + * tss to be in a1. This saves a few instructions as we no longer have + * to push them onto the stack and read them back right after. + * + * 02/17/96 - Jes Sorensen (jds@kom.auc.dk) + * + * Changed 96/09/19 by Andreas Schwab + * pass prev in a0, next in a1, offset of tss in d1, and whether + * the mm structures are shared in d2 (to avoid atc flushing). + * + * H8/300 Porting 2002/09/04 Yoshinori Sato + */ + +asmlinkage void resume(void); +#define switch_to(prev, next, last) \ +do { \ + void *_last; \ + __asm__ __volatile__( \ + "mov.l %1, er0\n\t" \ + "mov.l %2, er1\n\t" \ + "mov.l %3, er2\n\t" \ + "jsr @_resume\n\t" \ + "mov.l er2,%0\n\t" \ + : "=r" (_last) \ + : "r" (&(prev->thread)), \ + "r" (&(next->thread)), \ + "g" (prev) \ + : "cc", "er0", "er1", "er2", "er3"); \ + (last) = _last; \ +} while (0) + +#endif /* _H8300_SWITCH_TO_H */ diff --git a/arch/h8300/include/asm/syscall.h b/arch/h8300/include/asm/syscall.h new file mode 100644 index 000000000..01666b8bb --- /dev/null +++ b/arch/h8300/include/asm/syscall.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_H8300_SYSCALLS_32_H +#define __ASM_H8300_SYSCALLS_32_H + +#ifdef __KERNEL__ + +#include <linux/compiler.h> +#include <linux/linkage.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <uapi/linux/audit.h> + +static inline int +syscall_get_nr(struct task_struct *task, struct pt_regs *regs) +{ + return regs->orig_er0; +} + +static inline void +syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, + unsigned long *args) +{ + *args++ = regs->er1; + *args++ = regs->er2; + *args++ = regs->er3; + *args++ = regs->er4; + *args++ = regs->er5; + *args = regs->er6; +} + +static inline int +syscall_get_arch(struct task_struct *task) +{ + return AUDIT_ARCH_H8300; +} + + +/* Misc syscall related bits */ +asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); +asmlinkage void do_syscall_trace_leave(struct pt_regs *regs); + +#endif /* __KERNEL__ */ +#endif /* __ASM_H8300_SYSCALLS_32_H */ diff --git a/arch/h8300/include/asm/thread_info.h b/arch/h8300/include/asm/thread_info.h new file mode 100644 index 000000000..a518214d4 --- /dev/null +++ b/arch/h8300/include/asm/thread_info.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* thread_info.h: h8300 low-level thread information + * adapted from the i386 and PPC versions by Yoshinori Sato <ysato@users.sourceforge.jp> + * + * Copyright (C) 2002 David Howells (dhowells@redhat.com) + * - Incorporating suggestions made by Linus Torvalds and Dave Miller + */ + +#ifndef _ASM_THREAD_INFO_H +#define _ASM_THREAD_INFO_H + +#include <asm/page.h> +#include <asm/segment.h> + +#ifdef __KERNEL__ + +/* + * Size of kernel stack for each process. This must be a power of 2... + */ +#define THREAD_SIZE_ORDER 1 +#define THREAD_SIZE 8192 /* 2 pages */ + +#ifndef __ASSEMBLY__ + +/* + * low level task data. + * If you change this, change the TI_* offsets below to match. + */ +struct thread_info { + struct task_struct *task; /* main task structure */ + unsigned long flags; /* low level flags */ + int cpu; /* cpu we're on */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ + mm_segment_t addr_limit; +}; + +/* + * macros/functions for gaining access to the thread information structure + */ +#define INIT_THREAD_INFO(tsk) \ +{ \ + .task = &tsk, \ + .flags = 0, \ + .cpu = 0, \ + .preempt_count = INIT_PREEMPT_COUNT, \ + .addr_limit = KERNEL_DS, \ +} + +/* how to get the thread information struct from C */ +static inline struct thread_info *current_thread_info(void) +{ + struct thread_info *ti; + + __asm__("mov.l sp, %0\n\t" + "and.w %1, %T0" + : "=&r"(ti) + : "i" (~(THREAD_SIZE-1) & 0xffff)); + return ti; +} + +#endif /* __ASSEMBLY__ */ + +/* + * thread information flag bit numbers + */ +#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ +#define TIF_SIGPENDING 1 /* signal pending */ +#define TIF_NEED_RESCHED 2 /* rescheduling necessary */ +#define TIF_SINGLESTEP 3 /* singlestepping active */ +#define TIF_MEMDIE 4 /* is terminating due to OOM killer */ +#define TIF_RESTORE_SIGMASK 5 /* restore signal mask in do_signal() */ +#define TIF_NOTIFY_RESUME 6 /* callback before returning to user */ +#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ +#define TIF_SYSCALL_TRACEPOINT 8 /* for ftrace syscall instrumentation */ +#define TIF_POLLING_NRFLAG 9 /* true if poll_idle() is polling TIF_NEED_RESCHED */ +#define TIF_NOTIFY_SIGNAL 10 /* signal notifications exist */ + +/* as above, but as bit values */ +#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) +#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) +#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) +#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) +#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP) +#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) +#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) +#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) +#define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) + +/* work to do in syscall trace */ +#define _TIF_WORK_SYSCALL_MASK (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP | \ + _TIF_SYSCALL_AUDIT | _TIF_SYSCALL_TRACEPOINT) + +/* work to do on any return to u-space */ +#define _TIF_ALLWORK_MASK (_TIF_SYSCALL_TRACE | _TIF_SIGPENDING | \ + _TIF_NEED_RESCHED | _TIF_SYSCALL_AUDIT | \ + _TIF_SINGLESTEP | _TIF_NOTIFY_RESUME | \ + _TIF_SYSCALL_TRACEPOINT | _TIF_NOTIFY_SIGNAL) + +/* work to do on interrupt/exception return */ +#define _TIF_WORK_MASK (_TIF_ALLWORK_MASK & ~(_TIF_SYSCALL_TRACE | \ + _TIF_SYSCALL_AUDIT | _TIF_SINGLESTEP)) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_THREAD_INFO_H */ diff --git a/arch/h8300/include/asm/tlb.h b/arch/h8300/include/asm/tlb.h new file mode 100644 index 000000000..d8201ca31 --- /dev/null +++ b/arch/h8300/include/asm/tlb.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __H8300_TLB_H__ +#define __H8300_TLB_H__ + +#include <asm-generic/tlb.h> + +#endif diff --git a/arch/h8300/include/asm/traps.h b/arch/h8300/include/asm/traps.h new file mode 100644 index 000000000..1c5a30ec2 --- /dev/null +++ b/arch/h8300/include/asm/traps.h @@ -0,0 +1,41 @@ +/* + * linux/include/asm-h8300/traps.h + * + * Copyright (C) 2003 Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#ifndef _H8300_TRAPS_H +#define _H8300_TRAPS_H + +extern void _system_call(void); +extern void _interrupt_entry(void); +extern void _trace_break(void); +extern void _nmi(void); +extern void _interrupt_entry(void); + +extern unsigned long *_interrupt_redirect_table; + +#define JMP_OP 0x5a000000 +#define JSR_OP 0x5e000000 +#define VECTOR(address) ((JMP_OP)|((unsigned long)address)) +#define REDIRECT(address) ((JSR_OP)|((unsigned long)address)) +#define CPU_VECTOR ((unsigned long *)0x000000) +#define ADDR_MASK (0xffffff) + +#define TRACE_VEC 5 + +#define TRAP0_VEC 8 +#define TRAP1_VEC 9 +#define TRAP2_VEC 10 +#define TRAP3_VEC 11 + +extern char _start[], _etext[]; +#define check_kernel_text(addr) \ + ((addr >= (unsigned long)(_start)) && \ + (addr < (unsigned long)(_etext)) && !(addr & 1)) + +#endif /* _H8300_TRAPS_H */ diff --git a/arch/h8300/include/asm/user.h b/arch/h8300/include/asm/user.h new file mode 100644 index 000000000..2298909f2 --- /dev/null +++ b/arch/h8300/include/asm/user.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _H8300_USER_H +#define _H8300_USER_H + +#include <asm/page.h> + +/* Core file format: The core file is written in such a way that gdb + can understand it and provide useful information to the user (under + linux we use the 'trad-core' bfd). There are quite a number of + obstacles to being able to view the contents of the floating point + registers, and until these are solved you will not be able to view the + contents of them. Actually, you can read in the core file and look at + the contents of the user struct to find out what the floating point + registers contain. + The actual file contents are as follows: + UPAGE: 1 page consisting of a user struct that tells gdb what is present + in the file. Directly after this is a copy of the task_struct, which + is currently not used by gdb, but it may come in useful at some point. + All of the registers are stored as part of the upage. The upage should + always be only one page. + DATA: The data area is stored. We use current->end_text to + current->brk to pick up all of the user variables, plus any memory + that may have been malloced. No attempt is made to determine if a page + is demand-zero or if a page is totally unused, we just cover the entire + range. All of the addresses are rounded in such a way that an integral + number of pages is written. + STACK: We need the stack information in order to get a meaningful + backtrace. We need to write the data from (esp) to + current->start_stack, so we round each of these off in order to be able + to write an integer number of pages. + The minimum core file size is 3 pages, or 12288 bytes. +*/ + +/* This is the old layout of "struct pt_regs" as of Linux 1.x, and + is still the layout used by user (the new pt_regs doesn't have + all registers). */ +struct user_regs_struct { + long er1, er2, er3, er4, er5, er6; + long er0; + long usp; + long orig_er0; + long ccr; + long pc; +}; + +/* When the kernel dumps core, it starts by dumping the user struct - + this will be used by gdb to figure out where the data and stack segments + are within the file, and what virtual addresses to use. */ +struct user { +/* We start with the registers, to mimic the way that "memory" is returned + from the ptrace(3,...) function. */ + struct user_regs_struct regs; /* Where the registers are actually stored */ +/* ptrace does not yet supply these. Someday.... */ +/* The rest of this junk is to help gdb figure out what goes where */ + unsigned long int u_tsize; /* Text segment size (pages). */ + unsigned long int u_dsize; /* Data segment size (pages). */ + unsigned long int u_ssize; /* Stack segment size (pages). */ + unsigned long start_code; /* Starting virtual address of text. */ + unsigned long start_stack; /* Starting virtual address of stack area. + This is actually the bottom of the stack, + the top of the stack is always found in the + esp register. */ + long int signal; /* Signal that caused the core dump. */ + int reserved; /* No longer used */ + unsigned long u_ar0; /* Used by gdb to help find the values for */ + /* the registers. */ + unsigned long magic; /* To uniquely identify a core file */ + char u_comm[32]; /* User command that was responsible */ +}; +#define NBPG PAGE_SIZE +#define UPAGES 1 +#define HOST_TEXT_START_ADDR (u.start_code) +#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) + +#endif diff --git a/arch/h8300/include/asm/vmalloc.h b/arch/h8300/include/asm/vmalloc.h new file mode 100644 index 000000000..08a55c1df --- /dev/null +++ b/arch/h8300/include/asm/vmalloc.h @@ -0,0 +1,4 @@ +#ifndef _ASM_H8300_VMALLOC_H +#define _ASM_H8300_VMALLOC_H + +#endif /* _ASM_H8300_VMALLOC_H */ diff --git a/arch/h8300/include/uapi/asm/Kbuild b/arch/h8300/include/uapi/asm/Kbuild new file mode 100644 index 000000000..e78470141 --- /dev/null +++ b/arch/h8300/include/uapi/asm/Kbuild @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +generic-y += ucontext.h diff --git a/arch/h8300/include/uapi/asm/byteorder.h b/arch/h8300/include/uapi/asm/byteorder.h new file mode 100644 index 000000000..1fe2f9344 --- /dev/null +++ b/arch/h8300/include/uapi/asm/byteorder.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _H8300_BYTEORDER_H +#define _H8300_BYTEORDER_H + +#include <linux/byteorder/big_endian.h> + +#endif /* _H8300_BYTEORDER_H */ diff --git a/arch/h8300/include/uapi/asm/posix_types.h b/arch/h8300/include/uapi/asm/posix_types.h new file mode 100644 index 000000000..3efc9dd59 --- /dev/null +++ b/arch/h8300/include/uapi/asm/posix_types.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_POSIX_TYPES_H +#define _UAPI_ASM_POSIX_TYPES_H + +/* h8300-unknown-linux required long */ +#define __kernel_size_t __kernel_size_t +typedef unsigned long __kernel_size_t; +typedef long __kernel_ssize_t; +typedef long __kernel_ptrdiff_t; + +#include <asm-generic/posix_types.h> + +#endif /* _UAPI_ASM_POSIX_TYPES_H */ diff --git a/arch/h8300/include/uapi/asm/ptrace.h b/arch/h8300/include/uapi/asm/ptrace.h new file mode 100644 index 000000000..78167f517 --- /dev/null +++ b/arch/h8300/include/uapi/asm/ptrace.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_H8300_PTRACE_H +#define _UAPI_H8300_PTRACE_H + +#ifndef __ASSEMBLY__ + +#define PT_ER1 0 +#define PT_ER2 1 +#define PT_ER3 2 +#define PT_ER4 3 +#define PT_ER5 4 +#define PT_ER6 5 +#define PT_ER0 6 +#define PT_USP 7 +#define PT_ORIG_ER0 8 +#define PT_CCR 9 +#define PT_PC 10 +#define PT_EXR 11 + +/* this struct defines the way the registers are stored on the + stack during a system call. */ + +struct pt_regs { + long retpc; + long er4; + long er5; + long er6; + long er3; + long er2; + long er1; + long orig_er0; + long sp; + unsigned short ccr; + long er0; + long vector; +#if defined(__H8300S__) + unsigned short exr; +#endif + unsigned long pc; +} __attribute__((aligned(2), packed)); + +#endif /* __ASSEMBLY__ */ +#endif /* _UAPI_H8300_PTRACE_H */ diff --git a/arch/h8300/include/uapi/asm/sigcontext.h b/arch/h8300/include/uapi/asm/sigcontext.h new file mode 100644 index 000000000..f77273c85 --- /dev/null +++ b/arch/h8300/include/uapi/asm/sigcontext.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_H8300_SIGCONTEXT_H +#define _ASM_H8300_SIGCONTEXT_H + +struct sigcontext { + unsigned long sc_mask; /* old sigmask */ + unsigned long sc_usp; /* old user stack pointer */ + unsigned long sc_er0; + unsigned long sc_er1; + unsigned long sc_er2; + unsigned long sc_er3; + unsigned long sc_er4; + unsigned long sc_er5; + unsigned long sc_er6; + unsigned short sc_ccr; + unsigned long sc_pc; +}; + +#endif diff --git a/arch/h8300/include/uapi/asm/signal.h b/arch/h8300/include/uapi/asm/signal.h new file mode 100644 index 000000000..e15521037 --- /dev/null +++ b/arch/h8300/include/uapi/asm/signal.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_H8300_SIGNAL_H +#define _UAPI_H8300_SIGNAL_H + +#include <linux/types.h> + +/* Avoid too many header ordering problems. */ +struct siginfo; + +#ifndef __KERNEL__ +/* Here we must cater to libcs that poke about in kernel headers. */ + +#define NSIG 32 +typedef unsigned long sigset_t; + +#endif /* __KERNEL__ */ + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +/* +#define SIGLOST 29 +*/ +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +/* These should not be considered constants from userland. */ +#define SIGRTMIN 32 +#define SIGRTMAX _NSIG + +/* + * SA_FLAGS values: + * + * SA_ONSTACK indicates that a registered stack_t will be used. + * SA_RESTART flag to get restarting signals (which were the default long ago) + * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. + * SA_RESETHAND clears the handler when the signal is delivered. + * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. + * SA_NODEFER prevents the current signal from being masked in the handler. + * + * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single + * Unix names RESETHAND and NODEFER respectively. + */ +#define SA_NOCLDSTOP 0x00000001 +#define SA_NOCLDWAIT 0x00000002 /* not supported yet */ +#define SA_SIGINFO 0x00000004 +#define SA_ONSTACK 0x08000000 +#define SA_RESTART 0x10000000 +#define SA_NODEFER 0x40000000 +#define SA_RESETHAND 0x80000000 + +#define SA_NOMASK SA_NODEFER +#define SA_ONESHOT SA_RESETHAND + +#define SA_RESTORER 0x04000000 + +#define MINSIGSTKSZ 2048 +#define SIGSTKSZ 8192 + +#include <asm-generic/signal-defs.h> + +#ifndef __KERNEL__ +/* Here we must cater to libcs that poke about in kernel headers. */ + +struct sigaction { + union { + __sighandler_t _sa_handler; + void (*_sa_sigaction)(int, struct siginfo *, void *); + } _u; + sigset_t sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +}; + +#define sa_handler _u._sa_handler +#define sa_sigaction _u._sa_sigaction + +#endif /* __KERNEL__ */ + +typedef struct sigaltstack { + void *ss_sp; + int ss_flags; + size_t ss_size; +} stack_t; + + +#endif /* _UAPI_H8300_SIGNAL_H */ diff --git a/arch/h8300/include/uapi/asm/unistd.h b/arch/h8300/include/uapi/asm/unistd.h new file mode 100644 index 000000000..eb7bc0012 --- /dev/null +++ b/arch/h8300/include/uapi/asm/unistd.h @@ -0,0 +1,8 @@ +#define __ARCH_NOMMU + +#define __ARCH_WANT_RENAMEAT +#define __ARCH_WANT_STAT64 +#define __ARCH_WANT_SET_GET_RLIMIT +#define __ARCH_WANT_TIME32_SYSCALLS + +#include <asm-generic/unistd.h> diff --git a/arch/h8300/kernel/.gitignore b/arch/h8300/kernel/.gitignore new file mode 100644 index 000000000..bbb90f92d --- /dev/null +++ b/arch/h8300/kernel/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +vmlinux.lds diff --git a/arch/h8300/kernel/Makefile b/arch/h8300/kernel/Makefile new file mode 100644 index 000000000..307aa5157 --- /dev/null +++ b/arch/h8300/kernel/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the linux kernel. +# + +extra-y := vmlinux.lds + +obj-y := process.o traps.o ptrace.o \ + signal.o setup.o syscalls.o \ + irq.o entry.o + +obj-$(CONFIG_ROMKERNEL) += head_rom.o +obj-$(CONFIG_RAMKERNEL) += head_ram.o + +obj-$(CONFIG_MODULES) += module.o h8300_ksyms.o +obj-$(CONFIG_H8300H_SIM) += sim-console.o +obj-$(CONFIG_H8S_SIM) += sim-console.o + +obj-$(CONFIG_CPU_H8300H) += ptrace_h.o +obj-$(CONFIG_CPU_H8S) += ptrace_s.o + +obj-$(CONFIG_KGDB) += kgdb.o diff --git a/arch/h8300/kernel/asm-offsets.c b/arch/h8300/kernel/asm-offsets.c new file mode 100644 index 000000000..d4b53af65 --- /dev/null +++ b/arch/h8300/kernel/asm-offsets.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This program is used to generate definitions needed by + * assembly language modules. + * + * We use the technique used in the OSF Mach kernel code: + * generate asm statements containing #defines, + * compile this file to assembler, and then extract the + * #defines from the assembly-language output. + */ + +#include <linux/stddef.h> +#include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <linux/ptrace.h> +#include <linux/hardirq.h> +#include <linux/kbuild.h> +#include <asm/irq.h> +#include <asm/ptrace.h> + +int main(void) +{ + /* offsets into the task struct */ + OFFSET(TASK_STATE, task_struct, state); + OFFSET(TASK_FLAGS, task_struct, flags); + OFFSET(TASK_PTRACE, task_struct, ptrace); + OFFSET(TASK_BLOCKED, task_struct, blocked); + OFFSET(TASK_THREAD, task_struct, thread); + OFFSET(TASK_THREAD_INFO, task_struct, stack); + OFFSET(TASK_MM, task_struct, mm); + OFFSET(TASK_ACTIVE_MM, task_struct, active_mm); + + /* offsets into the irq_cpustat_t struct */ + DEFINE(CPUSTAT_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, + __softirq_pending)); + + /* offsets into the thread struct */ + OFFSET(THREAD_KSP, thread_struct, ksp); + OFFSET(THREAD_USP, thread_struct, usp); + OFFSET(THREAD_CCR, thread_struct, ccr); + + /* offsets into the pt_regs struct */ + DEFINE(LER0, offsetof(struct pt_regs, er0) - sizeof(long)); + DEFINE(LER1, offsetof(struct pt_regs, er1) - sizeof(long)); + DEFINE(LER2, offsetof(struct pt_regs, er2) - sizeof(long)); + DEFINE(LER3, offsetof(struct pt_regs, er3) - sizeof(long)); + DEFINE(LER4, offsetof(struct pt_regs, er4) - sizeof(long)); + DEFINE(LER5, offsetof(struct pt_regs, er5) - sizeof(long)); + DEFINE(LER6, offsetof(struct pt_regs, er6) - sizeof(long)); + DEFINE(LORIG, offsetof(struct pt_regs, orig_er0) - sizeof(long)); + DEFINE(LSP, offsetof(struct pt_regs, sp) - sizeof(long)); + DEFINE(LCCR, offsetof(struct pt_regs, ccr) - sizeof(long)); + DEFINE(LVEC, offsetof(struct pt_regs, vector) - sizeof(long)); +#if defined(CONFIG_CPU_H8S) + DEFINE(LEXR, offsetof(struct pt_regs, exr) - sizeof(long)); +#endif + DEFINE(LRET, offsetof(struct pt_regs, pc) - sizeof(long)); + + DEFINE(PT_PTRACED, PT_PTRACED); + + /* offsets in thread_info structure */ + OFFSET(TI_TASK, thread_info, task); + OFFSET(TI_FLAGS, thread_info, flags); + OFFSET(TI_CPU, thread_info, cpu); + OFFSET(TI_PRE, thread_info, preempt_count); +#ifdef CONFIG_PREEMPTION + DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count)); +#endif + + return 0; +} diff --git a/arch/h8300/kernel/entry.S b/arch/h8300/kernel/entry.S new file mode 100644 index 000000000..c6e289b5f --- /dev/null +++ b/arch/h8300/kernel/entry.S @@ -0,0 +1,434 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * + * linux/arch/h8300/kernel/entry.S + * + * Yoshinori Sato <ysato@users.sourceforge.jp> + * David McCullough <davidm@snapgear.com> + * + */ + +/* + * entry.S + * include exception/interrupt gateway + * system call entry + */ + +#include <linux/sys.h> +#include <asm/unistd.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/thread_info.h> +#include <asm/errno.h> + +#if defined(CONFIG_CPU_H8300H) +#define USERRET 8 +INTERRUPTS = 64 + .h8300h + .macro SHLL2 reg + shll.l \reg + shll.l \reg + .endm + .macro SHLR2 reg + shlr.l \reg + shlr.l \reg + .endm + .macro SAVEREGS + mov.l er0,@-sp + mov.l er1,@-sp + mov.l er2,@-sp + mov.l er3,@-sp + .endm + .macro RESTOREREGS + mov.l @sp+,er3 + mov.l @sp+,er2 + .endm + .macro SAVEEXR + .endm + .macro RESTOREEXR + .endm +#endif +#if defined(CONFIG_CPU_H8S) +#define USERRET 10 +#define USEREXR 8 +INTERRUPTS = 128 + .h8300s + .macro SHLL2 reg + shll.l #2,\reg + .endm + .macro SHLR2 reg + shlr.l #2,\reg + .endm + .macro SAVEREGS + stm.l er0-er3,@-sp + .endm + .macro RESTOREREGS + ldm.l @sp+,er2-er3 + .endm + .macro SAVEEXR + mov.w @(USEREXR:16,er0),r1 + mov.w r1,@(LEXR-LER3:16,sp) /* copy EXR */ + .endm + .macro RESTOREEXR + mov.w @(LEXR-LER1:16,sp),r1 /* restore EXR */ + mov.b r1l,r1h + mov.w r1,@(USEREXR:16,er0) + .endm +#endif + + +/* CPU context save/restore macros. */ + + .macro SAVE_ALL + mov.l er0,@-sp + stc ccr,r0l /* check kernel mode */ + btst #4,r0l + bne 5f + + /* user mode */ + mov.l sp,@_sw_usp + mov.l @sp,er0 /* restore saved er0 */ + orc #0x10,ccr /* switch kernel stack */ + mov.l @_sw_ksp,sp + sub.l #(LRET-LORIG),sp /* allocate LORIG - LRET */ + SAVEREGS + mov.l @_sw_usp,er0 + mov.l @(USERRET:16,er0),er1 /* copy the RET addr */ + mov.l er1,@(LRET-LER3:16,sp) + SAVEEXR + + mov.l @(LORIG-LER3:16,sp),er0 + mov.l er0,@(LER0-LER3:16,sp) /* copy ER0 */ + mov.w e1,r1 /* e1 highbyte = ccr */ + and #0xef,r1h /* mask mode? flag */ + bra 6f +5: + /* kernel mode */ + mov.l @sp,er0 /* restore saved er0 */ + subs #2,sp /* set dummy ccr */ + subs #4,sp /* set dummp sp */ + SAVEREGS + mov.w @(LRET-LER3:16,sp),r1 /* copy old ccr */ +6: + mov.b r1h,r1l + mov.b #0,r1h + mov.w r1,@(LCCR-LER3:16,sp) /* set ccr */ + mov.l @_sw_usp,er2 + mov.l er2,@(LSP-LER3:16,sp) /* set usp */ + mov.l er6,@-sp /* syscall arg #6 */ + mov.l er5,@-sp /* syscall arg #5 */ + mov.l er4,@-sp /* syscall arg #4 */ + .endm /* r1 = ccr */ + + .macro RESTORE_ALL + mov.l @sp+,er4 + mov.l @sp+,er5 + mov.l @sp+,er6 + RESTOREREGS + mov.w @(LCCR-LER1:16,sp),r0 /* check kernel mode */ + btst #4,r0l + bne 7f + + orc #0xc0,ccr + mov.l @(LSP-LER1:16,sp),er0 + mov.l @(LER0-LER1:16,sp),er1 /* restore ER0 */ + mov.l er1,@er0 + RESTOREEXR + mov.w @(LCCR-LER1:16,sp),r1 /* restore the RET addr */ + mov.b r1l,r1h + mov.b @(LRET+1-LER1:16,sp),r1l + mov.w r1,e1 + mov.w @(LRET+2-LER1:16,sp),r1 + mov.l er1,@(USERRET:16,er0) + + mov.l @sp+,er1 + add.l #(LRET-LER1),sp /* remove LORIG - LRET */ + mov.l sp,@_sw_ksp + andc #0xef,ccr /* switch to user mode */ + mov.l er0,sp + bra 8f +7: + mov.l @sp+,er1 + add.l #10,sp +8: + mov.l @sp+,er0 + adds #4,sp /* remove the sw created LVEC */ + rte + .endm + +.globl _system_call +.globl ret_from_exception +.globl ret_from_fork +.globl ret_from_kernel_thread +.globl ret_from_interrupt +.globl _interrupt_redirect_table +.globl _sw_ksp,_sw_usp +.globl _resume +.globl _interrupt_entry +.globl _trace_break +.globl _nmi + +#if defined(CONFIG_ROMKERNEL) + .section .int_redirect,"ax" +_interrupt_redirect_table: +#if defined(CONFIG_CPU_H8300H) + .rept 7 + .long 0 + .endr +#endif +#if defined(CONFIG_CPU_H8S) + .rept 5 + .long 0 + .endr + jmp @_trace_break + .long 0 +#endif + + jsr @_interrupt_entry /* NMI */ + jmp @_system_call /* TRAPA #0 (System call) */ + .long 0 +#if defined(CONFIG_KGDB) + jmp @_kgdb_trap +#else + .long 0 +#endif + jmp @_trace_break /* TRAPA #3 (breakpoint) */ + .rept INTERRUPTS-12 + jsr @_interrupt_entry + .endr +#endif +#if defined(CONFIG_RAMKERNEL) +.globl _interrupt_redirect_table + .section .bss +_interrupt_redirect_table: + .space 4 +#endif + + .section .text + .align 2 +_interrupt_entry: + SAVE_ALL +/* r1l is saved ccr */ + mov.l sp,er0 + add.l #LVEC,er0 + btst #4,r1l + bne 1f + /* user LVEC */ + mov.l @_sw_usp,er0 + adds #4,er0 +1: + mov.l @er0,er0 /* LVEC address */ +#if defined(CONFIG_ROMKERNEL) + sub.l #_interrupt_redirect_table,er0 +#endif +#if defined(CONFIG_RAMKERNEL) + mov.l @_interrupt_redirect_table,er1 + sub.l er1,er0 +#endif + SHLR2 er0 + dec.l #1,er0 + mov.l sp,er1 + subs #4,er1 /* adjust ret_pc */ +#if defined(CONFIG_CPU_H8S) + orc #7,exr +#endif + jsr @do_IRQ + jmp @ret_from_interrupt + +_system_call: + subs #4,sp /* dummy LVEC */ + SAVE_ALL + /* er0: syscall nr */ + andc #0xbf,ccr + mov.l er0,er4 + + /* save top of frame */ + mov.l sp,er0 + jsr @set_esp0 + andc #0x3f,ccr + mov.l sp,er2 + and.w #0xe000,r2 + mov.l @(TI_FLAGS:16,er2),er2 + and.w #_TIF_WORK_SYSCALL_MASK,r2 + beq 1f + mov.l sp,er0 + jsr @do_syscall_trace_enter +1: + cmp.l #__NR_syscalls,er4 + bcc badsys + SHLL2 er4 + mov.l #_sys_call_table,er0 + add.l er4,er0 + mov.l @er0,er4 + beq ret_from_exception:16 + mov.l @(LER1:16,sp),er0 + mov.l @(LER2:16,sp),er1 + mov.l @(LER3:16,sp),er2 + jsr @er4 + mov.l er0,@(LER0:16,sp) /* save the return value */ + mov.l sp,er2 + and.w #0xe000,r2 + mov.l @(TI_FLAGS:16,er2),er2 + and.w #_TIF_WORK_SYSCALL_MASK,r2 + beq 2f + mov.l sp,er0 + jsr @do_syscall_trace_leave +2: + orc #0xc0,ccr + bra resume_userspace + +badsys: + mov.l #-ENOSYS,er0 + mov.l er0,@(LER0:16,sp) + bra resume_userspace + +#if !defined(CONFIG_PREEMPTION) +#define resume_kernel restore_all +#endif + +ret_from_exception: +#if defined(CONFIG_PREEMPTION) + orc #0xc0,ccr +#endif +ret_from_interrupt: + mov.b @(LCCR+1:16,sp),r0l + btst #4,r0l + bne resume_kernel:16 /* return from kernel */ +resume_userspace: + andc #0xbf,ccr + mov.l sp,er4 + and.w #0xe000,r4 /* er4 <- current thread info */ + mov.l @(TI_FLAGS:16,er4),er1 + and.l #_TIF_WORK_MASK,er1 + beq restore_all:8 +work_pending: + btst #TIF_NEED_RESCHED,r1l + bne work_resched:8 + /* work notifysig */ + mov.l sp,er0 + subs #4,er0 /* er0: pt_regs */ + jsr @do_notify_resume + bra resume_userspace:8 +work_resched: + mov.l sp,er0 + jsr @set_esp0 + jsr @schedule + bra resume_userspace:8 +restore_all: + RESTORE_ALL /* Does RTE */ + +#if defined(CONFIG_PREEMPTION) +resume_kernel: + mov.l @(TI_PRE_COUNT:16,er4),er0 + bne restore_all:8 +need_resched: + mov.l @(TI_FLAGS:16,er4),er0 + btst #TIF_NEED_RESCHED,r0l + beq restore_all:8 + mov.b @(LCCR+1:16,sp),r0l /* Interrupt Enabled? */ + bmi restore_all:8 + mov.l sp,er0 + jsr @set_esp0 + jsr @preempt_schedule_irq + bra need_resched:8 +#endif + +ret_from_fork: + mov.l er2,er0 + jsr @schedule_tail + jmp @ret_from_exception + +ret_from_kernel_thread: + mov.l er2,er0 + jsr @schedule_tail + mov.l @(LER4:16,sp),er0 + mov.l @(LER5:16,sp),er1 + jsr @er1 + jmp @ret_from_exception + +_resume: + /* + * Beware - when entering resume, offset of tss is in d1, + * prev (the current task) is in a0, next (the new task) + * is in a1 and d2.b is non-zero if the mm structure is + * shared between the tasks, so don't change these + * registers until their contents are no longer needed. + */ + + /* save sr */ + sub.w r3,r3 + stc ccr,r3l + mov.w r3,@(THREAD_CCR+2:16,er0) + + /* disable interrupts */ + orc #0xc0,ccr + mov.l @_sw_usp,er3 + mov.l er3,@(THREAD_USP:16,er0) + mov.l sp,@(THREAD_KSP:16,er0) + + /* Skip address space switching if they are the same. */ + /* FIXME: what did we hack out of here, this does nothing! */ + + mov.l @(THREAD_USP:16,er1),er0 + mov.l er0,@_sw_usp + mov.l @(THREAD_KSP:16,er1),sp + + /* restore status register */ + mov.w @(THREAD_CCR+2:16,er1),r3 + + ldc r3l,ccr + rts + +_trace_break: + subs #4,sp + SAVE_ALL + sub.l er1,er1 + dec.l #1,er1 + mov.l er1,@(LORIG,sp) + mov.l sp,er0 + jsr @set_esp0 + mov.l @_sw_usp,er0 + mov.l @er0,er1 + mov.w @(-2:16,er1),r2 + cmp.w #0x5730,r2 + beq 1f + subs #2,er1 + mov.l er1,@er0 +1: + and.w #0xff,e1 + mov.l er1,er0 + jsr @trace_trap + jmp @ret_from_exception + +_nmi: + subs #4, sp + mov.l er0, @-sp + mov.l @_interrupt_redirect_table, er0 + add.l #8*4, er0 + mov.l er0, @(4,sp) + mov.l @sp+, er0 + jmp @_interrupt_entry + +#if defined(CONFIG_KGDB) +_kgdb_trap: + subs #4,sp + SAVE_ALL + mov.l sp,er0 + add.l #LRET,er0 + mov.l er0,@(LSP,sp) + jsr @set_esp0 + mov.l sp,er0 + subs #4,er0 + jsr @h8300_kgdb_trap + jmp @ret_from_exception +#endif + + .section .bss +_sw_ksp: + .space 4 +_sw_usp: + .space 4 + + .end diff --git a/arch/h8300/kernel/h8300_ksyms.c b/arch/h8300/kernel/h8300_ksyms.c new file mode 100644 index 000000000..1c6f902e8 --- /dev/null +++ b/arch/h8300/kernel/h8300_ksyms.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/module.h> +#include <linux/linkage.h> + +/* + * libgcc functions - functions that are used internally by the + * compiler... (prototypes are not correct though, but that + * doesn't really matter since they're not versioned). + */ +asmlinkage long __ucmpdi2(long long, long long); +asmlinkage long long __ashldi3(long long, int); +asmlinkage long long __ashrdi3(long long, int); +asmlinkage long long __lshrdi3(long long, int); +asmlinkage long __divsi3(long, long); +asmlinkage long __modsi3(long, long); +asmlinkage unsigned long __umodsi3(unsigned long, unsigned long); +asmlinkage long long __muldi3(long long, long long); +asmlinkage long __mulsi3(long, long); +asmlinkage long __udivsi3(long, long); +asmlinkage void *memcpy(void *, const void *, size_t); +asmlinkage void *memset(void *, int, size_t); +asmlinkage long strncpy_from_user(void *to, void *from, size_t n); + + /* gcc lib functions */ +EXPORT_SYMBOL(__ucmpdi2); +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__divsi3); +EXPORT_SYMBOL(__modsi3); +EXPORT_SYMBOL(__umodsi3); +EXPORT_SYMBOL(__muldi3); +EXPORT_SYMBOL(__mulsi3); +EXPORT_SYMBOL(__udivsi3); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(strncpy_from_user); diff --git a/arch/h8300/kernel/head_ram.S b/arch/h8300/kernel/head_ram.S new file mode 100644 index 000000000..dbf8429f5 --- /dev/null +++ b/arch/h8300/kernel/head_ram.S @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include <linux/sys.h> +#include <linux/init.h> +#include <asm/unistd.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/thread_info.h> +#include <asm/errno.h> + +#if defined(CONFIG_CPU_H8300H) + .h8300h +#define SYSCR 0xfee012 +#define IRAMTOP 0xffff20 +#endif +#if defined(CONFIG_CPU_H8S) + .h8300s +#define INTCR 0xffff31 +#define IRAMTOP 0xffc000 +#endif + + __HEAD + .global _start +_start: + mov.l #IRAMTOP,sp + /* .bss clear */ + mov.l #_sbss,er5 + mov.l #_ebss,er4 + sub.l er5,er4 + shlr er4 + shlr er4 + sub.l er2,er2 +1: + mov.l er2,@er5 + adds #4,er5 + dec.l #1,er4 + bne 1b + jsr @h8300_fdt_init + + /* linux kernel start */ +#if defined(CONFIG_CPU_H8300H) + ldc #0xd0,ccr /* running kernel */ + mov.l #SYSCR,er0 + bclr #3,@er0 +#endif +#if defined(CONFIG_CPU_H8S) + ldc #0x07,exr + bclr #4,@INTCR:8 + bset #5,@INTCR:8 /* Interrupt mode 2 */ + ldc #0x90,ccr /* running kernel */ +#endif + mov.l #init_thread_union,sp + add.l #0x2000,sp + jsr @start_kernel + +1: + bra 1b + + .end diff --git a/arch/h8300/kernel/head_rom.S b/arch/h8300/kernel/head_rom.S new file mode 100644 index 000000000..ab55a9cb2 --- /dev/null +++ b/arch/h8300/kernel/head_rom.S @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/init.h> +#include <asm/thread_info.h> + +#if defined(CONFIG_CPU_H8300H) + .h8300h +#define SYSCR 0xfee012 +#define IRAMTOP 0xffff20 +#define NR_INT 64 +#endif +#if defined(CONFIG_CPU_H8S) + .h8300s +#define INTCR 0xffff31 +#define IRAMTOP 0xffc000 +#define NR_INT 128 +#endif + + __HEAD + .global _start +_start: + mov.l #IRAMTOP,sp +#if !defined(CONFIG_H8300H_SIM) && \ + !defined(CONFIG_H8S_SIM) + jsr @lowlevel_init + + /* copy .data */ + mov.l #_begin_data,er5 + mov.l #_sdata,er6 + mov.l #_edata,er4 + sub.l er6,er4 + shlr.l er4 + shlr.l er4 +1: + mov.l @er5+,er0 + mov.l er0,@er6 + adds #4,er6 + dec.l #1,er4 + bne 1b + /* .bss clear */ + mov.l #_sbss,er5 + mov.l #_ebss,er4 + sub.l er5,er4 + shlr er4 + shlr er4 + sub.l er0,er0 +1: + mov.l er0,@er5 + adds #4,er5 + dec.l #1,er4 + bne 1b +#else + /* get cmdline from gdb */ + jsr @0xcc + ;; er0 - argc + ;; er1 - argv + mov.l #command_line,er3 + adds #4,er1 + dec.l #1,er0 + beq 4f +1: + mov.l @er1+,er2 +2: + mov.b @er2+,r4l + beq 3f + mov.b r4l,@er3 + adds #1,er3 + bra 2b +3: + mov.b #' ',r4l + mov.b r4l,@er3 + adds #1,er3 + dec.l #1,er0 + bne 1b + subs #1,er3 + mov.b #0,r4l + mov.b r4l,@er3 +4: +#endif + sub.l er0,er0 + jsr @h8300_fdt_init + /* linux kernel start */ +#if defined(CONFIG_CPU_H8300H) + ldc #0xd0,ccr /* running kernel */ + mov.l #SYSCR,er0 + bclr #3,@er0 +#endif +#if defined(CONFIG_CPU_H8S) + ldc #0x07,exr + bclr #4,@INTCR:8 + bset #5,@INTCR:8 /* Interrupt mode 2 */ + ldc #0x90,ccr /* running kernel */ +#endif + mov.l #init_thread_union,sp + add.l #0x2000,sp + jsr @start_kernel + +1: + bra 1b + +#if defined(CONFIG_ROMKERNEL) + /* interrupt vector */ + .section .vectors,"ax" + .long _start + .long _start +vector = 2 + .rept NR_INT - 2 + .long _interrupt_redirect_table+vector*4 +vector = vector + 1 + .endr +#endif + .end diff --git a/arch/h8300/kernel/irq.c b/arch/h8300/kernel/irq.c new file mode 100644 index 000000000..834e4d7b1 --- /dev/null +++ b/arch/h8300/kernel/irq.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/h8300/kernel/irq.c + * + * Copyright 2014-2015 Yoshinori Sato <ysato@users.sourceforge.jp> + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/of_irq.h> +#include <asm/traps.h> + +#ifdef CONFIG_RAMKERNEL +typedef void (*h8300_vector)(void); + +static const h8300_vector __initconst trap_table[] = { + 0, 0, 0, 0, + _trace_break, + 0, 0, + _nmi, + _system_call, + 0, 0, + _trace_break, +}; + +static unsigned long __init *get_vector_address(void) +{ + unsigned long *rom_vector = CPU_VECTOR; + unsigned long base, tmp; + int vec_no; + + base = rom_vector[EXT_IRQ0] & ADDR_MASK; + + /* check romvector format */ + for (vec_no = EXT_IRQ0 + 1; vec_no <= EXT_IRQ0+EXT_IRQS; vec_no++) { + if ((base+(vec_no - EXT_IRQ0)*4) != + (rom_vector[vec_no] & ADDR_MASK)) + return NULL; + } + + /* ramvector base address */ + base -= EXT_IRQ0*4; + + /* writerble? */ + tmp = ~(*(volatile unsigned long *)base); + (*(volatile unsigned long *)base) = tmp; + if ((*(volatile unsigned long *)base) != tmp) + return NULL; + return (unsigned long *)base; +} + +static void __init setup_vector(void) +{ + int i; + unsigned long *ramvec, *ramvec_p; + const h8300_vector *trap_entry; + + ramvec = get_vector_address(); + if (ramvec == NULL) + panic("interrupt vector serup failed."); + else + pr_debug("virtual vector at 0x%p\n", ramvec); + + /* create redirect table */ + ramvec_p = ramvec; + trap_entry = trap_table; + for (i = 0; i < NR_IRQS; i++) { + if (i < 12) { + if (*trap_entry) + *ramvec_p = VECTOR(*trap_entry); + ramvec_p++; + trap_entry++; + } else + *ramvec_p++ = REDIRECT(_interrupt_entry); + } + _interrupt_redirect_table = ramvec; +} +#else +void setup_vector(void) +{ + /* noting do */ +} +#endif + +void __init init_IRQ(void) +{ + setup_vector(); + irqchip_init(); +} + +asmlinkage void do_IRQ(int irq) +{ + irq_enter(); + generic_handle_irq(irq); + irq_exit(); +} diff --git a/arch/h8300/kernel/kgdb.c b/arch/h8300/kernel/kgdb.c new file mode 100644 index 000000000..602e478af --- /dev/null +++ b/arch/h8300/kernel/kgdb.c @@ -0,0 +1,135 @@ +/* + * H8/300 KGDB support + * + * Copyright (C) 2015 Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/ptrace.h> +#include <linux/kgdb.h> +#include <linux/kdebug.h> +#include <linux/io.h> + +struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { + { "er0", GDB_SIZEOF_REG, offsetof(struct pt_regs, er0) }, + { "er1", GDB_SIZEOF_REG, offsetof(struct pt_regs, er1) }, + { "er2", GDB_SIZEOF_REG, offsetof(struct pt_regs, er2) }, + { "er3", GDB_SIZEOF_REG, offsetof(struct pt_regs, er3) }, + { "er4", GDB_SIZEOF_REG, offsetof(struct pt_regs, er4) }, + { "er5", GDB_SIZEOF_REG, offsetof(struct pt_regs, er5) }, + { "er6", GDB_SIZEOF_REG, offsetof(struct pt_regs, er6) }, + { "sp", GDB_SIZEOF_REG, offsetof(struct pt_regs, sp) }, + { "ccr", GDB_SIZEOF_REG, offsetof(struct pt_regs, ccr) }, + { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, pc) }, + { "cycles", GDB_SIZEOF_REG, -1 }, +#if defined(CONFIG_CPU_H8S) + { "exr", GDB_SIZEOF_REG, offsetof(struct pt_regs, exr) }, +#endif + { "tick", GDB_SIZEOF_REG, -1 }, + { "inst", GDB_SIZEOF_REG, -1 }, +}; + +char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return NULL; + + switch (regno) { + case GDB_CCR: +#if defined(CONFIG_CPU_H8S) + case GDB_EXR: +#endif + *(u32 *)mem = *(u16 *)((void *)regs + + dbg_reg_def[regno].offset); + break; + default: + if (dbg_reg_def[regno].offset >= 0) + memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, + dbg_reg_def[regno].size); + else + memset(mem, 0, dbg_reg_def[regno].size); + break; + } + return dbg_reg_def[regno].name; +} + +int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return -EINVAL; + + switch (regno) { + case GDB_CCR: +#if defined(CONFIG_CPU_H8S) + case GDB_EXR: +#endif + *(u16 *)((void *)regs + + dbg_reg_def[regno].offset) = *(u32 *)mem; + break; + default: + memcpy((void *)regs + dbg_reg_def[regno].offset, mem, + dbg_reg_def[regno].size); + } + return 0; +} + +asmlinkage void h8300_kgdb_trap(struct pt_regs *regs) +{ + regs->pc &= 0x00ffffff; + if (kgdb_handle_exception(10, SIGTRAP, 0, regs)) + return; + if (*(u16 *)(regs->pc) == *(u16 *)&arch_kgdb_ops.gdb_bpt_instr) + regs->pc += BREAK_INSTR_SIZE; + regs->pc |= regs->ccr << 24; +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + memset((char *)gdb_regs, 0, NUMREGBYTES); + gdb_regs[GDB_SP] = p->thread.ksp; + gdb_regs[GDB_PC] = KSTK_EIP(p); +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) +{ + regs->pc = pc; +} + +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + char *ptr; + unsigned long addr; + + switch (remcom_in_buffer[0]) { + case 's': + case 'c': + /* handle the optional parameters */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + regs->pc = addr; + + return 0; + } + + return -1; /* this means that we do not want to exit from the handler */ +} + +int kgdb_arch_init(void) +{ + return 0; +} + +void kgdb_arch_exit(void) +{ + /* Nothing to do */ +} + +const struct kgdb_arch arch_kgdb_ops = { + /* Breakpoint instruction: trapa #2 */ + .gdb_bpt_instr = { 0x57, 0x20 }, +}; diff --git a/arch/h8300/kernel/module.c b/arch/h8300/kernel/module.c new file mode 100644 index 000000000..cd802cc78 --- /dev/null +++ b/arch/h8300/kernel/module.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; + + pr_debug("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { + /* This is where to make the change */ + uint32_t *loc = + (uint32_t *)(sechdrs[sechdrs[relsec].sh_info].sh_addr + + rela[i].r_offset); + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + Elf32_Sym *sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rela[i].r_info); + uint32_t v = sym->st_value + rela[i].r_addend; + + switch (ELF32_R_TYPE(rela[i].r_info)) { + case R_H8_DIR24R8: + loc = (uint32_t *)((uint32_t)loc - 1); + *loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v); + break; + case R_H8_DIR24A8: + if (ELF32_R_SYM(rela[i].r_info)) + *loc += v; + break; + case R_H8_DIR32: + case R_H8_DIR32A16: + *loc += v; + break; + case R_H8_PCREL16: + v -= (unsigned long)loc + 2; + if ((Elf32_Sword)v > 0x7fff || + (Elf32_Sword)v < -(Elf32_Sword)0x8000) + goto overflow; + else + *(unsigned short *)loc = v; + break; + case R_H8_PCREL8: + v -= (unsigned long)loc + 1; + if ((Elf32_Sword)v > 0x7f || + (Elf32_Sword)v < -(Elf32_Sword)0x80) + goto overflow; + else + *(unsigned char *)loc = v; + break; + default: + pr_err("module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rela[i].r_info)); + return -ENOEXEC; + } + } + return 0; + overflow: + pr_err("module %s: relocation offset overflow: %08x\n", + me->name, rela[i].r_offset); + return -ENOEXEC; +} diff --git a/arch/h8300/kernel/process.c b/arch/h8300/kernel/process.c new file mode 100644 index 000000000..46b1342ce --- /dev/null +++ b/arch/h8300/kernel/process.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/h8300/kernel/process.c + * + * Yoshinori Sato <ysato@users.sourceforge.jp> + * + * Based on: + * + * linux/arch/m68knommu/kernel/process.c + * + * Copyright (C) 1998 D. Jeff Dionne <jeff@ryeham.ee.ryerson.ca>, + * Kenneth Albanowski <kjahds@kjahds.com>, + * The Silver Hammer Group, Ltd. + * + * linux/arch/m68k/kernel/process.c + * + * Copyright (C) 1995 Hamish Macdonald + * + * 68060 fixes by Jesper Skov + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/sched/debug.h> +#include <linux/sched/task.h> +#include <linux/sched/task_stack.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/rcupdate.h> + +#include <linux/uaccess.h> +#include <asm/traps.h> +#include <asm/setup.h> + +void (*pm_power_off)(void) = NULL; +EXPORT_SYMBOL(pm_power_off); + +asmlinkage void ret_from_fork(void); +asmlinkage void ret_from_kernel_thread(void); + +/* + * The idle loop on an H8/300.. + */ +void arch_cpu_idle(void) +{ + raw_local_irq_enable(); + __asm__("sleep"); +} + +void machine_restart(char *__unused) +{ + local_irq_disable(); + __asm__("jmp @@0"); +} + +void machine_halt(void) +{ + local_irq_disable(); + __asm__("sleep"); + for (;;) + ; +} + +void machine_power_off(void) +{ + local_irq_disable(); + __asm__("sleep"); + for (;;) + ; +} + +void show_regs(struct pt_regs *regs) +{ + show_regs_print_info(KERN_DEFAULT); + + pr_notice("\n"); + pr_notice("PC: %08lx Status: %02x\n", + regs->pc, regs->ccr); + pr_notice("ORIG_ER0: %08lx ER0: %08lx ER1: %08lx\n", + regs->orig_er0, regs->er0, regs->er1); + pr_notice("ER2: %08lx ER3: %08lx ER4: %08lx ER5: %08lx\n", + regs->er2, regs->er3, regs->er4, regs->er5); + pr_notice("ER6' %08lx ", regs->er6); + if (user_mode(regs)) + printk("USP: %08lx\n", rdusp()); + else + printk("\n"); +} + +void flush_thread(void) +{ +} + +int copy_thread(unsigned long clone_flags, unsigned long usp, + unsigned long topstk, struct task_struct *p, unsigned long tls) +{ + struct pt_regs *childregs; + + childregs = (struct pt_regs *) (THREAD_SIZE + task_stack_page(p)) - 1; + + if (unlikely(p->flags & (PF_KTHREAD | PF_IO_WORKER))) { + memset(childregs, 0, sizeof(struct pt_regs)); + childregs->retpc = (unsigned long) ret_from_kernel_thread; + childregs->er4 = topstk; /* arg */ + childregs->er5 = usp; /* fn */ + } else { + *childregs = *current_pt_regs(); + childregs->er0 = 0; + childregs->retpc = (unsigned long) ret_from_fork; + p->thread.usp = usp ?: rdusp(); + } + p->thread.ksp = (unsigned long)childregs; + + return 0; +} + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long fp, pc; + unsigned long stack_page; + int count = 0; + + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + + stack_page = (unsigned long)p; + fp = ((struct pt_regs *)p->thread.ksp)->er6; + do { + if (fp < stack_page+sizeof(struct thread_info) || + fp >= 8184+stack_page) + return 0; + pc = ((unsigned long *)fp)[1]; + if (!in_sched_functions(pc)) + return pc; + fp = *(unsigned long *) fp; + } while (count++ < 16); + return 0; +} + +/* generic sys_clone is not enough registers */ +asmlinkage int sys_clone(unsigned long __user *args) +{ + unsigned long clone_flags; + unsigned long newsp; + uintptr_t parent_tidptr; + uintptr_t child_tidptr; + struct kernel_clone_args kargs = {}; + + get_user(clone_flags, &args[0]); + get_user(newsp, &args[1]); + get_user(parent_tidptr, &args[2]); + get_user(child_tidptr, &args[3]); + + kargs.flags = (lower_32_bits(clone_flags) & ~CSIGNAL); + kargs.pidfd = (int __user *)parent_tidptr; + kargs.child_tid = (int __user *)child_tidptr; + kargs.parent_tid = (int __user *)parent_tidptr; + kargs.exit_signal = (lower_32_bits(clone_flags) & CSIGNAL); + kargs.stack = newsp; + + return kernel_clone(&kargs); +} diff --git a/arch/h8300/kernel/ptrace.c b/arch/h8300/kernel/ptrace.c new file mode 100644 index 000000000..a11db009d --- /dev/null +++ b/arch/h8300/kernel/ptrace.c @@ -0,0 +1,200 @@ +/* + * linux/arch/h8300/kernel/ptrace.c + * + * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file COPYING in the main directory of + * this archive for more details. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/audit.h> +#include <linux/tracehook.h> +#include <linux/regset.h> +#include <linux/elf.h> + +#define CCR_MASK 0x6f /* mode/imask not set */ +#define EXR_MASK 0x80 /* modify only T */ + +#define PT_REG(r) offsetof(struct pt_regs, r) + +extern void user_disable_single_step(struct task_struct *child); + +/* Mapping from PT_xxx to the stack offset at which the register is + saved. Notice that usp has no stack-slot and needs to be treated + specially (see get_reg/put_reg below). */ +static const int register_offset[] = { + PT_REG(er1), PT_REG(er2), PT_REG(er3), PT_REG(er4), + PT_REG(er5), PT_REG(er6), PT_REG(er0), -1, + PT_REG(orig_er0), PT_REG(ccr), PT_REG(pc), +#if defined(CONFIG_CPU_H8S) + PT_REG(exr), +#endif +}; + +/* read register */ +long h8300_get_reg(struct task_struct *task, int regno) +{ + switch (regno) { + case PT_USP: + return task->thread.usp + sizeof(long)*2; + case PT_CCR: + case PT_EXR: + return *(unsigned short *)(task->thread.esp0 + + register_offset[regno]); + default: + return *(unsigned long *)(task->thread.esp0 + + register_offset[regno]); + } +} + +int h8300_put_reg(struct task_struct *task, int regno, unsigned long data) +{ + unsigned short oldccr; + unsigned short oldexr; + + switch (regno) { + case PT_USP: + task->thread.usp = data - sizeof(long)*2; + case PT_CCR: + oldccr = *(unsigned short *)(task->thread.esp0 + + register_offset[regno]); + oldccr &= ~CCR_MASK; + data &= CCR_MASK; + data |= oldccr; + *(unsigned short *)(task->thread.esp0 + + register_offset[regno]) = data; + break; + case PT_EXR: + oldexr = *(unsigned short *)(task->thread.esp0 + + register_offset[regno]); + oldccr &= ~EXR_MASK; + data &= EXR_MASK; + data |= oldexr; + *(unsigned short *)(task->thread.esp0 + + register_offset[regno]) = data; + break; + default: + *(unsigned long *)(task->thread.esp0 + + register_offset[regno]) = data; + break; + } + return 0; +} + +static int regs_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + int r; + + BUILD_BUG_ON(sizeof(struct user_regs_struct) % sizeof(long) != 0); + for (r = 0; r < ELF_NGREG; r++) + membuf_store(&to, h8300_get_reg(target, r)); + + return 0; +} + +static int regs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int r; + int ret; + struct user_regs_struct regs; + long *reg; + + /* build user regs in buffer */ + BUILD_BUG_ON(sizeof(regs) % sizeof(long) != 0); + for (reg = (long *)®s, r = 0; r < sizeof(regs) / sizeof(long); r++) + *reg++ = h8300_get_reg(target, r); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + ®s, 0, sizeof(regs)); + if (ret) + return ret; + + /* write back to pt_regs */ + for (reg = (long *)®s, r = 0; r < sizeof(regs) / sizeof(long); r++) + h8300_put_reg(target, r, *reg++); + return 0; +} + +enum h8300_regset { + REGSET_GENERAL, +}; + +static const struct user_regset h8300_regsets[] = { + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = ELF_NGREG, + .size = sizeof(long), + .align = sizeof(long), + .regset_get = regs_get, + .set = regs_set, + }, +}; + +static const struct user_regset_view user_h8300_native_view = { + .name = "h8300", + .e_machine = EM_H8_300, + .regsets = h8300_regsets, + .n = ARRAY_SIZE(h8300_regsets), +}; + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ + return &user_h8300_native_view; +} + +void ptrace_disable(struct task_struct *child) +{ + user_disable_single_step(child); +} + +long arch_ptrace(struct task_struct *child, long request, + unsigned long addr, unsigned long data) +{ + int ret; + + switch (request) { + default: + ret = ptrace_request(child, request, addr, data); + break; + } + return ret; +} + +asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) +{ + long ret = 0; + + if (test_thread_flag(TIF_SYSCALL_TRACE) && + tracehook_report_syscall_entry(regs)) + /* + * Tracing decided this syscall should not happen. + * We'll return a bogus call number to get an ENOSYS + * error, but leave the original number in regs->regs[0]. + */ + ret = -1L; + + audit_syscall_entry(regs->er1, regs->er2, regs->er3, + regs->er4, regs->er5); + + return ret ?: regs->er0; +} + +asmlinkage void do_syscall_trace_leave(struct pt_regs *regs) +{ + int step; + + audit_syscall_exit(regs); + + step = test_thread_flag(TIF_SINGLESTEP); + if (step || test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall_exit(regs, step); +} diff --git a/arch/h8300/kernel/ptrace_h.c b/arch/h8300/kernel/ptrace_h.c new file mode 100644 index 000000000..15db45a03 --- /dev/null +++ b/arch/h8300/kernel/ptrace_h.c @@ -0,0 +1,256 @@ +/* + * ptrace cpu depend helper functions + * + * Copyright 2003, 2015 Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file COPYING in the main directory of + * this archive for more details. + */ + +#include <linux/linkage.h> +#include <linux/sched/signal.h> +#include <asm/ptrace.h> + +#define BREAKINST 0x5730 /* trapa #3 */ + +/* disable singlestep */ +void user_disable_single_step(struct task_struct *child) +{ + if ((long)child->thread.breakinfo.addr != -1L) { + *(child->thread.breakinfo.addr) = child->thread.breakinfo.inst; + child->thread.breakinfo.addr = (unsigned short *)-1L; + } +} + +/* calculate next pc */ +enum jump_type {none, /* normal instruction */ + jabs, /* absolute address jump */ + ind, /* indirect address jump */ + ret, /* return to subrutine */ + reg, /* register indexed jump */ + relb, /* pc relative jump (byte offset) */ + relw, /* pc relative jump (word offset) */ + }; + +/* opcode decode table define + ptn: opcode pattern + msk: opcode bitmask + len: instruction length (<0 next table index) + jmp: jump operation mode */ +struct optable { + unsigned char bitpattern; + unsigned char bitmask; + signed char length; + signed char type; +} __packed __aligned(1); + +#define OPTABLE(ptn, msk, len, jmp) \ + { \ + .bitpattern = ptn, \ + .bitmask = msk, \ + .length = len, \ + .type = jmp, \ + } + +static const struct optable optable_0[] = { + OPTABLE(0x00, 0xff, 1, none), /* 0x00 */ + OPTABLE(0x01, 0xff, -1, none), /* 0x01 */ + OPTABLE(0x02, 0xfe, 1, none), /* 0x02-0x03 */ + OPTABLE(0x04, 0xee, 1, none), /* 0x04-0x05/0x14-0x15 */ + OPTABLE(0x06, 0xfe, 1, none), /* 0x06-0x07 */ + OPTABLE(0x08, 0xea, 1, none), /* 0x08-0x09/0x0c-0x0d/0x18-0x19/0x1c-0x1d */ + OPTABLE(0x0a, 0xee, 1, none), /* 0x0a-0x0b/0x1a-0x1b */ + OPTABLE(0x0e, 0xee, 1, none), /* 0x0e-0x0f/0x1e-0x1f */ + OPTABLE(0x10, 0xfc, 1, none), /* 0x10-0x13 */ + OPTABLE(0x16, 0xfe, 1, none), /* 0x16-0x17 */ + OPTABLE(0x20, 0xe0, 1, none), /* 0x20-0x3f */ + OPTABLE(0x40, 0xf0, 1, relb), /* 0x40-0x4f */ + OPTABLE(0x50, 0xfc, 1, none), /* 0x50-0x53 */ + OPTABLE(0x54, 0xfd, 1, ret), /* 0x54/0x56 */ + OPTABLE(0x55, 0xff, 1, relb), /* 0x55 */ + OPTABLE(0x57, 0xff, 1, none), /* 0x57 */ + OPTABLE(0x58, 0xfb, 2, relw), /* 0x58/0x5c */ + OPTABLE(0x59, 0xfb, 1, reg), /* 0x59/0x5b */ + OPTABLE(0x5a, 0xfb, 2, jabs), /* 0x5a/0x5e */ + OPTABLE(0x5b, 0xfb, 2, ind), /* 0x5b/0x5f */ + OPTABLE(0x60, 0xe8, 1, none), /* 0x60-0x67/0x70-0x77 */ + OPTABLE(0x68, 0xfa, 1, none), /* 0x68-0x69/0x6c-0x6d */ + OPTABLE(0x6a, 0xfe, -2, none), /* 0x6a-0x6b */ + OPTABLE(0x6e, 0xfe, 2, none), /* 0x6e-0x6f */ + OPTABLE(0x78, 0xff, 4, none), /* 0x78 */ + OPTABLE(0x79, 0xff, 2, none), /* 0x79 */ + OPTABLE(0x7a, 0xff, 3, none), /* 0x7a */ + OPTABLE(0x7b, 0xff, 2, none), /* 0x7b */ + OPTABLE(0x7c, 0xfc, 2, none), /* 0x7c-0x7f */ + OPTABLE(0x80, 0x80, 1, none), /* 0x80-0xff */ +}; + +static const struct optable optable_1[] = { + OPTABLE(0x00, 0xff, -3, none), /* 0x0100 */ + OPTABLE(0x40, 0xf0, -3, none), /* 0x0140-0x14f */ + OPTABLE(0x80, 0xf0, 1, none), /* 0x0180-0x018f */ + OPTABLE(0xc0, 0xc0, 2, none), /* 0x01c0-0x01ff */ +}; + +static const struct optable optable_2[] = { + OPTABLE(0x00, 0x20, 2, none), /* 0x6a0?/0x6a8?/0x6b0?/0x6b8? */ + OPTABLE(0x20, 0x20, 3, none), /* 0x6a2?/0x6aa?/0x6b2?/0x6ba? */ +}; + +static const struct optable optable_3[] = { + OPTABLE(0x69, 0xfb, 2, none), /* 0x010069/0x01006d/014069/0x01406d */ + OPTABLE(0x6b, 0xff, -4, none), /* 0x01006b/0x01406b */ + OPTABLE(0x6f, 0xff, 3, none), /* 0x01006f/0x01406f */ + OPTABLE(0x78, 0xff, 5, none), /* 0x010078/0x014078 */ +}; + +static const struct optable optable_4[] = { +/* 0x0100690?/0x01006d0?/0140690?/0x01406d0?/ + 0x0100698?/0x01006d8?/0140698?/0x01406d8? */ + OPTABLE(0x00, 0x78, 3, none), +/* 0x0100692?/0x01006d2?/0140692?/0x01406d2?/ + 0x010069a?/0x01006da?/014069a?/0x01406da? */ + OPTABLE(0x20, 0x78, 4, none), +}; + +static const struct optables_list { + const struct optable *ptr; + int size; +} optables[] = { +#define OPTABLES(no) \ + { \ + .ptr = optable_##no, \ + .size = sizeof(optable_##no) / sizeof(struct optable), \ + } + OPTABLES(0), + OPTABLES(1), + OPTABLES(2), + OPTABLES(3), + OPTABLES(4), + +}; + +const unsigned char condmask[] = { + 0x00, 0x40, 0x01, 0x04, 0x02, 0x08, 0x10, 0x20 +}; + +static int isbranch(struct task_struct *task, int reson) +{ + unsigned char cond = h8300_get_reg(task, PT_CCR); + + /* encode complex conditions */ + /* B4: N^V + B5: Z|(N^V) + B6: C|Z */ + __asm__("bld #3,%w0\n\t" + "bxor #1,%w0\n\t" + "bst #4,%w0\n\t" + "bor #2,%w0\n\t" + "bst #5,%w0\n\t" + "bld #2,%w0\n\t" + "bor #0,%w0\n\t" + "bst #6,%w0\n\t" + : "=&r"(cond) : "0"(cond) : "cc"); + cond &= condmask[reson >> 1]; + if (!(reson & 1)) + return cond == 0; + else + return cond != 0; +} + +static unsigned short *decode(struct task_struct *child, + const struct optable *op, + char *fetch_p, unsigned short *pc, + unsigned char inst) +{ + unsigned long addr; + unsigned long *sp; + int regno; + + switch (op->type) { + case none: + return (unsigned short *)pc + op->length; + case jabs: + addr = *(unsigned long *)pc; + return (unsigned short *)(addr & 0x00ffffff); + case ind: + addr = *pc & 0xff; + return (unsigned short *)(*(unsigned long *)addr); + case ret: + sp = (unsigned long *)h8300_get_reg(child, PT_USP); + /* user stack frames + | er0 | temporary saved + +--------+ + | exp | exception stack frames + +--------+ + | ret pc | userspace return address + */ + return (unsigned short *)(*(sp+2) & 0x00ffffff); + case reg: + regno = (*pc >> 4) & 0x07; + if (regno == 0) + addr = h8300_get_reg(child, PT_ER0); + else + addr = h8300_get_reg(child, regno-1 + PT_ER1); + return (unsigned short *)addr; + case relb: + if (inst == 0x55 || isbranch(child, inst & 0x0f)) + pc = (unsigned short *)((unsigned long)pc + + ((signed char)(*fetch_p))); + return pc+1; /* skip myself */ + case relw: + if (inst == 0x5c || isbranch(child, (*fetch_p & 0xf0) >> 4)) + pc = (unsigned short *)((unsigned long)pc + + ((signed short)(*(pc+1)))); + return pc+2; /* skip myself */ + default: + return NULL; + } +} + +static unsigned short *nextpc(struct task_struct *child, unsigned short *pc) +{ + const struct optable *op; + unsigned char *fetch_p; + int op_len; + unsigned char inst; + + op = optables[0].ptr; + op_len = optables[0].size; + fetch_p = (unsigned char *)pc; + inst = *fetch_p++; + do { + if ((inst & op->bitmask) == op->bitpattern) { + if (op->length < 0) { + op = optables[-op->length].ptr; + op_len = optables[-op->length].size + 1; + inst = *fetch_p++; + } else + return decode(child, op, fetch_p, pc, inst); + } else + op++; + } while (--op_len > 0); + return NULL; +} + +/* Set breakpoint(s) to simulate a single step from the current PC. */ + +void user_enable_single_step(struct task_struct *child) +{ + unsigned short *next; + + next = nextpc(child, (unsigned short *)h8300_get_reg(child, PT_PC)); + child->thread.breakinfo.addr = next; + child->thread.breakinfo.inst = *next; + *next = BREAKINST; +} + +asmlinkage void trace_trap(unsigned long bp) +{ + if ((unsigned long)current->thread.breakinfo.addr == bp) { + user_disable_single_step(current); + force_sig(SIGTRAP); + } else + force_sig(SIGILL); +} diff --git a/arch/h8300/kernel/ptrace_s.c b/arch/h8300/kernel/ptrace_s.c new file mode 100644 index 000000000..ee21f37b7 --- /dev/null +++ b/arch/h8300/kernel/ptrace_s.c @@ -0,0 +1,44 @@ +/* + * linux/arch/h8300/kernel/ptrace_h8s.c + * ptrace cpu depend helper functions + * + * Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file COPYING in the main directory of + * this archive for more details. + */ + +#include <linux/linkage.h> +#include <linux/sched/signal.h> +#include <linux/errno.h> +#include <asm/ptrace.h> + +#define CCR_MASK 0x6f +#define EXR_TRACE 0x80 + +/* disable singlestep */ +void user_disable_single_step(struct task_struct *child) +{ + unsigned char exr; + + exr = h8300_get_reg(child, PT_EXR); + exr &= ~EXR_TRACE; + h8300_put_reg(child, PT_EXR, exr); +} + +/* enable singlestep */ +void user_enable_single_step(struct task_struct *child) +{ + unsigned char exr; + + exr = h8300_get_reg(child, PT_EXR); + exr |= EXR_TRACE; + h8300_put_reg(child, PT_EXR, exr); +} + +asmlinkage void trace_trap(unsigned long bp) +{ + (void)bp; + force_sig(SIGTRAP); +} diff --git a/arch/h8300/kernel/setup.c b/arch/h8300/kernel/setup.c new file mode 100644 index 000000000..0281f92ee --- /dev/null +++ b/arch/h8300/kernel/setup.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/h8300/kernel/setup.c + * + * Copyright (C) 2001-2014 Yoshinori Sato <ysato@users.sourceforge.jp> + */ + +/* + * This file handles the architecture-dependent parts of system setup + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/console.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/seq_file.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_address.h> +#include <linux/clk-provider.h> +#include <linux/memblock.h> +#include <linux/screen_info.h> +#include <linux/clocksource.h> + +#include <asm/setup.h> +#include <asm/irq.h> +#include <asm/sections.h> +#include <asm/page.h> + +#if defined(CONFIG_CPU_H8300H) +#define CPU "H8/300H" +#elif defined(CONFIG_CPU_H8S) +#define CPU "H8S" +#else +#define CPU "Unknown" +#endif + +unsigned long memory_start; +unsigned long memory_end; +EXPORT_SYMBOL(memory_end); +static unsigned long freq; +extern char __dtb_start[]; + +#ifdef CONFIG_VT +struct screen_info screen_info; +#endif + +char __initdata command_line[COMMAND_LINE_SIZE]; + +void sim_console_register(void); + +void __init h8300_fdt_init(void *fdt, char *bootargs) +{ + if (!fdt) + fdt = __dtb_start; + else + strcpy(command_line, bootargs); + + early_init_dt_scan(fdt); + memblock_allow_resize(); +} + +static void __init bootmem_init(void) +{ + struct memblock_region *region; + + memory_end = memory_start = 0; + + /* Find main memory where is the kernel */ + memory_start = memblock_start_of_DRAM(); + memory_end = memblock_end_of_DRAM(); + + if (!memory_end) + panic("No memory!"); + + /* setup bootmem globals (we use no_bootmem, but mm still depends on this) */ + min_low_pfn = PFN_UP(memory_start); + max_low_pfn = PFN_DOWN(memory_end); + max_pfn = max_low_pfn; + + memblock_reserve(__pa(_stext), _end - _stext); + + early_init_fdt_reserve_self(); + early_init_fdt_scan_reserved_mem(); + + memblock_dump_all(); +} + +void __init setup_arch(char **cmdline_p) +{ + unflatten_and_copy_device_tree(); + + init_mm.start_code = (unsigned long) _stext; + init_mm.end_code = (unsigned long) _etext; + init_mm.end_data = (unsigned long) _edata; + init_mm.brk = (unsigned long) 0; + + pr_notice("\r\n\nuClinux " CPU "\n"); + pr_notice("Flat model support (C) 1998,1999 Kenneth Albanowski, D. Jeff Dionne\n"); + + if (*command_line) + strcpy(boot_command_line, command_line); + *cmdline_p = boot_command_line; + + parse_early_param(); + + bootmem_init(); + /* + * get kmalloc into gear + */ + paging_init(); +} + +/* + * Get CPU information for use by the procfs. + */ + +static int show_cpuinfo(struct seq_file *m, void *v) +{ + char *cpu; + + cpu = CPU; + + seq_printf(m, "CPU:\t\t%s\n" + "Clock:\t\t%lu.%1luMHz\n" + "BogoMips:\t%lu.%02lu\n" + "Calibration:\t%lu loops\n", + cpu, + freq/1000, freq%1000, + (loops_per_jiffy*HZ)/500000, + ((loops_per_jiffy*HZ)/5000)%100, + (loops_per_jiffy*HZ)); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < num_possible_cpus() ? + ((void *) 0x12345678) : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +const struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; + +#if defined(CONFIG_CPU_H8300H) +#define get_wait(base, addr) ({ \ + int baddr; \ + baddr = ((addr) / 0x200000 * 2); \ + w *= (readw((base) + 2) & (3 << baddr)) + 1; \ + }) +#endif +#if defined(CONFIG_CPU_H8S) +#define get_wait(base, addr) ({ \ + int baddr; \ + baddr = ((addr) / 0x200000 * 16); \ + w *= (readl((base) + 2) & (7 << baddr)) + 1; \ + }) +#endif + +static __init int access_timing(void) +{ + struct device_node *bsc; + void __iomem *base; + unsigned long addr = (unsigned long)&__delay; + int bit = 1 << (addr / 0x200000); + int w; + + bsc = of_find_compatible_node(NULL, NULL, "renesas,h8300-bsc"); + base = of_iomap(bsc, 0); + w = (readb(base + 0) & bit)?2:1; + if (readb(base + 1) & bit) + w *= get_wait(base, addr); + else + w *= 2; + return w * 3 / 2; +} + +void __init calibrate_delay(void) +{ + struct device_node *cpu; + int freq; + + cpu = of_find_compatible_node(NULL, NULL, "renesas,h8300"); + of_property_read_s32(cpu, "clock-frequency", &freq); + loops_per_jiffy = freq / HZ / (access_timing() * 2); + pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n", + loops_per_jiffy / (500000 / HZ), + (loops_per_jiffy / (5000 / HZ)) % 100, loops_per_jiffy); +} + + +void __init time_init(void) +{ + of_clk_init(NULL); + timer_probe(); +} diff --git a/arch/h8300/kernel/signal.c b/arch/h8300/kernel/signal.c new file mode 100644 index 000000000..75a1c36b1 --- /dev/null +++ b/arch/h8300/kernel/signal.c @@ -0,0 +1,287 @@ +/* + * linux/arch/h8300/kernel/signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +/* + * uClinux H8/300 support by Yoshinori Sato <ysato@users.sourceforge.jp> + * and David McCullough <davidm@snapgear.com> + * + * Based on + * Linux/m68k by Hamish Macdonald + */ + +/* + * ++roman (07/09/96): implemented signal stacks (specially for tosemu on + * Atari :-) Current limitation: Only one sigstack can be active at one time. + * If a second signal with SA_ONSTACK set arrives while working on a sigstack, + * SA_ONSTACK is ignored. This behaviour avoids lots of trouble with nested + * signal handlers! + */ + +#include <linux/sched.h> +#include <linux/sched/task_stack.h> +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/syscalls.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/stddef.h> +#include <linux/highuid.h> +#include <linux/personality.h> +#include <linux/tty.h> +#include <linux/binfmts.h> +#include <linux/tracehook.h> + +#include <asm/setup.h> +#include <linux/uaccess.h> +#include <asm/traps.h> +#include <asm/ucontext.h> + +/* + * Do a signal return; undo the signal stack. + * + * Keep the return code on the stack quadword aligned! + * That makes the cache flush below easier. + */ + +struct rt_sigframe { + long dummy_er0; + long dummy_vector; +#if defined(CONFIG_CPU_H8S) + short dummy_exr; +#endif + long dummy_pc; + char *pretcode; + struct siginfo *pinfo; + void *puc; + unsigned char retcode[8]; + struct siginfo info; + struct ucontext uc; + int sig; +} __packed __aligned(2); + +static inline int +restore_sigcontext(struct sigcontext *usc, int *pd0) +{ + struct pt_regs *regs = current_pt_regs(); + int err = 0; + unsigned int ccr; + unsigned int usp; + unsigned int er0; + + /* Always make any pending restarted system calls return -EINTR */ + current->restart_block.fn = do_no_restart_syscall; + + /* restore passed registers */ +#define COPY(r) do { err |= get_user(regs->r, &usc->sc_##r); } while (0) + COPY(er1); + COPY(er2); + COPY(er3); + COPY(er5); + COPY(pc); + ccr = regs->ccr & 0x10; + COPY(ccr); +#undef COPY + regs->ccr &= 0xef; + regs->ccr |= ccr; + regs->orig_er0 = -1; /* disable syscall checks */ + err |= __get_user(usp, &usc->sc_usp); + regs->sp = usp; + + err |= __get_user(er0, &usc->sc_er0); + *pd0 = er0; + return err; +} + +asmlinkage int sys_rt_sigreturn(void) +{ + unsigned long usp = rdusp(); + struct rt_sigframe *frame = (struct rt_sigframe *)(usp - 4); + sigset_t set; + int er0; + + if (!access_ok(frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + set_current_blocked(&set); + + if (restore_sigcontext(&frame->uc.uc_mcontext, &er0)) + goto badframe; + + if (restore_altstack(&frame->uc.uc_stack)) + goto badframe; + + return er0; + +badframe: + force_sig(SIGSEGV); + return 0; +} + +static int setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, + unsigned long mask) +{ + int err = 0; + + err |= __put_user(regs->er0, &sc->sc_er0); + err |= __put_user(regs->er1, &sc->sc_er1); + err |= __put_user(regs->er2, &sc->sc_er2); + err |= __put_user(regs->er3, &sc->sc_er3); + err |= __put_user(regs->er4, &sc->sc_er4); + err |= __put_user(regs->er5, &sc->sc_er5); + err |= __put_user(regs->er6, &sc->sc_er6); + err |= __put_user(rdusp(), &sc->sc_usp); + err |= __put_user(regs->pc, &sc->sc_pc); + err |= __put_user(regs->ccr, &sc->sc_ccr); + err |= __put_user(mask, &sc->sc_mask); + + return err; +} + +static inline void __user * +get_sigframe(struct ksignal *ksig, struct pt_regs *regs, size_t frame_size) +{ + return (void __user *)((sigsp(rdusp(), ksig) - frame_size) & -8UL); +} + +static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, + struct pt_regs *regs) +{ + struct rt_sigframe *frame; + int err = 0; + unsigned char *ret; + + frame = get_sigframe(ksig, regs, sizeof(*frame)); + + if (!access_ok(frame, sizeof(*frame))) + return -EFAULT; + + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + err |= copy_siginfo_to_user(&frame->info, &ksig->info); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __save_altstack(&frame->uc.uc_stack, rdusp()); + err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); + err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + return -EFAULT; + + /* Set up to return from userspace. */ + ret = (unsigned char *)&frame->retcode; + if (ksig->ka.sa.sa_flags & SA_RESTORER) + ret = (unsigned char *)(ksig->ka.sa.sa_restorer); + else { + /* sub.l er0,er0; mov.b #__NR_rt_sigreturn,r0l; trapa #0 */ + err |= __put_user(0x1a80f800 + (__NR_rt_sigreturn & 0xff), + (unsigned long *)(frame->retcode + 0)); + err |= __put_user(0x5700, + (unsigned short *)(frame->retcode + 4)); + } + err |= __put_user(ret, &frame->pretcode); + + if (err) + return -EFAULT; + + /* Set up registers for signal handler */ + regs->sp = (unsigned long)frame; + regs->pc = (unsigned long)ksig->ka.sa.sa_handler; + regs->er0 = ksig->sig; + regs->er1 = (unsigned long)&(frame->info); + regs->er2 = (unsigned long)&frame->uc; + regs->er5 = current->mm->start_data; /* GOT base */ + + return 0; +} + +static void +handle_restart(struct pt_regs *regs, struct k_sigaction *ka) +{ + switch (regs->er0) { + case -ERESTARTNOHAND: + if (!ka) + goto do_restart; + regs->er0 = -EINTR; + break; + case -ERESTART_RESTARTBLOCK: + if (!ka) { + regs->er0 = __NR_restart_syscall; + regs->pc -= 2; + } else + regs->er0 = -EINTR; + break; + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->er0 = -EINTR; + break; + } + fallthrough; + case -ERESTARTNOINTR: +do_restart: + regs->er0 = regs->orig_er0; + regs->pc -= 2; + break; + } +} + +/* + * OK, we're invoking a handler + */ +static void +handle_signal(struct ksignal *ksig, struct pt_regs *regs) +{ + sigset_t *oldset = sigmask_to_save(); + int ret; + /* are we from a system call? */ + if (regs->orig_er0 >= 0) + handle_restart(regs, &ksig->ka); + + ret = setup_rt_frame(ksig, oldset, regs); + + signal_setup_done(ret, ksig, 0); +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +static void do_signal(struct pt_regs *regs) +{ + struct ksignal ksig; + + current->thread.esp0 = (unsigned long) regs; + + if (get_signal(&ksig)) { + /* Whee! Actually deliver the signal. */ + handle_signal(&ksig, regs); + return; + } + /* Did we come from a system call? */ + if (regs->orig_er0 >= 0) + handle_restart(regs, NULL); + + /* If there's no signal to deliver, we just restore the saved mask. */ + restore_saved_sigmask(); +} + +asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags) +{ + if (thread_info_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) + do_signal(regs); + + if (thread_info_flags & _TIF_NOTIFY_RESUME) + tracehook_notify_resume(regs); +} diff --git a/arch/h8300/kernel/sim-console.c b/arch/h8300/kernel/sim-console.c new file mode 100644 index 000000000..03aa35b1a --- /dev/null +++ b/arch/h8300/kernel/sim-console.c @@ -0,0 +1,31 @@ +/* + * arch/h8300/kernel/sim-console.c + * + * Copyright (C) 2015 Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/console.h> +#include <linux/init.h> +#include <linux/serial_core.h> + +static void sim_write(struct console *con, const char *s, unsigned n) +{ + register const char *_ptr __asm__("er1") = s; + register const unsigned _len __asm__("er2") = n; + + __asm__("sub.l er0,er0\n\t" /* er0 = 1 (stdout) */ + "inc.l #1,er0\n\t" + ".byte 0x5e,0x00,0x00,0xc7\n\t" /* jsr @0xc7 (sys_write) */ + : : "g"(_ptr), "g"(_len):"er0"); +} + +static int __init sim_setup(struct earlycon_device *device, const char *opt) +{ + device->con->write = sim_write; + return 0; +} + +EARLYCON_DECLARE(h8sim, sim_setup); diff --git a/arch/h8300/kernel/syscalls.c b/arch/h8300/kernel/syscalls.c new file mode 100644 index 000000000..9022036fb --- /dev/null +++ b/arch/h8300/kernel/syscalls.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/syscalls.h> +#include <linux/signal.h> +#include <linux/unistd.h> + +#undef __SYSCALL +#define __SYSCALL(nr, call) [nr] = (call), + +#define sys_mmap2 sys_mmap_pgoff + +asmlinkage int sys_rt_sigreturn(void); + +void *_sys_call_table[__NR_syscalls] = { +#include <asm/unistd.h> +}; diff --git a/arch/h8300/kernel/traps.c b/arch/h8300/kernel/traps.c new file mode 100644 index 000000000..cf23ccb50 --- /dev/null +++ b/arch/h8300/kernel/traps.c @@ -0,0 +1,160 @@ +/* + * linux/arch/h8300/boot/traps.c -- general exception handling code + * H8/300 support Yoshinori Sato <ysato@users.sourceforge.jp> + * + * Cloned from Linux/m68k. + * + * No original Copyright holder listed, + * Probable original (C) Roman Zippel (assigned DJD, 1999) + * + * Copyright 1999-2000 D. Jeff Dionne, <jeff@rt-control.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/sched/debug.h> +#include <linux/sched/task.h> +#include <linux/mm_types.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/bug.h> + +#include <asm/irq.h> +#include <asm/traps.h> +#include <asm/page.h> + +static DEFINE_SPINLOCK(die_lock); + +/* + * this must be called very early as the kernel might + * use some instruction that are emulated on the 060 + */ + +void __init base_trap_init(void) +{ +} + +void __init trap_init(void) +{ +} + +asmlinkage void set_esp0(unsigned long ssp) +{ + current->thread.esp0 = ssp; +} + +/* + * Generic dumping code. Used for panic and debug. + */ + +static void dump(struct pt_regs *fp) +{ + unsigned long *sp; + unsigned char *tp; + int i; + + pr_info("\nCURRENT PROCESS:\n\n"); + pr_info("COMM=%s PID=%d\n", current->comm, current->pid); + if (current->mm) { + pr_info("TEXT=%08x-%08x DATA=%08x-%08x BSS=%08x-%08x\n", + (int) current->mm->start_code, + (int) current->mm->end_code, + (int) current->mm->start_data, + (int) current->mm->end_data, + (int) current->mm->end_data, + (int) current->mm->brk); + pr_info("USER-STACK=%08x KERNEL-STACK=%08lx\n\n", + (int) current->mm->start_stack, + (int) PAGE_SIZE+(unsigned long)current); + } + + show_regs(fp); + pr_info("\nCODE:"); + tp = ((unsigned char *) fp->pc) - 0x20; + for (sp = (unsigned long *) tp, i = 0; (i < 0x40); i += 4) { + if ((i % 0x10) == 0) + pr_info("\n%08x: ", (int) (tp + i)); + pr_info("%08x ", (int) *sp++); + } + pr_info("\n"); + + pr_info("\nKERNEL STACK:"); + tp = ((unsigned char *) fp) - 0x40; + for (sp = (unsigned long *) tp, i = 0; (i < 0xc0); i += 4) { + if ((i % 0x10) == 0) + pr_info("\n%08x: ", (int) (tp + i)); + pr_info("%08x ", (int) *sp++); + } + pr_info("\n"); + if (STACK_MAGIC != *(unsigned long *)((unsigned long)current+PAGE_SIZE)) + pr_info("(Possibly corrupted stack page??)\n"); + + pr_info("\n\n"); +} + +void die(const char *str, struct pt_regs *fp, unsigned long err) +{ + static int diecount; + + oops_enter(); + + console_verbose(); + spin_lock_irq(&die_lock); + report_bug(fp->pc, fp); + pr_crit("%s: %04lx [#%d] ", str, err & 0xffff, ++diecount); + dump(fp); + + spin_unlock_irq(&die_lock); + make_task_dead(SIGSEGV); +} + +static int kstack_depth_to_print = 24; + +void show_stack(struct task_struct *task, unsigned long *esp, const char *loglvl) +{ + unsigned long *stack, addr; + int i; + + if (esp == NULL) + esp = (unsigned long *) &esp; + + stack = esp; + + printk("%sStack from %08lx:", loglvl, (unsigned long)stack); + for (i = 0; i < kstack_depth_to_print; i++) { + if (((unsigned long)stack & (THREAD_SIZE - 1)) >= + THREAD_SIZE-4) + break; + if (i % 8 == 0) + printk("%s ", loglvl); + pr_cont(" %08lx", *stack++); + } + + printk("%s\nCall Trace:\n", loglvl); + i = 0; + stack = esp; + while (((unsigned long)stack & (THREAD_SIZE - 1)) < THREAD_SIZE-4) { + addr = *stack++; + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (check_kernel_text(addr)) { + if (i % 4 == 0) + printk("%s ", loglvl); + pr_cont(" [<%08lx>]", addr); + i++; + } + } + printk("%s\n", loglvl); +} diff --git a/arch/h8300/kernel/vmlinux.lds.S b/arch/h8300/kernel/vmlinux.lds.S new file mode 100644 index 000000000..6b1afc2f9 --- /dev/null +++ b/arch/h8300/kernel/vmlinux.lds.S @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#define RO_EXCEPTION_TABLE_ALIGN 16 + +#include <asm-generic/vmlinux.lds.h> +#include <asm/page.h> +#include <asm/thread_info.h> + +#define ROMTOP 0x000000 +#define RAMTOP 0x400000 + +jiffies = jiffies_64 + 4; + +ENTRY(_start) + +SECTIONS +{ +#if defined(CONFIG_ROMKERNEL) + . = ROMTOP; + .vectors : + { + _vector = . ; + *(.vector*) + } +#else + . = RAMTOP; + _ramstart = .; + . = . + CONFIG_OFFSET; +#endif + _text = .; + HEAD_TEXT_SECTION + .text : { + _stext = . ; + TEXT_TEXT + SCHED_TEXT + CPUIDLE_TEXT + LOCK_TEXT +#if defined(CONFIG_ROMKERNEL) + *(.int_redirect) +#endif + _etext = . ; + } + RO_DATA(4) + ROMEND = .; +#if defined(CONFIG_ROMKERNEL) + . = RAMTOP; + _ramstart = .; +#define ADDR(x) ROMEND +#endif + _sdata = . ; + __data_start = . ; + RW_DATA(0, PAGE_SIZE, THREAD_SIZE) +#if defined(CONFIG_ROMKERNEL) +#undef ADDR +#endif + . = ALIGN(0x4) ; + __init_begin = .; + INIT_TEXT_SECTION(4) + INIT_DATA_SECTION(4) + __init_end = .; + _edata = . ; + _begin_data = LOADADDR(.data); + _sbss =.; + BSS_SECTION(0, 0 ,0) + _ebss =.; + _ramend = .; + _end = .; + DISCARDS +} diff --git a/arch/h8300/lib/Makefile b/arch/h8300/lib/Makefile new file mode 100644 index 000000000..685fa837c --- /dev/null +++ b/arch/h8300/lib/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for H8/300-specific library files.. +# + +lib-y = memcpy.o memset.o abs.o strncpy.o \ + mulsi3.o udivsi3.o muldi3.o moddivsi3.o \ + ashldi3.o lshrdi3.o ashrdi3.o ucmpdi2.o \ + delay.o diff --git a/arch/h8300/lib/abs.S b/arch/h8300/lib/abs.S new file mode 100644 index 000000000..6e1a4ed3a --- /dev/null +++ b/arch/h8300/lib/abs.S @@ -0,0 +1,21 @@ +;;; SPDX-License-Identifier: GPL-2.0 +;;; abs.S + +#include <asm/linkage.h> + +#if defined(CONFIG_CPU_H8300H) + .h8300h +#endif +#if defined(CONFIG_CPU_H8S) + .h8300s +#endif + .text +.global _abs + +;;; int abs(int n) +_abs: + mov.l er0,er0 + bpl 1f + neg.l er0 +1: + rts diff --git a/arch/h8300/lib/ashldi3.c b/arch/h8300/lib/ashldi3.c new file mode 100644 index 000000000..430720c0c --- /dev/null +++ b/arch/h8300/lib/ashldi3.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libgcc.h" + +DWtype +__ashldi3(DWtype u, word_type b) +{ + const DWunion uu = {.ll = u}; + const word_type bm = (sizeof (Wtype) * BITS_PER_UNIT) - b; + DWunion w; + + if (b == 0) + return u; + + if (bm <= 0) { + w.s.low = 0; + w.s.high = (UWtype) uu.s.low << -bm; + } else { + const UWtype carries = (UWtype) uu.s.low >> bm; + + w.s.low = (UWtype) uu.s.low << b; + w.s.high = ((UWtype) uu.s.high << b) | carries; + } + + return w.ll; +} diff --git a/arch/h8300/lib/ashrdi3.c b/arch/h8300/lib/ashrdi3.c new file mode 100644 index 000000000..2c6cd424e --- /dev/null +++ b/arch/h8300/lib/ashrdi3.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libgcc.h" + +DWtype __ashrdi3(DWtype u, word_type b) +{ + const DWunion uu = {.ll = u}; + const word_type bm = (sizeof (Wtype) * BITS_PER_UNIT) - b; + DWunion w; + + if (b == 0) + return u; + + if (bm <= 0) { + /* w.s.high = 1..1 or 0..0 */ + w.s.high = uu.s.high >> (sizeof (Wtype) * BITS_PER_UNIT - 1); + w.s.low = uu.s.high >> -bm; + } else { + const UWtype carries = (UWtype) uu.s.high << bm; + + w.s.high = uu.s.high >> b; + w.s.low = ((UWtype) uu.s.low >> b) | carries; + } + + return w.ll; +} diff --git a/arch/h8300/lib/delay.c b/arch/h8300/lib/delay.c new file mode 100644 index 000000000..f4277529a --- /dev/null +++ b/arch/h8300/lib/delay.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * delay loops + * + * Copyright (C) 2015 Yoshinori Sato + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <asm/param.h> +#include <asm/processor.h> +#include <asm/timex.h> + +void __delay(unsigned long cycles) +{ + __asm__ volatile ("1: dec.l #1,%0\n\t" + "bne 1b":"=r"(cycles):"0"(cycles)); +} +EXPORT_SYMBOL(__delay); + +void __const_udelay(unsigned long xloops) +{ + u64 loops; + + loops = (u64)xloops * loops_per_jiffy * HZ; + + __delay(loops >> 32); +} +EXPORT_SYMBOL(__const_udelay); + +void __udelay(unsigned long usecs) +{ + __const_udelay(usecs * 0x10C7UL); /* 2**32 / 1000000 (rounded up) */ +} +EXPORT_SYMBOL(__udelay); + +void __ndelay(unsigned long nsecs) +{ + __const_udelay(nsecs * 0x5UL); /* 2**32 / 1000000000 (rounded up) */ +} +EXPORT_SYMBOL(__ndelay); diff --git a/arch/h8300/lib/libgcc.h b/arch/h8300/lib/libgcc.h new file mode 100644 index 000000000..b8bcf7035 --- /dev/null +++ b/arch/h8300/lib/libgcc.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __H8300_LIBGCC_H__ +#define __H8300_LIBGCC_H__ + +#ifdef __ASSEMBLY__ +#define A0 r0 +#define A0L r0l +#define A0H r0h + +#define A1 r1 +#define A1L r1l +#define A1H r1h + +#define A2 r2 +#define A2L r2l +#define A2H r2h + +#define A3 r3 +#define A3L r3l +#define A3H r3h + +#define S0 r4 +#define S0L r4l +#define S0H r4h + +#define S1 r5 +#define S1L r5l +#define S1H r5h + +#define S2 r6 +#define S2L r6l +#define S2H r6h + +#define PUSHP push.l +#define POPP pop.l + +#define A0P er0 +#define A1P er1 +#define A2P er2 +#define A3P er3 +#define S0P er4 +#define S1P er5 +#define S2P er6 + +#define A0E e0 +#define A1E e1 +#define A2E e2 +#define A3E e3 +#else +#define Wtype SItype +#define UWtype USItype +#define HWtype SItype +#define UHWtype USItype +#define DWtype DItype +#define UDWtype UDItype +#define UWtype USItype +#define Wtype SItype +#define UWtype USItype +#define W_TYPE_SIZE (4 * BITS_PER_UNIT) +#define BITS_PER_UNIT (8) + +typedef int SItype __attribute__ ((mode (SI))); +typedef unsigned int USItype __attribute__ ((mode (SI))); +typedef int DItype __attribute__ ((mode (DI))); +typedef unsigned int UDItype __attribute__ ((mode (DI))); +struct DWstruct { + Wtype high, low; +}; +typedef union { + struct DWstruct s; + DWtype ll; +} DWunion; + +typedef int word_type __attribute__ ((mode (__word__))); + +#endif + +#endif diff --git a/arch/h8300/lib/lshrdi3.c b/arch/h8300/lib/lshrdi3.c new file mode 100644 index 000000000..8dbc861ca --- /dev/null +++ b/arch/h8300/lib/lshrdi3.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libgcc.h" + +DWtype __lshrdi3(DWtype u, word_type b) +{ + const DWunion uu = {.ll = u}; + const word_type bm = (sizeof (Wtype) * BITS_PER_UNIT) - b; + DWunion w; + + if (b == 0) + return u; + + if (bm <= 0) { + w.s.high = 0; + w.s.low = (UWtype) uu.s.high >> -bm; + } else { + const UWtype carries = (UWtype) uu.s.high << bm; + + w.s.high = (UWtype) uu.s.high >> b; + w.s.low = ((UWtype) uu.s.low >> b) | carries; + } + + return w.ll; +} diff --git a/arch/h8300/lib/memcpy.S b/arch/h8300/lib/memcpy.S new file mode 100644 index 000000000..f1cd67d5e --- /dev/null +++ b/arch/h8300/lib/memcpy.S @@ -0,0 +1,86 @@ +;;; SPDX-License-Identifier: GPL-2.0 +;;; memcpy.S + +#include <asm/linkage.h> + +#if defined(CONFIG_CPU_H8300H) + .h8300h +#endif +#if defined(CONFIG_CPU_H8S) + .h8300s +#endif + .text +.global memcpy + +;;; void *memcpy(void *to, void *from, size_t n) +memcpy: + mov.l er2,er2 + bne 1f + rts +1: + ;; address check + bld #0,r0l + bxor #0,r1l + bcs 4f + mov.l er4,@-sp + mov.l er0,@-sp + btst #0,r0l + beq 1f + ;; (aligned even) odd address + mov.b @er1,r3l + mov.b r3l,@er0 + adds #1,er1 + adds #1,er0 + dec.l #1,er2 + beq 3f +1: + ;; n < sizeof(unsigned long) check + sub.l er4,er4 + adds #4,er4 ; loop count check value + cmp.l er4,er2 + blo 2f + ;; unsigned long copy +1: + mov.l @er1,er3 + mov.l er3,@er0 + adds #4,er0 + adds #4,er1 + subs #4,er2 + cmp.l er4,er2 + bcc 1b + ;; rest +2: + mov.l er2,er2 + beq 3f +1: + mov.b @er1,r3l + mov.b r3l,@er0 + adds #1,er1 + adds #1,er0 + dec.l #1,er2 + bne 1b +3: + mov.l @sp+,er0 + mov.l @sp+,er4 + rts + + ;; odd <- even / even <- odd +4: + mov.l er4,er3 + mov.l er2,er4 + mov.l er5,er2 + mov.l er1,er5 + mov.l er6,er1 + mov.l er0,er6 +1: + eepmov.w + mov.w r4,r4 + bne 1b + dec.w #1,e4 + bpl 1b + mov.l er1,er6 + mov.l er2,er5 + mov.l er3,er4 + rts + + .end diff --git a/arch/h8300/lib/memset.S b/arch/h8300/lib/memset.S new file mode 100644 index 000000000..2d1abc37f --- /dev/null +++ b/arch/h8300/lib/memset.S @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* memset.S */ + +#include <asm/linkage.h> + +#if defined(CONFIG_CPU_H8300H) + .h8300h +#endif +#if defined(CONFIG_CPU_H8S) + .h8300s +#endif + .text + +.global memset +.global clear_user + +;;void *memset(*ptr, int c, size_t count) +;; ptr = er0 +;; c = er1(r1l) +;; count = er2 +memset: + btst #0,r0l + beq 2f + + ;; odd address +1: + mov.b r1l,@er0 + adds #1,er0 + dec.l #1,er2 + beq 6f + + ;; even address +2: + mov.l er2,er3 + cmp.l #4,er2 + blo 4f + ;; count>=4 -> count/4 +#if defined(CONFIG_CPU_H8300H) + shlr.l er2 + shlr.l er2 +#endif +#if defined(CONFIG_CPU_H8S) + shlr.l #2,er2 +#endif + ;; byte -> long + mov.b r1l,r1h + mov.w r1,e1 +3: + mov.l er1,@er0 + adds #4,er0 + dec.l #1,er2 + bne 3b +4: + ;; count % 4 + and.b #3,r3l + beq 6f +5: + mov.b r1l,@er0 + adds #1,er0 + dec.b r3l + bne 5b +6: + rts + +clear_user: + mov.l er1, er2 + sub.l er1, er1 + bra memset + + .end diff --git a/arch/h8300/lib/moddivsi3.S b/arch/h8300/lib/moddivsi3.S new file mode 100644 index 000000000..9e33ab045 --- /dev/null +++ b/arch/h8300/lib/moddivsi3.S @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "libgcc.h" + +; numerator in A0/A1 +; denominator in A2/A3 + .global __modsi3 +__modsi3: + PUSHP S2P + bsr modnorm + bsr __divsi3 + mov.l er3,er0 + bra exitdiv + + .global __umodsi3 +__umodsi3: + bsr __udivsi3:16 + mov.l er3,er0 + rts + + .global __divsi3 +__divsi3: + PUSHP S2P + bsr divnorm + bsr __udivsi3:16 + + ; examine what the sign should be +exitdiv: + btst #3,S2L + beq reti + + ; should be -ve + neg.l A0P + +reti: + POPP S2P + rts + +divnorm: + mov.l A0P,A0P ; is the numerator -ve + stc ccr,S2L ; keep the sign in bit 3 of S2L + bge postive + + neg.l A0P ; negate arg + +postive: + mov.l A1P,A1P ; is the denominator -ve + bge postive2 + + neg.l A1P ; negate arg + xor.b #0x08,S2L ; toggle the result sign + +postive2: + rts + +;; Basically the same, except that the sign of the divisor determines +;; the sign. +modnorm: + mov.l A0P,A0P ; is the numerator -ve + stc ccr,S2L ; keep the sign in bit 3 of S2L + bge mpostive + + neg.l A0P ; negate arg + +mpostive: + mov.l A1P,A1P ; is the denominator -ve + bge mpostive2 + + neg.l A1P ; negate arg + +mpostive2: + rts + + .end diff --git a/arch/h8300/lib/modsi3.S b/arch/h8300/lib/modsi3.S new file mode 100644 index 000000000..bdc8a0029 --- /dev/null +++ b/arch/h8300/lib/modsi3.S @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "libgcc.h" + +; numerator in A0/A1 +; denominator in A2/A3 + .global __modsi3 +__modsi3: + PUSHP S2P + bsr modnorm + bsr __divsi3 + mov.l er3,er0 + bra exitdiv + + .global __umodsi3 +__umodsi3: + bsr __udivsi3 + mov.l er3,er0 + rts + + .global __divsi3 +__divsi3: + PUSHP S2P + jsr divnorm + bsr __udivsi3 + + ; examine what the sign should be +exitdiv: + btst #3,S2L + beq reti + + ; should be -ve + neg.l A0P + +reti: + POPP S2P + rts + +divnorm: + mov.l A0P,A0P ; is the numerator -ve + stc ccr,S2L ; keep the sign in bit 3 of S2L + bge postive + + neg.l A0P ; negate arg + +postive: + mov.l A1P,A1P ; is the denominator -ve + bge postive2 + + neg.l A1P ; negate arg + xor.b #0x08,S2L ; toggle the result sign + +postive2: + rts + +;; Basically the same, except that the sign of the divisor determines +;; the sign. +modnorm: + mov.l A0P,A0P ; is the numerator -ve + stc ccr,S2L ; keep the sign in bit 3 of S2L + bge mpostive + + neg.l A0P ; negate arg + +mpostive: + mov.l A1P,A1P ; is the denominator -ve + bge mpostive2 + + neg.l A1P ; negate arg + +mpostive2: + rts + + .end diff --git a/arch/h8300/lib/muldi3.c b/arch/h8300/lib/muldi3.c new file mode 100644 index 000000000..9cb11b134 --- /dev/null +++ b/arch/h8300/lib/muldi3.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libgcc.h" + +#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2)) +#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1)) +#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2)) + +#define umul_ppmm(w1, w0, u, v) \ + do { \ + UWtype __x0, __x1, __x2, __x3; \ + UHWtype __ul, __vl, __uh, __vh; \ + __ul = __ll_lowpart(u); \ + __uh = __ll_highpart(u); \ + __vl = __ll_lowpart(v); \ + __vh = __ll_highpart(v); \ + __x0 = (UWtype) __ul * __vl; \ + __x1 = (UWtype) __ul * __vh; \ + __x2 = (UWtype) __uh * __vl; \ + __x3 = (UWtype) __uh * __vh; \ + __x1 += __ll_highpart(__x0); \ + __x1 += __x2; \ + if (__x1 < __x2) \ + __x3 += __ll_B; \ + (w1) = __x3 + __ll_highpart(__x1); \ + (w0) = __ll_lowpart(__x1) * __ll_B + __ll_lowpart(__x0); \ + } while (0) + +#define __umulsidi3(u, v) ( \ + { \ + DWunion __w; \ + umul_ppmm(__w.s.high, __w.s.low, u, v); \ + __w.ll; } \ + ) + +DWtype __muldi3(DWtype u, DWtype v) +{ + const DWunion uu = {.ll = u}; + const DWunion vv = {.ll = v}; + DWunion w = {.ll = __umulsidi3(uu.s.low, vv.s.low)}; + + w.s.high += ((UWtype) uu.s.low * (UWtype) vv.s.high + + (UWtype) uu.s.high * (UWtype) vv.s.low); + + return w.ll; +} diff --git a/arch/h8300/lib/mulsi3.S b/arch/h8300/lib/mulsi3.S new file mode 100644 index 000000000..5a062fd29 --- /dev/null +++ b/arch/h8300/lib/mulsi3.S @@ -0,0 +1,39 @@ +; SPDX-License-Identifier: GPL-2.0 +; +; mulsi3 for H8/300H - based on Renesas SH implementation +; +; by Toshiyasu Morita +; +; Old code: +; +; 16b * 16b = 372 states (worst case) +; 32b * 32b = 724 states (worst case) +; +; New code: +; +; 16b * 16b = 48 states +; 16b * 32b = 72 states +; 32b * 32b = 92 states +; + + .global __mulsi3 +__mulsi3: + mov.w r1,r2 ; ( 2 states) b * d + mulxu r0,er2 ; (22 states) + + mov.w e0,r3 ; ( 2 states) a * d + beq L_skip1 ; ( 4 states) + mulxu r1,er3 ; (22 states) + add.w r3,e2 ; ( 2 states) + +L_skip1: + mov.w e1,r3 ; ( 2 states) c * b + beq L_skip2 ; ( 4 states) + mulxu r0,er3 ; (22 states) + add.w r3,e2 ; ( 2 states) + +L_skip2: + mov.l er2,er0 ; ( 2 states) + rts ; (10 states) + + .end diff --git a/arch/h8300/lib/strncpy.S b/arch/h8300/lib/strncpy.S new file mode 100644 index 000000000..8b65d7c47 --- /dev/null +++ b/arch/h8300/lib/strncpy.S @@ -0,0 +1,35 @@ +;;; SPDX-License-Identifier: GPL-2.0 +;;; strncpy.S + +#include <asm/linkage.h> + + .text +.global strncpy_from_user + +;;; long strncpy_from_user(void *to, void *from, size_t n) +strncpy_from_user: + mov.l er2,er2 + bne 1f + sub.l er0,er0 + rts +1: + mov.l er4,@-sp + sub.l er3,er3 +2: + mov.b @er1+,r4l + mov.b r4l,@er0 + adds #1,er0 + beq 3f + inc.l #1,er3 + dec.l #1,er2 + bne 2b +3: + dec.l #1,er2 +4: + mov.b r4l,@er0 + adds #1,er0 + dec.l #1,er2 + bne 4b + mov.l er3,er0 + mov.l @sp+,er4 + rts diff --git a/arch/h8300/lib/ucmpdi2.c b/arch/h8300/lib/ucmpdi2.c new file mode 100644 index 000000000..9f646885f --- /dev/null +++ b/arch/h8300/lib/ucmpdi2.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libgcc.h" + +word_type __ucmpdi2(DWtype a, DWtype b) +{ + const DWunion au = {.ll = a}; + const DWunion bu = {.ll = b}; + + if ((UWtype) au.s.high < (UWtype) bu.s.high) + return 0; + else if ((UWtype) au.s.high > (UWtype) bu.s.high) + return 2; + if ((UWtype) au.s.low < (UWtype) bu.s.low) + return 0; + else if ((UWtype) au.s.low > (UWtype) bu.s.low) + return 2; + return 1; +} diff --git a/arch/h8300/lib/udivsi3.S b/arch/h8300/lib/udivsi3.S new file mode 100644 index 000000000..b810aba8e --- /dev/null +++ b/arch/h8300/lib/udivsi3.S @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "libgcc.h" + + ;; This function also computes the remainder and stores it in er3. + .global __udivsi3 +__udivsi3: + mov.w A1E,A1E ; denominator top word 0? + bne DenHighNonZero + + ; do it the easy way, see page 107 in manual + mov.w A0E,A2 + extu.l A2P + divxu.w A1,A2P + mov.w A2E,A0E + divxu.w A1,A0P + mov.w A0E,A3 + mov.w A2,A0E + extu.l A3P + rts + + ; er0 = er0 / er1 + ; er3 = er0 % er1 + ; trashes er1 er2 + ; expects er1 >= 2^16 +DenHighNonZero: + mov.l er0,er3 + mov.l er1,er2 +#ifdef CONFIG_CPU_H8300H +divmod_L21: + shlr.l er0 + shlr.l er2 ; make divisor < 2^16 + mov.w e2,e2 + bne divmod_L21 +#else + shlr.l #2,er2 ; make divisor < 2^16 + mov.w e2,e2 + beq divmod_L22A +divmod_L21: + shlr.l #2,er0 +divmod_L22: + shlr.l #2,er2 ; make divisor < 2^16 + mov.w e2,e2 + bne divmod_L21 +divmod_L22A: + rotxl.w r2 + bcs divmod_L23 + shlr.l er0 + bra divmod_L24 +divmod_L23: + rotxr.w r2 + shlr.l #2,er0 +divmod_L24: +#endif + ;; At this point, + ;; er0 contains shifted dividend + ;; er1 contains divisor + ;; er2 contains shifted divisor + ;; er3 contains dividend, later remainder + divxu.w r2,er0 ; r0 now contains the approximate quotient (AQ) + extu.l er0 + beq divmod_L25 + subs #1,er0 ; er0 = AQ - 1 + mov.w e1,r2 + mulxu.w r0,er2 ; er2 = upper (AQ - 1) * divisor + sub.w r2,e3 ; dividend - 65536 * er2 + mov.w r1,r2 + mulxu.w r0,er2 ; compute er3 = remainder (tentative) + sub.l er2,er3 ; er3 = dividend - (AQ - 1) * divisor +divmod_L25: + cmp.l er1,er3 ; is divisor < remainder? + blo divmod_L26 + adds #1,er0 + sub.l er1,er3 ; correct the remainder +divmod_L26: + rts + + .end diff --git a/arch/h8300/mm/Makefile b/arch/h8300/mm/Makefile new file mode 100644 index 000000000..e85b5c91f --- /dev/null +++ b/arch/h8300/mm/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the linux h8300-specific parts of the memory manager. +# + +obj-y := init.o fault.o memory.o diff --git a/arch/h8300/mm/fault.c b/arch/h8300/mm/fault.c new file mode 100644 index 000000000..b465441f4 --- /dev/null +++ b/arch/h8300/mm/fault.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/h8300/mm/fault.c + * + * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, + * Copyright (C) 2000 Lineo, Inc. (www.lineo.com) + * + * Based on: + * + * linux/arch/m68knommu/mm/fault.c + * linux/arch/m68k/mm/fault.c + * + * Copyright (C) 1995 Hamish Macdonald + */ + +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/ptrace.h> + + +void die(const char *str, struct pt_regs *fp, unsigned long err); + +/* + * This routine handles page faults. It determines the problem, and + * then passes it off to one of the appropriate routines. + * + * error_code: + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write + * + * If this routine detects a bad access, it returns 1, otherwise it + * returns 0. + */ +asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, + unsigned long error_code) +{ +#ifdef DEBUG + pr_debug("regs->sr=%#x, regs->pc=%#lx, address=%#lx, %ld\n", + regs->sr, regs->pc, address, error_code); +#endif + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + if ((unsigned long) address < PAGE_SIZE) + pr_alert("Unable to handle kernel NULL pointer dereference"); + else + pr_alert("Unable to handle kernel access"); + printk(" at virtual address %08lx\n", address); + if (!user_mode(regs)) + die("Oops", regs, error_code); + make_task_dead(SIGKILL); + + return 1; +} diff --git a/arch/h8300/mm/init.c b/arch/h8300/mm/init.c new file mode 100644 index 000000000..1f3b345d6 --- /dev/null +++ b/arch/h8300/mm/init.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/h8300/mm/init.c + * + * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, + * Kenneth Albanowski <kjahds@kjahds.com>, + * Copyright (C) 2000 Lineo, Inc. (www.lineo.com) + * + * Based on: + * + * linux/arch/m68knommu/mm/init.c + * linux/arch/m68k/mm/init.c + * + * Copyright (C) 1995 Hamish Macdonald + * + * JAN/1999 -- hacked to support ColdFire (gerg@snapgear.com) + * DEC/2000 -- linux 2.4 support <davidm@snapgear.com> + */ + +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/init.h> +#include <linux/highmem.h> +#include <linux/pagemap.h> +#include <linux/memblock.h> +#include <linux/gfp.h> + +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/page.h> +#include <asm/sections.h> + +/* + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +unsigned long empty_zero_page; + +/* + * paging_init() continues the virtual memory environment setup which + * was begun by the code in arch/head.S. + * The parameters are pointers to where to stick the starting and ending + * addresses of available kernel virtual memory. + */ +void __init paging_init(void) +{ + /* + * Make sure start_mem is page aligned, otherwise bootmem and + * page_alloc get different views og the world. + */ + unsigned long start_mem = PAGE_ALIGN(memory_start); + unsigned long end_mem = memory_end & PAGE_MASK; + + pr_debug("start_mem is %#lx\nvirtual_end is %#lx\n", + start_mem, end_mem); + + /* + * Initialize the bad page table and bad page to point + * to a couple of allocated pages. + */ + empty_zero_page = (unsigned long)memblock_alloc(PAGE_SIZE, PAGE_SIZE); + if (!empty_zero_page) + panic("%s: Failed to allocate %lu bytes align=0x%lx\n", + __func__, PAGE_SIZE, PAGE_SIZE); + + /* + * Set up SFC/DFC registers (user data space). + */ + set_fs(USER_DS); + + pr_debug("before free_area_init\n"); + + pr_debug("free_area_init -> start_mem is %#lx\nvirtual_end is %#lx\n", + start_mem, end_mem); + + { + unsigned long max_zone_pfn[MAX_NR_ZONES] = {0, }; + + max_zone_pfn[ZONE_NORMAL] = end_mem >> PAGE_SHIFT; + free_area_init(max_zone_pfn); + } +} + +void __init mem_init(void) +{ + pr_devel("Mem_init: start=%lx, end=%lx\n", memory_start, memory_end); + + high_memory = (void *) (memory_end & PAGE_MASK); + max_mapnr = MAP_NR(high_memory); + + /* this will put all low memory onto the freelists */ + memblock_free_all(); + + mem_init_print_info(NULL); +} diff --git a/arch/h8300/mm/memory.c b/arch/h8300/mm/memory.c new file mode 100644 index 000000000..4a60e2b5e --- /dev/null +++ b/arch/h8300/mm/memory.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/h8300/mm/memory.c + * + * Copyright (C) 2002 Yoshinori Sato <ysato@users.sourceforge.jp>, + * + * Based on: + * + * linux/arch/m68knommu/mm/memory.c + * + * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>, + * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) + * + * Based on: + * + * linux/arch/m68k/mm/memory.c + * + * Copyright (C) 1995 Hamish Macdonald + */ + +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/page.h> +#include <asm/traps.h> +#include <asm/io.h> + +void cache_clear(unsigned long paddr, int len) +{ +} + + +void cache_push(unsigned long paddr, int len) +{ +} + +void cache_push_v(unsigned long vaddr, int len) +{ +} + +/* + * Map some physical address range into the kernel address space. + */ + +unsigned long kernel_map(unsigned long paddr, unsigned long size, + int nocacheflag, unsigned long *memavailp) +{ + return paddr; +} |