Changeset 3fcea34 in mainline for kernel


Ignore:
Timestamp:
2024-09-20T12:16:28Z (9 months ago)
Author:
Jiří Zárevúcky <zarevucky.jiri@…>
Branches:
master
Children:
d3109ff
Parents:
2cf8f994
git-author:
Jiří Zárevúcky <zarevucky.jiri@…> (2024-09-20 11:42:13)
git-committer:
Jiří Zárevúcky <zarevucky.jiri@…> (2024-09-20 12:16:28)
Message:

Simplify the SYS_THREAD_CREATE syscall interface

Removed the beefy uarg structure. Instead, the syscall gets two
parameters: %pc (program counter) and %sp (stack pointer). It starts
a thread with those values in corresponding registers, with no other
fuss whatsoever.

libc initializes threads by storing any other needed arguments on
the stack and retrieving them in thread_entry. Importantly, this
includes the address of the
thread_main function which is now
called indirectly to fix dynamic linking issues on some archs.

There's a bit of weirdness on SPARC and IA-64, because of their
stacked register handling. The current solution is that we require
some space *above* the stack pointer to be available for those
architectures. I think for SPARC, it can be made more normal.

For the remaining ones, we can (probably) just set the initial
%sp to the top edge of the stack. There's some lingering offsets
on some archs just because I didn't want to accidentally break
anything. The initial thread bringup should be functionally
unchanged from the previous state, and no binaries are currently
multithreaded except thread1 test, so there should be minimal
risk of breakage. Naturally, I tested all available emulator
builds, save for msim.

Location:
kernel
Files:
21 edited

Legend:

