Skip to content

Commit 91481c9

Browse files
committed
Merge tag 'parisc-for-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux
Pull parisc architecture fixes from Helge Deller: "The current exception handler, which helps on kernel accesses to userspace, may exhibit data corruption. The problem is that it is not guaranteed that the compiler will use the processor register we specified in the source code, but may choose another register which then will lead to silent register- and data corruption. To fix this issue we now use another strategy to help the exception handler to always find and set the error code into the correct CPU register. The other fixes are small: fixing CPU hotplug bringup, fix the page alignment of the RO_DATA section, added a check for the calculated cache stride and fix possible hangups when printing longer output at bootup when running on serial console. Most of the patches are tagged for stable series. - Fix random data corruption triggered by exception handler - Fix crash when setting up BTLB at CPU bringup - Prevent hung tasks when printing inventory on serial console - Make RO_DATA page aligned in vmlinux.lds.S - Add check for valid cache stride size" * tag 'parisc-for-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux: parisc: BTLB: Fix crash when setting up BTLB at CPU bringup parisc: Fix random data corruption from exception handler parisc: Drop unneeded semicolon in parse_tree_node() parisc: Prevent hung tasks when printing inventory on serial console parisc: Check for valid stride size for cache flushes parisc: Make RO_DATA page aligned in vmlinux.lds.S
2 parents a412682 + 913b9d4 commit 91481c9

File tree

10 files changed

+118
-74
lines changed

10 files changed

+118
-74
lines changed

arch/parisc/Kconfig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ config PARISC
2525
select RTC_DRV_GENERIC
2626
select INIT_ALL_POSSIBLE
2727
select BUG
28-
select BUILDTIME_TABLE_SORT
2928
select HAVE_KERNEL_UNCOMPRESSED
3029
select HAVE_PCI
3130
select HAVE_PERF_EVENTS

arch/parisc/include/asm/assembly.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@
576576
.section __ex_table,"aw" ! \
577577
.align 4 ! \
578578
.word (fault_addr - .), (except_addr - .) ! \
579+
or %r0,%r0,%r0 ! \
579580
.previous
580581

581582

arch/parisc/include/asm/extable.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __PARISC_EXTABLE_H
3+
#define __PARISC_EXTABLE_H
4+
5+
#include <asm/ptrace.h>
6+
#include <linux/compiler.h>
7+
8+
/*
9+
* The exception table consists of three addresses:
10+
*
11+
* - A relative address to the instruction that is allowed to fault.
12+
* - A relative address at which the program should continue (fixup routine)
13+
* - An asm statement which specifies which CPU register will
14+
* receive -EFAULT when an exception happens if the lowest bit in
15+
* the fixup address is set.
16+
*
17+
* Note: The register specified in the err_opcode instruction will be
18+
* modified at runtime if a fault happens. Register %r0 will be ignored.
19+
*
20+
* Since relative addresses are used, 32bit values are sufficient even on
21+
* 64bit kernel.
22+
*/
23+
24+
struct pt_regs;
25+
int fixup_exception(struct pt_regs *regs);
26+
27+
#define ARCH_HAS_RELATIVE_EXTABLE
28+
struct exception_table_entry {
29+
int insn; /* relative address of insn that is allowed to fault. */
30+
int fixup; /* relative address of fixup routine */
31+
int err_opcode; /* sample opcode with register which holds error code */
32+
};
33+
34+
#define ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr, opcode )\
35+
".section __ex_table,\"aw\"\n" \
36+
".align 4\n" \
37+
".word (" #fault_addr " - .), (" #except_addr " - .)\n" \
38+
opcode "\n" \
39+
".previous\n"
40+
41+
/*
42+
* ASM_EXCEPTIONTABLE_ENTRY_EFAULT() creates a special exception table entry
43+
* (with lowest bit set) for which the fault handler in fixup_exception() will
44+
* load -EFAULT on fault into the register specified by the err_opcode instruction,
45+
* and zeroes the target register in case of a read fault in get_user().
46+
*/
47+
#define ASM_EXCEPTIONTABLE_VAR(__err_var) \
48+
int __err_var = 0
49+
#define ASM_EXCEPTIONTABLE_ENTRY_EFAULT( fault_addr, except_addr, register )\
50+
ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr + 1, "or %%r0,%%r0," register)
51+
52+
static inline void swap_ex_entry_fixup(struct exception_table_entry *a,
53+
struct exception_table_entry *b,
54+
struct exception_table_entry tmp,
55+
int delta)
56+
{
57+
a->fixup = b->fixup + delta;
58+
b->fixup = tmp.fixup - delta;
59+
a->err_opcode = b->err_opcode;
60+
b->err_opcode = tmp.err_opcode;
61+
}
62+
#define swap_ex_entry_fixup swap_ex_entry_fixup
63+
64+
#endif

