On Mon, Jul 5, 2021 at 1:46 AM Geert Uytterhoeven <geert@xxxxxxxxxxxxxx> wrote:
Probably this should be select SET_FS if CPU_HAS_ADDRESS_SPACES
Actually, I don't think m68k has a single real "set_fs()" at all, and it should just be converted as-is to not use CONFIG_SET_FS. Yes, there is a "set_fs()" function, but none of the remaining uses actually are the traditional kernel style of "use kernel addresses as user addresses". So as far as the *kernel* is concerned, m68k already looks like a no-SET_FS architecture, and "set-fs()" is purely a syntactic thing. So I think the right thing to do looks something like this: - make the rule be that SFC/DFC is always normally USER_DATA - the special m68k sequences that need to play with special segments will always do preempt_disable(); set_segment(..whatever segment they need..); .. do the special operation .. set_segment(USER_DATA); preempt_enable(); - set_fs() goes away entirely, because the user access functions always work on USER_DATA and SFC/DFC is always right for them. Anyway, I'm attaching a COMPLETELY UNTESTED AND ALMOST CERTAINLY VERY VERY BROKEN patch that is likely not at all correct, but shows what I think the solution should be. The important thing is really just the removal of SET_FS entirely, the rest is me winging it. NOTE! The above very much assumes that all the special non-USER_DATA accesses can always be done with preemption disabled. Why? Because I also made the context switching always just save USER_DATA as the segment. I didn't *remove* the segment switching - because I'm not sure if that "just disable preemption across things that do segment games" is actually valid. So *if* that preempt_disable/enable is ok, then the segment switching can just be removed entirely at context switch time. And if it is *not* ok, then the preempt_disable/enable should go away, and the context switch should save/restore the actual SFC/DFC value. Again - let me be very very clear: the only m68k I've ever used was a 68008. It didn't have segments. This patch is COMPLETE GARBAGE. Do not trust it. Do not use it for anything but a "Linus suggests maybe something along these lines could work". This has not even been build-tested, and I'm pretty sure it won't build as-is. It really is a "Linus did some pattern matching and a few grep's". Oh, and if the magical SFC/DFC games can happen in interrupts, then the "preempt_disable/enable" actually needs to be a full interrupt disable/enable, or the code needs to re-introduce that "save old value, restore it". So that's another assumption this example patch makes - that the SFC/DFC games only happen in process context. Which may be complete garbage too. Did I mention that I think this patch is garbage? Because it really is. Linus
arch/m68k/Kconfig | 1 - arch/m68k/include/asm/segment.h | 21 +++------------------ arch/m68k/include/asm/tlbflush.h | 7 ++++--- arch/m68k/kernel/process.c | 2 +- arch/m68k/kernel/traps.c | 16 ++++++++-------- arch/m68k/mm/cache.c | 23 ++++++++++++++--------- arch/m68k/mm/init.c | 2 +- arch/m68k/mm/motorola.c | 2 +- arch/m68k/sun3/config.c | 2 +- arch/m68k/sun3/mmu_emu.c | 7 ++++--- 10 files changed, 37 insertions(+), 46 deletions(-) diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index 96989ad46f66..3a83013f0a96 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -32,7 +32,6 @@ config M68K select NO_DMA if !MMU && !COLDFIRE select OLD_SIGACTION select OLD_SIGSUSPEND3 - select SET_FS select UACCESS_MEMCPY if !MMU select VIRT_TO_BUS select ZONE_DMA diff --git a/arch/m68k/include/asm/segment.h b/arch/m68k/include/asm/segment.h index 2b5e68a71ef7..a9fabf7d2a2c 100644 --- a/arch/m68k/include/asm/segment.h +++ b/arch/m68k/include/asm/segment.h @@ -26,19 +26,9 @@ typedef struct { #ifdef CONFIG_CPU_HAS_ADDRESS_SPACES /* - * Get/set the SFC/DFC registers for MOVES instructions + * Set the SFC/DFC registers for MOVES instructions */ -#define USER_DS MAKE_MM_SEG(__USER_DS) -#define KERNEL_DS MAKE_MM_SEG(__KERNEL_DS) - -static inline mm_segment_t get_fs(void) -{ - mm_segment_t _v; - __asm__ ("movec %/dfc,%0":"=r" (_v.seg):); - return _v; -} - -static inline void set_fs(mm_segment_t val) +static inline void set_segment(mm_segment_t val) { __asm__ __volatile__ ("movec %0,%/sfc\n\t" "movec %0,%/dfc\n\t" @@ -46,14 +36,9 @@ static inline void set_fs(mm_segment_t val) } #else -#define USER_DS MAKE_MM_SEG(TASK_SIZE) -#define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF) -#define get_fs() (current_thread_info()->addr_limit) -#define set_fs(x) (current_thread_info()->addr_limit = (x)) +#define set_segment(x) ((void)(x)) #endif -#define uaccess_kernel() (get_fs().seg == KERNEL_DS.seg) - #endif /* __ASSEMBLY__ */ #endif /* _M68K_SEGMENT_H */ diff --git a/arch/m68k/include/asm/tlbflush.h b/arch/m68k/include/asm/tlbflush.h index a6318ccd308f..3c9e35c7e93f 100644 --- a/arch/m68k/include/asm/tlbflush.h +++ b/arch/m68k/include/asm/tlbflush.h @@ -13,13 +13,14 @@ static inline void flush_tlb_kernel_page(void *addr) if (CPU_IS_COLDFIRE) { mmu_write(MMUOR, MMUOR_CNL); } else if (CPU_IS_040_OR_060) { - mm_segment_t old_fs = get_fs(); - set_fs(KERNEL_DS); + preempt_disable(); + set_segment(SUPER_DATA); __asm__ __volatile__(".chip 68040\n\t" "pflush (%0)\n\t" ".chip 68k" : : "a" (addr)); - set_fs(old_fs); + set_segment(USER_DATA); + preempt_enable(); } else if (CPU_IS_020_OR_030) __asm__ __volatile__("pflush #4,#4,(%0)" : : "a" (addr)); } diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index db49f9091711..a176d00ffc97 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -155,7 +155,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, unsigned long arg, * Must save the current SFC/DFC value, NOT the value when * the parent was last descheduled - RGH 10-08-96 */ - p->thread.fs = get_fs().seg; + p->thread.fs = USER_DATA.seg; if (unlikely(p->flags & (PF_KTHREAD | PF_IO_WORKER))) { /* kernel thread */ diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index 9e1261462bcc..85863181466c 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -181,9 +181,9 @@ static inline void access_error060 (struct frame *fp) static inline unsigned long probe040(int iswrite, unsigned long addr, int wbs) { unsigned long mmusr; - mm_segment_t old_fs = get_fs(); - set_fs(MAKE_MM_SEG(wbs)); + preempt_disable(); + set_segment(MAKE_MM_SEG(wbs)); if (iswrite) asm volatile (".chip 68040; ptestw (%0); .chip 68k" : : "a" (addr)); @@ -192,7 +192,8 @@ static inline unsigned long probe040(int iswrite, unsigned long addr, int wbs) asm volatile (".chip 68040; movec %%mmusr,%0; .chip 68k" : "=r" (mmusr)); - set_fs(old_fs); + set_segment(USER_DATA); + preempt_enable(); return mmusr; } @@ -201,10 +202,9 @@ static inline int do_040writeback1(unsigned short wbs, unsigned long wba, unsigned long wbd) { int res = 0; - mm_segment_t old_fs = get_fs(); - /* set_fs can not be moved, otherwise put_user() may oops */ - set_fs(MAKE_MM_SEG(wbs)); + preempt_disable(); + set_segment(MAKE_MM_SEG(wbs)); switch (wbs & WBSIZ_040) { case BA_SIZE_BYTE: @@ -218,8 +218,8 @@ static inline int do_040writeback1(unsigned short wbs, unsigned long wba, break; } - /* set_fs can not be moved, otherwise put_user() may oops */ - set_fs(old_fs); + set_segment(USER_DATA); + preempt_enable(); pr_debug("do_040writeback1, res=%d\n", res); diff --git a/arch/m68k/mm/cache.c b/arch/m68k/mm/cache.c index b486c0889eec..80e311aa924d 100644 --- a/arch/m68k/mm/cache.c +++ b/arch/m68k/mm/cache.c @@ -12,7 +12,7 @@ #include <asm/traps.h> -static unsigned long virt_to_phys_slow(unsigned long vaddr) +static unsigned long virt_to_phys_slow(unsigned long vaddr, mm_segment_t seg) { if (CPU_IS_060) { unsigned long paddr; @@ -55,7 +55,7 @@ static unsigned long virt_to_phys_slow(unsigned long vaddr) asm volatile ("ptestr %3,%2@,#7,%0\n\t" "pmove %%psr,%1" : "=a&" (descaddr), "=m" (mmusr) - : "a" (vaddr), "d" (get_fs().seg)); + : "a" (vaddr), "d" (seg.seg)); if (mmusr & (MMU_I|MMU_B|MMU_L)) return 0; descaddr = phys_to_virt((unsigned long)descaddr); @@ -73,7 +73,7 @@ static unsigned long virt_to_phys_slow(unsigned long vaddr) /* Push n pages at kernel virtual address and clear the icache */ /* RZ: use cpush %bc instead of cpush %dc, cinv %ic */ -void flush_icache_user_range(unsigned long address, unsigned long endaddr) +static void do_flush_icache_user_range(unsigned long address, unsigned long endaddr, mm_segment_t seg) { if (CPU_IS_COLDFIRE) { unsigned long start, end; @@ -92,7 +92,7 @@ void flush_icache_user_range(unsigned long address, unsigned long endaddr) ".chip 68040\n\t" "cpushp %%bc,(%0)\n\t" ".chip 68k" - : : "a" (virt_to_phys_slow(address))); + : : "a" (virt_to_phys_slow(address, seg))); address += PAGE_SIZE; } while (address < endaddr); } else { @@ -105,13 +105,18 @@ void flush_icache_user_range(unsigned long address, unsigned long endaddr) } } -void flush_icache_range(unsigned long address, unsigned long endaddr) +void flush_icache_user_range(unsigned long address, unsigned long endaddr) { - mm_segment_t old_fs = get_fs(); + do_flush_icache_range(address, endaddr, USER_DATA); +} - set_fs(KERNEL_DS); - flush_icache_user_range(address, endaddr); - set_fs(old_fs); +void flush_icache_range(unsigned long address, unsigned long endaddr) +{ + preempt_disable(); + set_segment(SUPER_DATA); + do_flush_icache_range(address, endaddr, SUPER_DATA); + set_segment(USER_DATA); + preempt_enable(); } EXPORT_SYMBOL(flush_icache_range); diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index 5d749e188246..4a7a56139013 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -76,7 +76,7 @@ void __init paging_init(void) /* * Set up SFC/DFC registers (user data space). */ - set_fs (USER_DS); + set_segment(USER_DATA); max_zone_pfn[ZONE_DMA] = end_mem >> PAGE_SHIFT; free_area_init(max_zone_pfn); diff --git a/arch/m68k/mm/motorola.c b/arch/m68k/mm/motorola.c index 3a653f0a4188..7af7d5a1ac77 100644 --- a/arch/m68k/mm/motorola.c +++ b/arch/m68k/mm/motorola.c @@ -467,7 +467,7 @@ void __init paging_init(void) /* * Set up SFC/DFC registers */ - set_fs(KERNEL_DS); + set_segment(USER_DATA); #ifdef DEBUG printk ("before free_area_init\n"); diff --git a/arch/m68k/sun3/config.c b/arch/m68k/sun3/config.c index f7dd47232b6c..849457dbb424 100644 --- a/arch/m68k/sun3/config.c +++ b/arch/m68k/sun3/config.c @@ -89,7 +89,7 @@ void __init sun3_init(void) sun3_reserved_pmeg[249] = 1; sun3_reserved_pmeg[252] = 1; sun3_reserved_pmeg[253] = 1; - set_fs(KERNEL_DS); + set_segment(USER_DATA); } /* Without this, Bad Things happen when something calls arch_reset. */ diff --git a/arch/m68k/sun3/mmu_emu.c b/arch/m68k/sun3/mmu_emu.c index 7aa879b7c7ff..2bcf7e9822e6 100644 --- a/arch/m68k/sun3/mmu_emu.c +++ b/arch/m68k/sun3/mmu_emu.c @@ -191,14 +191,15 @@ void __init mmu_emu_init(unsigned long bootmem_end) for(seg = 0; seg < PAGE_OFFSET; seg += SUN3_PMEG_SIZE) sun3_put_segmap(seg, SUN3_INVALID_PMEG); - set_fs(MAKE_MM_SEG(3)); + preempt_disable(); + set_segment(MAKE_MM_SEG(3)); for(seg = 0; seg < 0x10000000; seg += SUN3_PMEG_SIZE) { i = sun3_get_segmap(seg); for(j = 1; j < CONTEXTS_NUM; j++) (*(romvec->pv_setctxt))(j, (void *)seg, i); } - set_fs(KERNEL_DS); - + set_segment(USER_DATA); + preempt_enable(); } /* erase the mappings for a dead context. Uses the pg_dir for hints