This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug c/70220] New: [x86] interrupt attribute optionally needs to provide read, write and control the set of saved registers


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70220

            Bug ID: 70220
           Summary: [x86] interrupt attribute optionally needs to provide
                    read, write and control the set of saved registers
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: wink at saville dot com
  Target Milestone: ---

I'm using the new C interrupt attribute for x86 and its working well. But when
I expanded its use to include handling thread context switches, I found that
its current implementation lacking.

When using an ISR for a thread context it is necessary to save and restore all
registers but by default it saves a few as it thinks are necessary. It is
possible to use the clobber list of inline assembly to tell the interrupt
attribute code to save additional registers but you can't seem to mention the
segment registers in that clobber list, thus to save these additional registers
you must manually save them manually.

In addition, there is a second problem. I need to know the location of the
registers so they can be read and written. For thread context switching I only
need to be able to initialize a few registers to specific values, but if the
ISR was being used for debugging such as handling a break point interrupt, then
you need to be able to read/write every register.

My current solution is uses the clobber list technique and then looking at the
generated code to determine where in the stack the registers are saved so they
can be initialized. As I mentioned above the clobber list is only a partial
solution but good enough for proof of concept.

Below are snippets a "working" interrupt service routine:

typedef struct intr_frame {
  ac_uptr ip;
  ac_uptr cs;
  ac_uptr flags;
  ac_uptr sp;
  ac_uptr ss;
} intr_frame;

struct saved_regs {
  ac_u64 rax;
  ac_u64 rdx;
  ac_u64 rcx;
  ac_u64 rbx;
  ac_u64 rsi;
  ac_u64 rdi;
  ac_u64 rbp;
  ac_u64 r8;
  ac_u64 r9;
  ac_u64 r10;
  ac_u64 r11;
  ac_u64 r12;
  ac_u64 r13;
  ac_u64 r14;
  ac_u64 r15;
};

struct full_stack_frame {
  union {
    struct saved_regs regs;
    ac_u64 regs_array[15];
  };
  struct intr_frame iret_frame;
} __attribute__ ((__packed__));

static void init_stack_frame(ac_u8* pstack, ac_uptr stack_size, ac_uptr flags,
    void* (*entry)(void*), void* entry_arg, ac_u8** psp, ac_u16 *pss) {
  ac_u8* tos = pstack + stack_size;
  struct full_stack_frame* sf =
    (struct full_stack_frame*)(tos - sizeof(struct full_stack_frame));

  sf->regs.rdi = (ac_u64)entry_arg;
  sf->iret_frame.ip = (ac_uptr)entry;
  sf->iret_frame.cs = 0x8;
  sf->iret_frame.flags = flags;

  sf->iret_frame.sp = (ac_uptr)(tos);
  sf->iret_frame.ss = 0x10;

  *psp = (ac_u8*)sf;
  *pss = sf->iret_frame.ss;
}

__attribute__((__interrupt__))
void timer_reschedule_isr(struct intr_frame* frame) {
  __asm__ volatile(""::: "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp",
                         "r8",  "r9",  "r10", "r11", "r12", "r13", "r14",
"r15");

  tcb_x86 *ptcb = thread_scheduler((ac_u8*)get_sp(), get_ss());
  __asm__ volatile("movq %0, %%rsp;" :: "rm" (ptcb->sp) : "rsp");
  __asm__ volatile("movw %0, %%ss;" :: "rm" (ptcb->ss));
  set_apic_timer_initial_count(ptcb->slice);

  __atomic_add_fetch(&timer_reschedule_isr_counter, 1, __ATOMIC_RELEASE);

  send_apic_eoi();
}


I've discussed this with H.J. Lu and currently we are leaning to a solution
where the interrupt attribute can have an optional list of registers to NOT
save and then assume the programmer will provide the code to save the desired
registers, thus all registers can be saved and their location known because the
programmer is in control. I'd also like an "all" to indicate that none of the
registers as I see that as a typical use case. And if possible I'd like the
compiler to detect if a register was used but not saved before being modified.

I have identified one possible problem and with this scheme, what if the
compiler needs to setup a stack frame by pushing rbp and then moving rsp to
rbp, how would that case be handled.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]