arch/parisc/include/asm/special_insns.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"copy %%r0,%0\n" \
99
"8:\tlpa %%r0(%1),%0\n" \
1010
"9:\n" \
11-
ASM_EXCEPTIONTABLE_ENTRY(8b, 9b) \
11+
ASM_EXCEPTIONTABLE_ENTRY(8b, 9b, \
12+
"or %%r0,%%r0,%%r0") \
1213
: "=&r" (pa) \
1314
: "r" (va) \
1415
: "memory" \
@@ -22,7 +23,8 @@
2223
"copy %%r0,%0\n" \
2324
"8:\tlpa %%r0(%%sr3,%1),%0\n" \
2425
"9:\n" \
25-
ASM_EXCEPTIONTABLE_ENTRY(8b, 9b) \
26+
ASM_EXCEPTIONTABLE_ENTRY(8b, 9b, \
27+
"or %%r0,%%r0,%%r0") \
2628
: "=&r" (pa) \
2729
: "r" (va) \
2830
: "memory" \

arch/parisc/include/asm/uaccess.h

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88
#include <asm/page.h>
99
#include <asm/cache.h>
10+
#include <asm/extable.h>
1011

1112
#include <linux/bug.h>
1213
#include <linux/string.h>
@@ -26,37 +27,6 @@
2627
#define STD_USER(sr, x, ptr) __put_user_asm(sr, "std", x, ptr)
2728
#endif
2829

29-
/*
30-
* The exception table contains two values: the first is the relative offset to
31-
* the address of the instruction that is allowed to fault, and the second is
32-
* the relative offset to the address of the fixup routine. Since relative
33-
* addresses are used, 32bit values are sufficient even on 64bit kernel.
34-
*/
35-
36-
#define ARCH_HAS_RELATIVE_EXTABLE
37-
struct exception_table_entry {
38-
int insn; /* relative address of insn that is allowed to fault. */
39-
int fixup; /* relative address of fixup routine */
40-
};
41-
42-
#define ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr )\
43-
".section __ex_table,\"aw\"\n" \
44-
".align 4\n" \
45-
".word (" #fault_addr " - .), (" #except_addr " - .)\n\t" \
46-
".previous\n"
47-
48-
/*
49-
* ASM_EXCEPTIONTABLE_ENTRY_EFAULT() creates a special exception table entry
50-
* (with lowest bit set) for which the fault handler in fixup_exception() will
51-
* load -EFAULT into %r29 for a read or write fault, and zeroes the target
52-
* register in case of a read fault in get_user().
53-
*/
54-
#define ASM_EXCEPTIONTABLE_REG 29
55-
#define ASM_EXCEPTIONTABLE_VAR(__variable) \
56-
register long __variable __asm__ ("r29") = 0
57-
#define ASM_EXCEPTIONTABLE_ENTRY_EFAULT( fault_addr, except_addr )\
58-
ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr + 1)
59-
6030
#define __get_user_internal(sr, val, ptr) \
6131
({ \
6232
ASM_EXCEPTIONTABLE_VAR(__gu_err); \
@@ -83,7 +53,7 @@ struct exception_table_entry {
8353
\
8454
__asm__("1: " ldx " 0(%%sr%2,%3),%0\n" \
8555
"9:\n" \
86-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
56+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%1") \
8757
: "=r"(__gu_val), "+r"(__gu_err) \
8858
: "i"(sr), "r"(ptr)); \
8959
\
@@ -115,8 +85,8 @@ struct exception_table_entry {
11585
"1: ldw 0(%%sr%2,%3),%0\n" \
11686
"2: ldw 4(%%sr%2,%3),%R0\n" \
11787
"9:\n" \
118-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
119-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b) \
88+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%1") \
89+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b, "%1") \
12090
: "=&r"(__gu_tmp.l), "+r"(__gu_err) \
12191
: "i"(sr), "r"(ptr)); \
12292
\
@@ -174,7 +144,7 @@ struct exception_table_entry {
174144
__asm__ __volatile__ ( \
175145
"1: " stx " %1,0(%%sr%2,%3)\n" \
176146
"9:\n" \
177-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
147+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%0") \
178148
: "+r"(__pu_err) \
179149
: "r"(x), "i"(sr), "r"(ptr))
180150

@@ -186,15 +156,14 @@ struct exception_table_entry {
186156
"1: stw %1,0(%%sr%2,%3)\n" \
187157
"2: stw %R1,4(%%sr%2,%3)\n" \
188158
"9:\n" \
189-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
190-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b) \
159+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%0") \
160+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b, "%0") \
191161
: "+r"(__pu_err) \
192162
: "r"(__val), "i"(sr), "r"(ptr)); \
193163
} while (0)
194164

195165
#endif /* !defined(CONFIG_64BIT) */
196166

197-
198167
/*
199168
* Complex access routines -- external declarations
200169
*/
@@ -216,7 +185,4 @@ unsigned long __must_check raw_copy_from_user(void *dst, const void __user *src,
216185
#define INLINE_COPY_TO_USER
217186
#define INLINE_COPY_FROM_USER
218187

219-
struct pt_regs;
220-
int fixup_exception(struct pt_regs *regs);
221-
222188
#endif /* __PARISC_UACCESS_H */

arch/parisc/kernel/cache.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ int pa_serialize_tlb_flushes __ro_after_init;
5858

5959
struct pdc_cache_info cache_info __ro_after_init;
6060
#ifndef CONFIG_PA20
61-
struct pdc_btlb_info btlb_info __ro_after_init;
61+
struct pdc_btlb_info btlb_info;
6262
#endif
6363

6464
DEFINE_STATIC_KEY_TRUE(parisc_has_cache);
@@ -264,6 +264,10 @@ parisc_cache_init(void)
264264
icache_stride = CAFL_STRIDE(cache_info.ic_conf);
265265
#undef CAFL_STRIDE
266266

267+
/* stride needs to be non-zero, otherwise cache flushes will not work */
268+
WARN_ON(cache_info.dc_size && dcache_stride == 0);
269+
WARN_ON(cache_info.ic_size && icache_stride == 0);
270+
267271
if ((boot_cpu_data.pdc.capabilities & PDC_MODEL_NVA_MASK) ==
268272
PDC_MODEL_NVA_UNSUPPORTED) {
269273
printk(KERN_WARNING "parisc_cache_init: Only equivalent aliasing supported!\n");
@@ -850,7 +854,7 @@ SYSCALL_DEFINE3(cacheflush, unsigned long, addr, unsigned long, bytes,
850854
#endif
851855
" fic,m %3(%4,%0)\n"
852856
"2: sync\n"
853-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 2b)
857+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 2b, "%1")
854858
: "+r" (start), "+r" (error)
855859
: "r" (end), "r" (dcache_stride), "i" (SR_USER));
856860
}
@@ -865,7 +869,7 @@ SYSCALL_DEFINE3(cacheflush, unsigned long, addr, unsigned long, bytes,
865869
#endif
866870
" fdc,m %3(%4,%0)\n"
867871
"2: sync\n"
868-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 2b)
872+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 2b, "%1")
869873
: "+r" (start), "+r" (error)
870874
: "r" (end), "r" (icache_stride), "i" (SR_USER));
871875
}