Unmodified
Added
Removed
  • kernel/arch/abs32le/src/userspace.c

    r2cf8f994 r3fcea34  
    3636#include <stdbool.h>
    3737#include <arch.h>
    38 #include <abi/proc/uarg.h>
    3938#include <mm/as.h>
    4039
    41 void userspace(uspace_arg_t *kernel_uarg)
     40uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size)
     41{
     42        return stack_base + stack_size;
     43}
     44
     45void userspace(uintptr_t pc, uintptr_t sp)
    4246{
    4347        /*
  • kernel/arch/amd64/src/userspace.c

    r2cf8f994 r3fcea34  
    3939#include <stdint.h>
    4040#include <arch.h>
    41 #include <abi/proc/uarg.h>
    4241#include <mm/as.h>
     42
     43uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size)
     44{
     45        return stack_base + stack_size;
     46}
    4347
    4448/** Enter userspace
     
    4751 *
    4852 */
    49 void userspace(uspace_arg_t *kernel_uarg)
     53void userspace(uintptr_t pc, uintptr_t sp)
    5054{
    5155        uint64_t rflags = read_rflags();
     
    6064            "pushq %[utext_des]\n"
    6165            "pushq %[entry]\n"
    62             "movq %[uarg], %%rax\n"
    6366
    6467            /* %rdi is defined to hold pcb_ptr - set it to 0 */
     
    6669            "iretq\n"
    6770            :: [udata_des] "i" (GDT_SELECTOR(UDATA_DES) | PL_USER),
    68               [stack_top] "r" (kernel_uarg->uspace_stack +
    69               kernel_uarg->uspace_stack_size),
     71              [stack_top] "r" (sp),
    7072              [rflags] "r" (rflags),
    7173              [utext_des] "i" (GDT_SELECTOR(UTEXT_DES) | PL_USER),
    72               [entry] "r" (kernel_uarg->uspace_entry),
    73               [uarg] "r" (kernel_uarg->uspace_uarg)
    74             : "rax"
     74              [entry] "r" (pc)
    7575        );
    7676
  • kernel/arch/arm32/src/userspace.c

    r2cf8f994 r3fcea34  
    6161} ustate_t;
    6262
     63uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size)
     64{
     65        return stack_base + stack_size;
     66}
     67
    6368/** Change processor mode
    6469 *
     
    6671 *
    6772 */
    68 void userspace(uspace_arg_t *kernel_uarg)
     73void userspace(uintptr_t pc, uintptr_t sp)
    6974{
    70         volatile ustate_t ustate;
    71 
    72         /* set first parameter */
    73         ustate.r0 = kernel_uarg->uspace_uarg;
    74 
    75         /* %r1 is defined to hold pcb_ptr - set it to 0 */
    76         ustate.r1 = 0;
     75        volatile ustate_t ustate = { };
    7776
    7877        /* pass the RAS page address in %r2 */
    7978        ustate.r2 = (uintptr_t) ras_page;
    8079
    81         /* clear other registers */
    82         ustate.r3 = 0;
    83         ustate.r4 = 0;
    84         ustate.r5 = 0;
    85         ustate.r6 = 0;
    86         ustate.r7 = 0;
    87         ustate.r8 = 0;
    88         ustate.r9 = 0;
    89         ustate.r10 = 0;
    90         ustate.r11 = 0;
    91         ustate.r12 = 0;
    92         ustate.lr = 0;
    93 
    94         /* set user stack */
    95         ustate.sp = kernel_uarg->uspace_stack +
    96             kernel_uarg->uspace_stack_size;
    97 
    98         /* set where uspace execution starts */
    99         ustate.pc = kernel_uarg->uspace_entry;
     80        ustate.sp = sp;
     81        ustate.pc = pc;
    10082
    10183        /* status register in user mode */
  • kernel/arch/arm64/src/arm64.c

    r2cf8f994 r3fcea34  
    146146}
    147147
     148uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size)
     149{
     150        return stack_base + stack_size;
     151}
     152
    148153/** Change processor mode.
    149154 *
    150155 * @param kernel_uarg Userspace settings (entry point, stack, ...).
    151156 */
    152 void userspace(uspace_arg_t *kernel_uarg)
     157void userspace(uintptr_t pc, uintptr_t sp)
    153158{
    154159        /* Prepare return to EL0. */
     
    157162
    158163        /* Set program entry. */
    159         ELR_EL1_write(kernel_uarg->uspace_entry);
     164        ELR_EL1_write(pc);
    160165
    161166        /* Set user stack. */
    162         SP_EL0_write(kernel_uarg->uspace_stack +
    163             kernel_uarg->uspace_stack_size);
     167        SP_EL0_write(sp);
    164168
    165169        /* Clear Thread ID register. */
     
    170174             * Reset the kernel stack to its base value.
    171175             *
    172              * Clear all general-purpose registers,
    173              * except x0 that holds an argument for
    174              * the user space.
     176             * Clear all general-purpose registers.
    175177             */
    176178            "mov sp, %[kstack]\n"
    177             "mov x0, %[uspace_uarg]\n"
     179            "mov x0, #0\n"
    178180            "mov x1, #0\n"
    179181            "mov x2, #0\n"
     
    207209            "mov x30, #0\n"
    208210            "eret\n"
    209             :: [uspace_uarg] "r" (kernel_uarg->uspace_uarg),
    210               [kstack] "r" (((uint64_t) (THREAD->kstack)) +
     211            :: [kstack] "r" (((uint64_t) (THREAD->kstack)) +
    211212              MEM_STACK_SIZE - SP_DELTA)
    212213        );
  • kernel/arch/ia32/src/userspace.c

    r2cf8f994 r3fcea34  
    3838#include <stdint.h>
    3939#include <arch.h>
    40 #include <abi/proc/uarg.h>
    4140#include <mm/as.h>
    4241#include <arch/cpu.h>
    4342#include <arch/asm.h>
     43
     44uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size)
     45{
     46        return stack_base + stack_size;
     47}
    4448
    4549/** Enter userspace
     
    4852 *
    4953 */
    50 void userspace(uspace_arg_t *kernel_uarg)
     54void userspace(uintptr_t pc, uintptr_t sp)
    5155{
    5256        uint32_t eflags = read_eflags();
     
    6165            "pushl %[utext_des]\n"
    6266            "pushl %[entry]\n"
    63             "movl %[uarg], %%eax\n"
    6467
    6568            /* %edi is defined to hold pcb_ptr - set it to 0 */
     
    7073            : [eflags_mask] "i" (~EFLAGS_NT),
    7174              [udata_des] "i" (GDT_SELECTOR(UDATA_DES) | PL_USER),
    72               [stack_top] "r" (kernel_uarg->uspace_stack +
    73               kernel_uarg->uspace_stack_size),
     75              [stack_top] "r" (sp),
    7476              [eflags] "r" ((eflags & ~(EFLAGS_NT)) | EFLAGS_IF),
    7577              [utext_des] "i" (GDT_SELECTOR(UTEXT_DES) | PL_USER),
    76               [entry] "r" (kernel_uarg->uspace_entry),
    77               [uarg] "r" (kernel_uarg->uspace_uarg),
     78              [entry] "r" (pc),
    7879              [vreg_des] "r" (GDT_SELECTOR(VREG_DES))
    7980            : "eax");
  • kernel/arch/ia64/src/ia64.c

    r2cf8f994 r3fcea34  
    218218}
    219219
     220uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size)
     221{
     222        return ALIGN_DOWN(stack_base + stack_size / 2, STACK_ALIGNMENT);
     223}
     224
    220225/** Enter userspace and never return. */
    221 void userspace(uspace_arg_t *kernel_uarg)
     226void userspace(uintptr_t pc, uintptr_t sp)
    222227{
    223228        psr_t psr;
     
    241246         *
    242247         * When calculating stack addresses, mind the stack split between the
    243          * memory stack and the RSE stack. Each occuppies
    244          * uspace_stack_size / 2 bytes.
     248         * memory stack and the RSE stack.
     249         * Memory stack occupies area under sp, while RSE stack occupies area above.
    245250         */
    246         switch_to_userspace(kernel_uarg->uspace_entry,
    247             kernel_uarg->uspace_stack +
    248             kernel_uarg->uspace_stack_size / 2 -
    249             ALIGN_UP(STACK_ITEM_SIZE, STACK_ALIGNMENT),
    250             kernel_uarg->uspace_stack +
    251             kernel_uarg->uspace_stack_size / 2,
    252             kernel_uarg->uspace_uarg, psr.value, rsc.value);
     251        switch_to_userspace(pc,
     252            sp, sp + ALIGN_UP(STACK_ITEM_SIZE, STACK_ALIGNMENT),
     253            0, psr.value, rsc.value);
    253254
    254255        while (true)
  • kernel/arch/mips32/include/arch/asm.h

    r2cf8f994 r3fcea34  
    8181extern void cpu_halt(void) __attribute__((noreturn));
    8282extern void asm_delay_loop(uint32_t);
    83 extern void userspace_asm(uintptr_t, uintptr_t, uintptr_t);
     83extern void userspace_asm(uintptr_t, uintptr_t);
    8484
    8585extern ipl_t interrupts_disable(void);
  • kernel/arch/mips32/src/mips32.c

    r2cf8f994 r3fcea34  
    163163}
    164164
    165 void userspace(uspace_arg_t *kernel_uarg)
     165uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size)
     166{
     167        return stack_base + stack_size;
     168}
     169
     170void userspace(uintptr_t pc, uintptr_t sp)
    166171{
    167172        /* EXL = 1, UM = 1, IE = 1 */
    168173        cp0_status_write(cp0_status_read() | (cp0_status_exl_exception_bit |
    169174            cp0_status_um_bit | cp0_status_ie_enabled_bit));
    170         cp0_epc_write(kernel_uarg->uspace_entry);
    171         userspace_asm(kernel_uarg->uspace_stack +
    172             kernel_uarg->uspace_stack_size,
    173             kernel_uarg->uspace_uarg,
    174             kernel_uarg->uspace_entry);
     175        cp0_epc_write(pc);
     176        userspace_asm(sp, pc);
    175177
    176178        while (true)
  • kernel/arch/mips32/src/start.S

    r2cf8f994 r3fcea34  
    339339FUNCTION_BEGIN(userspace_asm)
    340340        move $sp, $a0
    341         move $v0, $a1
    342         move $t9, $a2      /* set up correct entry into PIC code */
    343         xor $a0, $a0, $a0  /* $a0 is defined to hold pcb_ptr */
    344                            /* set it to 0 */
     341        xor $a0, $a0, $a0  /* $a0 is defined to hold pcb_ptr, set it to 0 */
     342        xor $fp, $fp, $fp  // FIXME: wipe all userspace-accessible registers
     343        xor $ra, $ra, $ra
    345344        eret
    346345FUNCTION_END(userspace_asm)
  • kernel/arch/ppc32/src/ppc32.c

    r2cf8f994 r3fcea34  
    4949#include <mm/km.h>
    5050#include <time/clock.h>
    51 #include <abi/proc/uarg.h>
    5251#include <console/console.h>
    5352#include <sysinfo/sysinfo.h>
     
    290289}
    291290
    292 void userspace(uspace_arg_t *kernel_uarg)
    293 {
    294         userspace_asm(kernel_uarg->uspace_uarg,
    295             kernel_uarg->uspace_stack +
    296             kernel_uarg->uspace_stack_size - SP_DELTA,
    297             kernel_uarg->uspace_entry);
    298 
     291uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size)
     292{
     293        return stack_base + stack_size - SP_DELTA;
     294}
     295
     296void userspace(uintptr_t pc, uintptr_t sp)
     297{
     298        userspace_asm(0, sp, pc);
    299299        unreachable();
    300300}
  • kernel/arch/riscv64/src/userspace.c

    r2cf8f994 r3fcea34  
    3333 */
    3434
    35 #include <abi/proc/uarg.h>
    3635#include <userspace.h>
    3736
    38 void userspace(uspace_arg_t *kernel_uarg)
     37uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size)
     38{
     39        return stack_base + stack_size;
     40}
     41
     42void userspace(uintptr_t pc, uintptr_t sp)
    3943{
    4044        // FIXME
  • kernel/arch/sparc64/src/sun4u/asm.S

    r2cf8f994 r3fcea34  
    8383 */
    8484FUNCTION_BEGIN(switch_to_userspace)
    85         save %o1, -(STACK_WINDOW_SAVE_AREA_SIZE + STACK_ARG_SAVE_AREA_SIZE), %sp
     85        save %o1, 0, %sp
    8686        flushw
    8787        wrpr %g0, 0, %cleanwin          ! avoid information leak
  • kernel/arch/sparc64/src/sun4u/sparc64.c

    r2cf8f994 r3fcea34  
    159159}
    160160
     161uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size)
     162{
     163        return ALIGN_DOWN(stack_base + stack_size - STACK_WINDOW_SAVE_AREA_SIZE - STACK_ARG_SAVE_AREA_SIZE, 16) - STACK_BIAS;
     164}
     165
    161166/** Switch to userspace. */
    162 void userspace(uspace_arg_t *kernel_uarg)
     167void userspace(uintptr_t pc, uintptr_t sp)
    163168{
    164169        (void) interrupts_disable();
    165         switch_to_userspace(kernel_uarg->uspace_entry,
    166             kernel_uarg->uspace_stack +
    167             kernel_uarg->uspace_stack_size -
    168             (ALIGN_UP(STACK_ITEM_SIZE, STACK_ALIGNMENT) + STACK_BIAS),
    169             kernel_uarg->uspace_uarg);
     170        switch_to_userspace(pc, sp, 0);
    170171
    171172        /* Not reached */
  • kernel/arch/sparc64/src/sun4v/asm.S

    r2cf8f994 r3fcea34  
    4141 */
    4242FUNCTION_BEGIN(switch_to_userspace)
    43         save %o1, -(STACK_WINDOW_SAVE_AREA_SIZE + STACK_ARG_SAVE_AREA_SIZE), %sp
     43        save %o1, 0, %sp
    4444        flushw
    4545        wrpr %g0, 0, %cleanwin          ! avoid information leak
  • kernel/arch/sparc64/src/sun4v/sparc64.c

    r2cf8f994 r3fcea34  
    157157}
    158158
     159uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size)
     160{
     161        return ALIGN_DOWN(stack_base + stack_size - STACK_WINDOW_SAVE_AREA_SIZE - STACK_ARG_SAVE_AREA_SIZE, 16) - STACK_BIAS;
     162}
     163
    159164/** Switch to userspace. */
    160 void userspace(uspace_arg_t *kernel_uarg)
     165void userspace(uintptr_t pc, uintptr_t sp)
    161166{
    162167        (void) interrupts_disable();
    163         switch_to_userspace(kernel_uarg->uspace_entry,
    164             kernel_uarg->uspace_stack +
    165             kernel_uarg->uspace_stack_size -
    166             (ALIGN_UP(STACK_ITEM_SIZE, STACK_ALIGNMENT) + STACK_BIAS),
    167             kernel_uarg->uspace_uarg);
     168        switch_to_userspace(pc, sp, 0);
    168169
    169170        /* Not reached */
  • kernel/generic/include/main/uinit.h

    r2cf8f994 r3fcea34  
    3636#define KERN_UINIT_H_
    3737
     38#include <stdint.h>
     39
     40typedef struct {
     41        uintptr_t pc;
     42        uintptr_t sp;
     43} uinit_arg_t;
     44
    3845extern void uinit(void *arg);
    3946
  • kernel/generic/include/proc/thread.h

    r2cf8f994 r3fcea34  
    4545#include <arch/cpu.h>
    4646#include <mm/tlb.h>
    47 #include <abi/proc/uarg.h>
    4847#include <udebug/udebug.h>
    4948#include <abi/proc/thread.h>
  • kernel/generic/include/userspace.h

    r2cf8f994 r3fcea34  
    3939#include <typedefs.h>
    4040
     41extern uintptr_t arch_get_initial_sp(uintptr_t stack_base, uintptr_t stack_size);
     42
    4143/** Switch to user-space (CPU user priviledge level) */
    42 extern void userspace(uspace_arg_t *uarg) __attribute__((noreturn));
     44extern void userspace(sysarg_t pc, sysarg_t sp) __attribute__((noreturn));
    4345
    4446#endif
  • kernel/generic/src/main/uinit.c

    r2cf8f994 r3fcea34  
    6060#endif
    6161
    62         uspace_arg_t *uarg = arg;
    63         uspace_arg_t local_uarg;
    64 
    65         local_uarg.uspace_entry = uarg->uspace_entry;
    66         local_uarg.uspace_stack = uarg->uspace_stack;
    67         local_uarg.uspace_stack_size = uarg->uspace_stack_size;
    68         local_uarg.uspace_uarg = uarg->uspace_uarg;
    69         local_uarg.uspace_thread_function = USPACE_NULL;
    70         local_uarg.uspace_thread_arg = USPACE_NULL;
    71 
     62        uinit_arg_t *uarg = arg;
     63        sysarg_t pc = uarg->pc;
     64        sysarg_t sp = uarg->sp;
    7265        free(uarg);
    7366
    74         userspace(&local_uarg);
     67        userspace(pc, sp);
    7568}
    7669
  • kernel/generic/src/proc/program.c

    r2cf8f994 r3fcea34  
    5353#include <syscall/copy.h>
    5454#include <proc/program.h>
     55#include <userspace.h>
    5556
    5657/**
     
    7273errno_t program_create(as_t *as, uspace_addr_t entry_addr, char *name, program_t *prg)
    7374{
    74         uspace_arg_t *kernel_uarg = (uspace_arg_t *)
    75             malloc(sizeof(uspace_arg_t));
     75        uinit_arg_t *kernel_uarg = malloc(sizeof(uinit_arg_t));
    7676        if (!kernel_uarg)
    7777                return ENOMEM;
     
    104104        }
    105105
    106         kernel_uarg->uspace_entry = entry_addr;
    107         kernel_uarg->uspace_stack = virt;
    108         kernel_uarg->uspace_stack_size = STACK_SIZE_USER;
    109         kernel_uarg->uspace_thread_function = USPACE_NULL;
    110         kernel_uarg->uspace_thread_arg = USPACE_NULL;
    111         kernel_uarg->uspace_uarg = USPACE_NULL;
     106        kernel_uarg->pc = entry_addr;
     107        kernel_uarg->sp = arch_get_initial_sp(virt, STACK_SIZE_USER);
    112108
    113109        /*
  • kernel/generic/src/proc/thread.c

    r2cf8f994 r3fcea34  
    944944
    945945/** Process syscall to create new thread.
    946  *
    947  */
    948 sys_errno_t sys_thread_create(uspace_ptr_uspace_arg_t uspace_uarg, uspace_ptr_char uspace_name,
    949     size_t name_len, uspace_ptr_thread_id_t uspace_thread_id)
     946 * The started thread will have initial pc and sp set to the exact values passed
     947 * to the syscall. The kernel will not touch any stack data below the stack
     948 * pointer, but some architectures may require some space to be available
     949 * for use above it. See userspace() in kernel, and <libarch/thread.h> in libc.
     950 *
     951 */
     952sys_errno_t sys_thread_create(sysarg_t pc, sysarg_t sp,
     953    uspace_ptr_char uspace_name, size_t name_len)
    950954{
    951955        if (name_len > THREAD_NAME_BUFLEN - 1)
     
    963967         * In case of success, kernel_uarg will be freed in uinit().
    964968         */
    965         uspace_arg_t *kernel_uarg =
    966             (uspace_arg_t *) malloc(sizeof(uspace_arg_t));
     969        uinit_arg_t *kernel_uarg = malloc(sizeof(uinit_arg_t));
    967970        if (!kernel_uarg)
    968971                return (sys_errno_t) ENOMEM;
    969972
    970         rc = copy_from_uspace(kernel_uarg, uspace_uarg, sizeof(uspace_arg_t));
    971         if (rc != EOK) {
    972                 free(kernel_uarg);
    973                 return (sys_errno_t) rc;
    974         }
     973        kernel_uarg->pc = pc;
     974        kernel_uarg->sp = sp;
     975
     976        // TODO: fix some unnecessary inconsistencies between architectures
    975977
    976978        thread_t *thread = thread_create(uinit, kernel_uarg, TASK,
    977979            THREAD_FLAG_USPACE | THREAD_FLAG_NOATTACH, namebuf);
    978         if (thread) {
    979                 if (uspace_thread_id) {
    980                         rc = copy_to_uspace(uspace_thread_id, &thread->tid,
    981                             sizeof(thread->tid));
    982                         if (rc != EOK) {
    983                                 /*
    984                                  * We have encountered a failure, but the thread
    985                                  * has already been created. We need to undo its
    986                                  * creation now.
    987                                  */
    988 
    989                                 /*
    990                                  * The new thread structure is initialized, but
    991                                  * is still not visible to the system.
    992                                  * We can safely deallocate it.
    993                                  */
    994                                 slab_free(thread_cache, thread);
    995                                 free(kernel_uarg);
    996 
    997                                 return (sys_errno_t) rc;
    998                         }
    999                 }
     980        if (!thread) {
     981                free(kernel_uarg);
     982                return (sys_errno_t) ENOMEM;
     983        }
    1000984
    1001985#ifdef CONFIG_UDEBUG
    1002                 /*
    1003                 * Generate udebug THREAD_B event and attach the thread.
    1004                 * This must be done atomically (with the debug locks held),
    1005                 * otherwise we would either miss some thread or receive
    1006                 * THREAD_B events for threads that already existed
    1007                 * and could be detected with THREAD_READ before.
    1008                 */
    1009                 udebug_thread_b_event_attach(thread, TASK);
     986        /*
     987        * Generate udebug THREAD_B event and attach the thread.
     988        * This must be done atomically (with the debug locks held),
     989        * otherwise we would either miss some thread or receive
     990        * THREAD_B events for threads that already existed
     991        * and could be detected with THREAD_READ before.
     992        */
     993        udebug_thread_b_event_attach(thread, TASK);
    1010994#else
    1011                 thread_attach(thread, TASK);
     995        thread_attach(thread, TASK);
    1012996#endif
    1013                 thread_start(thread);
    1014                 thread_put(thread);
    1015 
    1016                 return 0;
    1017         } else
    1018                 free(kernel_uarg);
    1019 
    1020         return (sys_errno_t) ENOMEM;
     997        thread_start(thread);
     998        thread_put(thread);
     999
     1000        return (sys_errno_t) EOK;
    10211001}
    10221002
Note: See TracChangeset for help on using the changeset viewer.