#include #include #include #include "dat.h" #include "fns.h" #include "linux.h" enum { Index, Type, Flags, DPL, Base, Limit, Nfields, }; static int descempty(struct linux_user_desc *info) { return info->base_addr==0 && info->limit==0 && info->contents==0 && info->read_exec_only==1 && info->seg_32bit==0 && info->limit_in_pages==0 && info->seg_not_present==1 && info->useable==0; } int sys_set_thread_area(void *pinfo) { struct linux_user_desc *info = pinfo; char buf[1024]; char *p, *e, *f[Nfields]; int n, fd, idx, err; trace("sys_set_thread_area(%p)", pinfo); err = -ENOSYS; if((fd = open("/dev/gdt", ORDWR)) < 0) goto out; idx = info->entry_number; if(idx == -1){ err = -ESRCH; if((n = read(fd, buf, sizeof(buf)-1)) <= 0) goto out; buf[n] = 0; p = buf; while(e = strchr(p, '\n')){ *e = 0; if(getfields(p, f, nelem(f), 1, " ") != nelem(f)) goto out; idx = strtoul(f[Index], nil, 16); if(idx >= 8*sizeof(current->tlsmask)) break; if((current->tlsmask & (1<= 8*sizeof(current->tlsmask)) goto out; buf[0] = 0; if(!info->seg_not_present) strcat(buf, "P"); if(info->limit_in_pages) strcat(buf, "G"); if(info->useable) strcat(buf, "U"); if(info->contents & 2){ /* code segment */ if(info->contents & 1) strcat(buf, "C"); if(info->seg_32bit) strcat(buf, "D"); if(!info->read_exec_only) strcat(buf, "R"); if(buf[0] == 0) strcat(buf, "-"); if(fprint(fd, "%x code %s 3 %lux %lux\n", idx, buf, (ulong)info->base_addr, (ulong)info->limit) < 0) goto out; } else { /* data segment */ if(info->contents & 1) strcat(buf, "E"); if(info->seg_32bit) strcat(buf, "B"); if(!info->read_exec_only) strcat(buf, "W"); if(buf[0] == 0) strcat(buf, "-"); if(fprint(fd, "%x data %s 3 %lux %lux\n", idx, buf, (ulong)info->base_addr, (ulong)info->limit) < 0) goto out; } err = 0; info->entry_number = idx; if(!descempty(info)){ current->tlsmask |= 1<tlsmask &= ~(1<= 0) close(fd); return err; } int sys_get_thread_area(void *pinfo) { struct linux_user_desc *info = pinfo; int err, n, fd, idx; char buf[1024]; char *p, *e, *f[Nfields]; trace("sys_get_thread_area(%p)", pinfo); err = -ENOSYS; if((fd = open("/dev/gdt", OREAD)) < 0) goto out; err = -EINVAL; if((n = read(fd, buf, sizeof(buf)-1)) <= 0) goto out; buf[n] = 0; p = buf; while(e = strchr(p, '\n')){ *e = 0; if(getfields(p, f, nelem(f), 1, " ") != nelem(f)) goto out; idx = strtoul(f[Index], nil, 16); if(idx >= 8*sizeof(current->tlsmask)) break; if(idx == info->entry_number) goto found; p = e+1; } goto out; found: info->contents = 0; if(strcmp(f[Type], "code") == 0) info->contents |= 2; info->seg_not_present = 1; info->limit_in_pages = 0; info->seg_32bit = 0; info->read_exec_only = 1; info->useable = 0; for(p = f[Flags]; *p; p++){ switch(*p){ case 'P': info->seg_not_present = 0; break; case 'G': info->limit_in_pages = 1; break; case 'B': case 'D': info->seg_32bit = 1; break; case 'W': case 'R': info->read_exec_only = 0; break; case 'U': info->useable = 1; break; case 'E': case 'C': info->contents |= 1; break; } } info->base_addr = strtoul(f[Base], nil, 16); info->limit = strtoul(f[Limit], nil, 16); err = 0; out: if(fd >= 0) close(fd); return err; } static void cleardesc(struct linux_user_desc *info) { info->base_addr=0; info->limit=0; info->contents=0; info->read_exec_only=1; info->seg_32bit=0; info->limit_in_pages=0; info->seg_not_present=1; info->useable=0; } void inittls(void) { struct linux_user_desc info; int i; for(i=0; i<8*sizeof(current->tlsmask); i++){ if((current->tlsmask & (1 << i)) == 0) continue; cleardesc(&info); info.entry_number = i; sys_set_thread_area(&info); } current->tlsmask = 0; } void clonetls(Uproc *new) { new->tlsmask = current->tlsmask; } int sys_modify_ldt(int func, void *data, int count) { trace("sys_modify_ldt(%d, %p, %x)", func, data, count); return -ENOSYS; }