arch/parisc/kernel/drivers.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ parse_tree_node(struct device *parent, int index, struct hardware_path *modpath)
742742
};
743743

744744
if (device_for_each_child(parent, &recurse_data, descend_children))
745-
{ /* nothing */ };
745+
{ /* nothing */ }
746746

747747
return d.dev;
748748
}
@@ -1004,6 +1004,9 @@ static __init int qemu_print_iodc_data(struct device *lin_dev, void *data)
10041004

10051005
pr_info("\n");
10061006

1007+
/* Prevent hung task messages when printing on serial console */
1008+
cond_resched();
1009+
10071010
pr_info("#define HPA_%08lx_DESCRIPTION \"%s\"\n",
10081011
hpa, parisc_hardware_description(&dev->id));
10091012

arch/parisc/kernel/unaligned.c

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ static int emulate_ldh(struct pt_regs *regs, int toreg)
120120
"2: ldbs 1(%%sr1,%3), %0\n"
121121
" depw %2, 23, 24, %0\n"
122122
"3: \n"
123-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
124-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
123+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b, "%1")
124+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%1")
125125
: "+r" (val), "+r" (ret), "=&r" (temp1)
126126
: "r" (saddr), "r" (regs->isr) );
127127

@@ -152,8 +152,8 @@ static int emulate_ldw(struct pt_regs *regs, int toreg, int flop)
152152
" mtctl %2,11\n"
153153
" vshd %0,%3,%0\n"
154154
"3: \n"
155-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
156-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
155+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b, "%1")
156+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%1")
157157
: "+r" (val), "+r" (ret), "=&r" (temp1), "=&r" (temp2)
158158
: "r" (saddr), "r" (regs->isr) );
159159

