From mboxrd@z Thu Jan 1 00:00:00 1970 Message-ID: <24aec0f0aa0c2eb4bd08246eb12d0730@gmx.de> To: 9fans@9fans.net Date: Wed, 14 May 2008 00:10:06 +0200 From: cinap_lenrek@gmx.de MIME-Version: 1.0 Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit Subject: [9fans] some x86 Segment descriptor experiments Topicbox-Message-UUID: a7fc1cea-ead3-11e9-9d60-3106f5b1d025 Hi, I'm trying to implement TLS support for linuxemu. TLS on linux works as following: here is a syscall called set_thread_area() where a struct is passed containing something like a segment descriptor that is stored for the process and is switched in the gdt when the process gets resumed for execution. the linux thread library allocates some memory, makes an descriptor with the base address on that memory using set_thread_area, build segment selector for it and stores that on GS segment register. that memory is then accessed with the GS segment prefixed so that every thread ends up with its own TLS block. For testing, i reserved 2 slots in the Plan9 gdt and wrote a driver to read/write the slots from userspace. I also needed to comment out a check that disallows changing the segment register GS and FS to something other than UDSEG. This worked fine so far... i could get bash running with libthread and TLS initialization. (They crash with null-pointer exception when here is no valid segment descriptor installed) Things got interesting when i tried to make the descriptors local for every process. (Thats the whole idea of TLS) I put a Segdesc tlsdescr[2]; array on the PMMU struct in pc/dat.h and added code in port/devproc.c to read/write that array from userspace. To switch the gdt entries in when the program gets resumed, added something like this in pc/main.c: void procrestore(Proc *p){ ... + m->gdt[UTLSSEG0] = p->tlsdescr[0]; + m->gdt[UTLSSRG1] = p->tlsdescr[1]; ... } and void procsetup(Proc *p){ ... + p->tlsdescr[0] = (Segdesc){0, 0}; + p->tlsdescr[1] = (Segdesc){0, 0}; ... } assuming the segment registers get reloaded when the kernel restores to userspace in pc/l.s: TEXT _strayintr(SB), $0 /* ... segment registers get saved ... */ PUSHL SP /* Ureg* argument to trap */ CALL trap(SB) TEXT forkret(SB), $0 POPL AX POPAL POPL GS /* <- here we reload the segment registers right? */ POPL FS POPL ES POPL DS ADDL $8, SP /* pop error code and trap type */ IRETL or are here other paths out of the kernel too? the reloading is required because as i read from thr processor documentation, the processor caches the effective contents of a GDT/LDR descriptor in shadow segment registers and only updates them on LOAD. OK... after all... doent work and i have no idea why. Program just crashed as if here was no segment at all. I added a sleep(1) after i wrote the p->tlsdescr[x] and then it worked for a short time... and then crashed the whole kernel... I have never done any kernelhacking so far and never fiddled with GDT stuff... So some stupid newbie questions :-) Does the kernel use any other segment selectors other than CS and DS? If it doent use GS/FS... this should not have any side effects... also... a process could change the segment descriptor to some invalid range... paging would protect the process from doing any harm right? Can the Mach *m->gdt[] be accessed on restoreproc() without problems? Do i need to disable interrupts or something? Are all the segment registers reloaded when the kernel switches back to userspace? (as the thing above) Any other idea why this doent work? cinap