@@ -189,8 +189,8 @@ static int emulate_ldd(struct pt_regs *regs, int toreg, int flop)
189189
" mtsar %%r19\n"
190190
" shrpd %0,%%r20,%%sar,%0\n"
191191
"3: \n"
192-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
193-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
192+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b, "%1")
193+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%1")
194194
: "=r" (val), "+r" (ret)
195195
: "0" (val), "r" (saddr), "r" (regs->isr)
196196
: "r19", "r20" );
@@ -209,9 +209,9 @@ static int emulate_ldd(struct pt_regs *regs, int toreg, int flop)
209209
" vshd %0,%R0,%0\n"
210210
" vshd %R0,%4,%R0\n"
211211
"4: \n"
212-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 4b)
213-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 4b)
214-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 4b)
212+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 4b, "%1")
213+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 4b, "%1")
214+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 4b, "%1")
215215
: "+r" (val), "+r" (ret), "+r" (saddr), "=&r" (shift), "=&r" (temp1)
216216
: "r" (regs->isr) );
217217
}
@@ -244,8 +244,8 @@ static int emulate_sth(struct pt_regs *regs, int frreg)
244244
"1: stb %1, 0(%%sr1, %3)\n"
245245
"2: stb %2, 1(%%sr1, %3)\n"
246246
"3: \n"
247-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
248-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
247+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b, "%0")
248+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%0")
249249
: "+r" (ret), "=&r" (temp1)
250250
: "r" (val), "r" (regs->ior), "r" (regs->isr) );
251251

@@ -285,8 +285,8 @@ static int emulate_stw(struct pt_regs *regs, int frreg, int flop)
285285
" stw %%r20,0(%%sr1,%2)\n"
286286
" stw %%r21,4(%%sr1,%2)\n"
287287
"3: \n"
288-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
289-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
288+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b, "%0")
289+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%0")
290290
: "+r" (ret)
291291
: "r" (val), "r" (regs->ior), "r" (regs->isr)
292292
: "r19", "r20", "r21", "r22", "r1" );
@@ -329,10 +329,10 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
329329
"3: std %%r20,0(%%sr1,%2)\n"
330330
"4: std %%r21,8(%%sr1,%2)\n"
331331
"5: \n"
332-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 5b)
333-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 5b)
334-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 5b)
335-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 5b)
332+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 5b, "%0")
333+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 5b, "%0")
334+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 5b, "%0")
335+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 5b, "%0")
336336
: "+r" (ret)
337337
: "r" (val), "r" (regs->ior), "r" (regs->isr)
338338
: "r19", "r20", "r21", "r22", "r1" );
@@ -357,11 +357,11 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
357357
"4: stw %%r1,4(%%sr1,%2)\n"
358358
"5: stw %R1,8(%%sr1,%2)\n"
359359
"6: \n"
360-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 6b)
361-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 6b)
362-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 6b)
363-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 6b)
364-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(5b, 6b)
360+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 6b, "%0")
361+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 6b, "%0")
362+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 6b, "%0")
363+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 6b, "%0")
364+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(5b, 6b, "%0")
365365
: "+r" (ret)
366366
: "r" (val), "r" (regs->ior), "r" (regs->isr)
367367
: "r19", "r20", "r21", "r1" );

arch/parisc/kernel/vmlinux.lds.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ SECTIONS
127127
}
128128
#endif
129129

130-
RO_DATA(8)
130+
RO_DATA(PAGE_SIZE)
131131

132132
/* unwind info */
133133
. = ALIGN(4);

arch/parisc/mm/fault.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,16 @@ int fixup_exception(struct pt_regs *regs)
150150
* Fix up get_user() and put_user().
151151
* ASM_EXCEPTIONTABLE_ENTRY_EFAULT() sets the least-significant
152152
* bit in the relative address of the fixup routine to indicate
153-
* that gr[ASM_EXCEPTIONTABLE_REG] should be loaded with
154-
* -EFAULT to report a userspace access error.
153+
* that the register encoded in the "or %r0,%r0,register"
154+
* opcode should be loaded with -EFAULT to report a userspace
155+
* access error.
155156
*/
156157
if (fix->fixup & 1) {
157-
regs->gr[ASM_EXCEPTIONTABLE_REG] = -EFAULT;
158+
int fault_error_reg = fix->err_opcode & 0x1f;
159+
if (!WARN_ON(!fault_error_reg))
160+
regs->gr[fault_error_reg] = -EFAULT;
161+
pr_debug("Unalignment fixup of register %d at %pS\n",
162+
fault_error_reg, (void*)regs->iaoq[0]);
158163

159164
/* zero target register for get_user() */
160165
if (parisc_acctyp(0, regs->iir) == VM_READ) {

0 commit comments

Comments
 (0)