direct-io.hg
changeset 1335:99a0fa51b992
bitkeeper revision 1.883 (4094212dXJnedPkykZkVt9oqyUah3w)
move full execution context out-of-line from dom0_op_t.
further changes to save/restore.
move full execution context out-of-line from dom0_op_t.
further changes to save/restore.
author | iap10@labyrinth.cl.cam.ac.uk |
---|---|
date | Sat May 01 22:14:05 2004 +0000 (2004-05-01) |
parents | f7c9d0a33a21 |
children | 75286d976ac6 |
files | tools/xc/lib/xc_domain.c tools/xc/lib/xc_linux_build.c tools/xc/lib/xc_linux_restore.c tools/xc/lib/xc_linux_save.c tools/xc/lib/xc_netbsd_build.c xen/common/dom0_ops.c xen/common/domain.c xen/include/hypervisor-ifs/dom0_ops.h |
line diff
1.1 --- a/tools/xc/lib/xc_domain.c Sat May 01 00:30:43 2004 +0000 1.2 +++ b/tools/xc/lib/xc_domain.c Sat May 01 22:14:05 2004 +0000 1.3 @@ -84,6 +84,7 @@ int xc_domain_getinfo(int xc_handle, 1.4 { 1.5 op.cmd = DOM0_GETDOMAININFO; 1.6 op.u.getdomaininfo.domain = (domid_t)next_domid; 1.7 + op.u.getdomaininfo.ctxt = NULL; // no exec context info, thanks. 1.8 if ( do_dom0_op(xc_handle, &op) < 0 ) 1.9 break; 1.10 info->domid = (u64)op.u.getdomaininfo.domain;
2.1 --- a/tools/xc/lib/xc_linux_build.c Sat May 01 00:30:43 2004 +0000 2.2 +++ b/tools/xc/lib/xc_linux_build.c Sat May 01 22:14:05 2004 +0000 2.3 @@ -26,6 +26,7 @@ static long get_tot_pages(int xc_handle, 2.4 dom0_op_t op; 2.5 op.cmd = DOM0_GETDOMAININFO; 2.6 op.u.getdomaininfo.domain = (domid_t)domid; 2.7 + op.u.getdomaininfo.ctxt = NULL; 2.8 return (do_dom0_op(xc_handle, &op) < 0) ? 2.9 -1 : op.u.getdomaininfo.tot_pages; 2.10 } 2.11 @@ -70,7 +71,7 @@ static int setup_guestos(int xc_handle, 2.12 gzFile initrd_gfd, unsigned long initrd_len, 2.13 unsigned long nr_pages, 2.14 unsigned long *pvsi, unsigned long *pvke, 2.15 - dom0_builddomain_t *builddomain, 2.16 + full_execution_context_t *ctxt, 2.17 const char *cmdline, 2.18 unsigned long shared_info_frame, 2.19 unsigned int control_evtchn) 2.20 @@ -163,8 +164,6 @@ static int setup_guestos(int xc_handle, 2.21 v_start, v_end); 2.22 printf(" ENTRY ADDRESS: %08lx\n", vkern_entry); 2.23 2.24 - memset(builddomain, 0, sizeof(*builddomain)); 2.25 - 2.26 if ( (pm_handle = init_pfn_mapper((domid_t)dom)) < 0 ) 2.27 goto error_out; 2.28 2.29 @@ -205,7 +204,7 @@ static int setup_guestos(int xc_handle, 2.30 /* First allocate page for page dir. */ 2.31 ppt_alloc = (vpt_start - v_start) >> PAGE_SHIFT; 2.32 l2tab = page_array[ppt_alloc++] << PAGE_SHIFT; 2.33 - builddomain->ctxt.pt_base = l2tab; 2.34 + ctxt->pt_base = l2tab; 2.35 2.36 /* Initialise the page tables. */ 2.37 if ( (vl2tab = map_pfn_writeable(pm_handle, l2tab >> PAGE_SHIFT)) == NULL ) 2.38 @@ -388,7 +387,7 @@ int xc_linux_build(int xc_handle, 2.39 int initrd_fd = -1; 2.40 gzFile initrd_gfd = NULL; 2.41 int rc, i; 2.42 - full_execution_context_t *ctxt; 2.43 + full_execution_context_t st_ctxt, *ctxt = &st_ctxt; 2.44 unsigned long nr_pages; 2.45 char *image = NULL; 2.46 unsigned long image_size, initrd_size=0; 2.47 @@ -420,8 +419,15 @@ int xc_linux_build(int xc_handle, 2.48 } 2.49 } 2.50 2.51 + if ( mlock(&st_ctxt, sizeof(st_ctxt) ) ) 2.52 + { 2.53 + PERROR("Unable to mlock ctxt"); 2.54 + return 1; 2.55 + } 2.56 + 2.57 op.cmd = DOM0_GETDOMAININFO; 2.58 op.u.getdomaininfo.domain = (domid_t)domid; 2.59 + op.u.getdomaininfo.ctxt = ctxt; 2.60 if ( (do_dom0_op(xc_handle, &op) < 0) || 2.61 ((u64)op.u.getdomaininfo.domain != domid) ) 2.62 { 2.63 @@ -429,7 +435,7 @@ int xc_linux_build(int xc_handle, 2.64 goto error_out; 2.65 } 2.66 if ( (op.u.getdomaininfo.state != DOMSTATE_STOPPED) || 2.67 - (op.u.getdomaininfo.ctxt.pt_base != 0) ) 2.68 + (ctxt->pt_base != 0) ) 2.69 { 2.70 ERROR("Domain is already constructed"); 2.71 goto error_out; 2.72 @@ -438,7 +444,7 @@ int xc_linux_build(int xc_handle, 2.73 if ( setup_guestos(xc_handle, domid, image, image_size, 2.74 initrd_gfd, initrd_size, nr_pages, 2.75 &vstartinfo_start, &vkern_entry, 2.76 - &launch_op.u.builddomain, cmdline, 2.77 + ctxt, cmdline, 2.78 op.u.getdomaininfo.shared_info_frame, 2.79 control_evtchn) < 0 ) 2.80 { 2.81 @@ -453,8 +459,6 @@ int xc_linux_build(int xc_handle, 2.82 if ( image != NULL ) 2.83 free(image); 2.84 2.85 - ctxt = &launch_op.u.builddomain.ctxt; 2.86 - 2.87 ctxt->flags = 0; 2.88 2.89 /* 2.90 @@ -507,8 +511,11 @@ int xc_linux_build(int xc_handle, 2.91 ctxt->failsafe_callback_cs = FLAT_GUESTOS_CS; 2.92 ctxt->failsafe_callback_eip = 0; 2.93 2.94 + memset( &launch_op, 0, sizeof(launch_op) ); 2.95 + 2.96 launch_op.u.builddomain.domain = (domid_t)domid; 2.97 launch_op.u.builddomain.num_vifs = 1; 2.98 + launch_op.u.builddomain.ctxt = ctxt; 2.99 2.100 launch_op.cmd = DOM0_BUILDDOMAIN; 2.101 rc = do_dom0_op(xc_handle, &launch_op);
3.1 --- a/tools/xc/lib/xc_linux_restore.c Sat May 01 00:30:43 2004 +0000 3.2 +++ b/tools/xc/lib/xc_linux_restore.c Sat May 01 22:14:05 2004 +0000 3.3 @@ -58,8 +58,8 @@ int xc_linux_restore(int xc_handle, 3.4 u64 *pdomid) 3.5 { 3.6 dom0_op_t op; 3.7 - int rc = 1, i, j; 3.8 - unsigned long mfn, pfn; 3.9 + int rc = 1, i, j, n, k; 3.10 + unsigned long mfn, pfn, xpfn; 3.11 unsigned int prev_pc, this_pc; 3.12 3.13 /* Number of page frames in use by this Linux session. */ 3.14 @@ -114,6 +114,14 @@ int xc_linux_restore(int xc_handle, 3.15 return 1; 3.16 } 3.17 3.18 + if ( mlock(&ctxt, sizeof(ctxt) ) ) 3.19 + { 3.20 + /* needed for when we do the build dom0 op, 3.21 + but might as well do early */ 3.22 + PERROR("Unable to mlock ctxt"); 3.23 + return 1; 3.24 + } 3.25 + 3.26 /* Start writing out the saved-domain record. */ 3.27 if ( !checked_read(gfd, signature, 16) || 3.28 (memcmp(signature, "LinuxGuestRecord", 16) != 0) ) 3.29 @@ -159,12 +167,6 @@ int xc_linux_restore(int xc_handle, 3.30 goto out; 3.31 } 3.32 3.33 - if ( !checked_read(gfd, pfn_type, 4 * nr_pfns) ) 3.34 - { 3.35 - ERROR("Error when reading from state file"); 3.36 - goto out; 3.37 - } 3.38 - 3.39 /* Set the domain's name to that from the restore file */ 3.40 if ( xc_domain_setname( xc_handle, dom, name ) ) 3.41 { 3.42 @@ -184,6 +186,7 @@ int xc_linux_restore(int xc_handle, 3.43 /* Get the domain's shared-info frame. */ 3.44 op.cmd = DOM0_GETDOMAININFO; 3.45 op.u.getdomaininfo.domain = (domid_t)dom; 3.46 + op.u.getdomaininfo.ctxt = NULL; 3.47 if ( do_dom0_op(xc_handle, &op) < 0 ) 3.48 { 3.49 ERROR("Could not get information on new domain"); 3.50 @@ -219,73 +222,119 @@ int xc_linux_restore(int xc_handle, 3.51 * We uncanonicalise page tables as we go. 3.52 */ 3.53 prev_pc = 0; 3.54 - for ( i = 0; i < nr_pfns; i++ ) 3.55 + 3.56 + n=0; 3.57 + while(1) 3.58 { 3.59 - this_pc = (i * 100) / nr_pfns; 3.60 + int j; 3.61 + unsigned long region_pfn_type[1024]; 3.62 + 3.63 + this_pc = (n * 100) / nr_pfns; 3.64 if ( (this_pc - prev_pc) >= 5 ) 3.65 { 3.66 verbose_printf("\b\b\b\b%3d%%", this_pc); 3.67 prev_pc = this_pc; 3.68 } 3.69 3.70 - mfn = pfn_to_mfn_table[i]; 3.71 + if ( !checked_read(gfd, &j, sizeof(int)) ) 3.72 + { 3.73 + ERROR("Error when reading from state file"); 3.74 + goto out; 3.75 + } 3.76 3.77 - ppage = map_pfn_writeable(pm_handle, mfn); 3.78 - 3.79 - if ( !checked_read(gfd, ppage, PAGE_SIZE) ) 3.80 + printf("batch=%d\n",j); 3.81 + 3.82 + if(j==0) break; // our work here is done 3.83 + 3.84 + if ( !checked_read(gfd, region_pfn_type, j*sizeof(unsigned long)) ) 3.85 { 3.86 ERROR("Error when reading from state file"); 3.87 goto out; 3.88 } 3.89 3.90 - if ( pfn_type[i] == L1TAB ) 3.91 - { 3.92 - for ( j = 0; j < 1024; j++ ) 3.93 - { 3.94 - if ( ppage[j] & _PAGE_PRESENT ) 3.95 - { 3.96 - if ( (pfn = ppage[j] >> PAGE_SHIFT) >= nr_pfns ) 3.97 - { 3.98 - ERROR("Frame number in type %d page table is out of range. i=%d j=%d pfn=%d nr_pfns=%d",pfn_type[i],i,j,pfn,nr_pfns); 3.99 - goto out; 3.100 - } 3.101 - if ( (pfn_type[pfn] != NONE) && (ppage[j] & _PAGE_RW) ) 3.102 - { 3.103 - ERROR("Write access requested for a restricted frame"); 3.104 - goto out; 3.105 - } 3.106 - ppage[j] &= (PAGE_SIZE - 1) & ~(_PAGE_GLOBAL | _PAGE_PAT); 3.107 - ppage[j] |= pfn_to_mfn_table[pfn] << PAGE_SHIFT; 3.108 - } 3.109 - } 3.110 - } 3.111 - else if ( pfn_type[i] == L2TAB ) 3.112 - { 3.113 - for ( j = 0; j < (HYPERVISOR_VIRT_START>>L2_PAGETABLE_SHIFT); j++ ) 3.114 - { 3.115 - if ( ppage[j] & _PAGE_PRESENT ) 3.116 - { 3.117 - if ( (pfn = ppage[j] >> PAGE_SHIFT) >= nr_pfns ) 3.118 - { 3.119 - ERROR("Frame number in page table is out of range"); 3.120 - goto out; 3.121 - } 3.122 - if ( pfn_type[pfn] != L1TAB ) 3.123 - { 3.124 - ERROR("Page table mistyping"); 3.125 - goto out; 3.126 - } 3.127 - ppage[j] &= (PAGE_SIZE - 1) & ~(_PAGE_GLOBAL | _PAGE_PSE); 3.128 - ppage[j] |= pfn_to_mfn_table[pfn] << PAGE_SHIFT; 3.129 - } 3.130 - } 3.131 - } 3.132 + for(i=0;i<j;i++) 3.133 + { 3.134 + pfn = region_pfn_type[i] & ~PGT_type_mask; 3.135 + 3.136 +//if(pfn_type[i])printf("^pfn=%d %08lx\n",pfn,pfn_type[i]); 3.137 + 3.138 + if (pfn>nr_pfns) 3.139 + { 3.140 + ERROR("pfn out of range"); 3.141 + goto out; 3.142 + } 3.143 + 3.144 + region_pfn_type[i] &= PGT_type_mask; 3.145 + 3.146 + pfn_type[pfn] = region_pfn_type[i]; 3.147 + 3.148 + mfn = pfn_to_mfn_table[pfn]; 3.149 + 3.150 +if(region_pfn_type[i])printf("i=%d pfn=%d mfn=%d type=%lx\n",i,pfn,mfn,region_pfn_type[i]); 3.151 + 3.152 + ppage = map_pfn_writeable(pm_handle, mfn); 3.153 + 3.154 + if ( !checked_read(gfd, ppage, PAGE_SIZE) ) 3.155 + { 3.156 + ERROR("Error when reading from state file"); 3.157 + goto out; 3.158 + } 3.159 3.160 - unmap_pfn(pm_handle, ppage); 3.161 + if ( region_pfn_type[i] == L1TAB ) 3.162 + { 3.163 + for ( k = 0; k < 1024; k++ ) 3.164 + { 3.165 + if ( ppage[k] & _PAGE_PRESENT ) 3.166 + { 3.167 + if ( (xpfn = ppage[k] >> PAGE_SHIFT) >= nr_pfns ) 3.168 + { 3.169 + ERROR("Frame number in type %d page table is out of range. i=%d k=%d pfn=%d nr_pfns=%d",region_pfn_type[i],i,k,xpfn,nr_pfns); 3.170 + goto out; 3.171 + } 3.172 +#if 0 3.173 + if ( (region_pfn_type[xpfn] != NONE) && (ppage[k] & _PAGE_RW) ) 3.174 + { 3.175 + ERROR("Write access requested for a restricted frame"); 3.176 + goto out; 3.177 + } 3.178 +#endif 3.179 + ppage[k] &= (PAGE_SIZE - 1) & ~(_PAGE_GLOBAL | _PAGE_PAT); 3.180 + ppage[k] |= pfn_to_mfn_table[xpfn] << PAGE_SHIFT; 3.181 + } 3.182 + } 3.183 + } 3.184 + else if ( region_pfn_type[i] == L2TAB ) 3.185 + { 3.186 + for ( k = 0; k < (HYPERVISOR_VIRT_START>>L2_PAGETABLE_SHIFT); k++ ) 3.187 + { 3.188 + if ( ppage[k] & _PAGE_PRESENT ) 3.189 + { 3.190 + if ( (xpfn = ppage[k] >> PAGE_SHIFT) >= nr_pfns ) 3.191 + { 3.192 + ERROR("Frame number in page table is out of range"); 3.193 + goto out; 3.194 + } 3.195 +#if 0 3.196 + if ( region_pfn_type[pfn] != L1TAB ) 3.197 + { 3.198 + ERROR("Page table mistyping"); 3.199 + goto out; 3.200 + } 3.201 +#endif 3.202 + ppage[k] &= (PAGE_SIZE - 1) & ~(_PAGE_GLOBAL | _PAGE_PSE); 3.203 + ppage[k] |= pfn_to_mfn_table[xpfn] << PAGE_SHIFT; 3.204 + } 3.205 + } 3.206 + } 3.207 3.208 - if ( add_mmu_update(xc_handle, mmu, 3.209 - (mfn<<PAGE_SHIFT) | MMU_MACHPHYS_UPDATE, i) ) 3.210 - goto out; 3.211 + unmap_pfn(pm_handle, ppage); 3.212 + 3.213 + if ( add_mmu_update(xc_handle, mmu, 3.214 + (mfn<<PAGE_SHIFT) | MMU_MACHPHYS_UPDATE, pfn) ) 3.215 + goto out; 3.216 + 3.217 + } 3.218 + 3.219 } 3.220 3.221 /* 3.222 @@ -352,7 +401,9 @@ int xc_linux_restore(int xc_handle, 3.223 pfn = ctxt.pt_base >> PAGE_SHIFT; 3.224 if ( (pfn >= nr_pfns) || (pfn_type[pfn] != L2TAB) ) 3.225 { 3.226 - ERROR("PT base is bad"); 3.227 + printf("PT base is bad. pfn=%d nr=%d type=%08lx %08lx\n", 3.228 + pfn, nr_pfns, pfn_type[pfn], L2TAB); 3.229 + ERROR("PT base is bad."); 3.230 goto out; 3.231 } 3.232 ctxt.pt_base = pfn_to_mfn_table[pfn] << PAGE_SHIFT; 3.233 @@ -406,11 +457,11 @@ int xc_linux_restore(int xc_handle, 3.234 ERROR("Bad LDT base or size"); 3.235 goto out; 3.236 } 3.237 - 3.238 + 3.239 op.cmd = DOM0_BUILDDOMAIN; 3.240 op.u.builddomain.domain = (domid_t)dom; 3.241 op.u.builddomain.num_vifs = 1; 3.242 - memcpy(&op.u.builddomain.ctxt, &ctxt, sizeof(ctxt)); 3.243 + op.u.builddomain.ctxt = &ctxt; 3.244 rc = do_dom0_op(xc_handle, &op); 3.245 3.246 out:
4.1 --- a/tools/xc/lib/xc_linux_save.c Sat May 01 00:30:43 2004 +0000 4.2 +++ b/tools/xc/lib/xc_linux_save.c Sat May 01 22:14:05 2004 +0000 4.3 @@ -10,7 +10,7 @@ 4.4 #include <asm-xen/suspend.h> 4.5 #include <zlib.h> 4.6 4.7 -#define BATCH_SIZE 512 /* 1024 pages (4MB) at a time */ 4.8 +#define BATCH_SIZE 1024 /* 1024 pages (4MB) at a time */ 4.9 4.10 /* This may allow us to create a 'quiet' command-line option, if necessary. */ 4.11 #define verbose_printf(_f, _a...) \ 4.12 @@ -122,11 +122,18 @@ int xc_linux_save(int xc_handle, 4.13 return 1; 4.14 } 4.15 4.16 + if ( mlock(&ctxt, sizeof(ctxt) ) ) 4.17 + { 4.18 + PERROR("Unable to mlock ctxt"); 4.19 + return 1; 4.20 + } 4.21 + 4.22 /* Ensure that the domain exists, and that it is stopped. */ 4.23 for ( ; ; ) 4.24 { 4.25 op.cmd = DOM0_GETDOMAININFO; 4.26 op.u.getdomaininfo.domain = (domid_t)domid; 4.27 + op.u.getdomaininfo.ctxt = &ctxt; 4.28 if ( (do_dom0_op(xc_handle, &op) < 0) || 4.29 ((u64)op.u.getdomaininfo.domain != domid) ) 4.30 { 4.31 @@ -134,7 +141,6 @@ int xc_linux_save(int xc_handle, 4.32 goto out; 4.33 } 4.34 4.35 - memcpy(&ctxt, &op.u.getdomaininfo.ctxt, sizeof(ctxt)); 4.36 memcpy(name, op.u.getdomaininfo.name, sizeof(name)); 4.37 shared_info_frame = op.u.getdomaininfo.shared_info_frame; 4.38 4.39 @@ -223,7 +229,7 @@ int xc_linux_save(int xc_handle, 4.40 4.41 4.42 /* We want zeroed memory so use calloc rather than malloc. */ 4.43 - pfn_type = calloc(1, 4 * srec.nr_pfns); 4.44 + pfn_type = calloc(BATCH_SIZE, sizeof(unsigned long)); 4.45 4.46 if ( (pfn_type == NULL) ) 4.47 { 4.48 @@ -231,6 +237,11 @@ int xc_linux_save(int xc_handle, 4.49 goto out; 4.50 } 4.51 4.52 + if ( mlock( pfn_type, BATCH_SIZE * sizeof(unsigned long) ) ) 4.53 + { 4.54 + ERROR("Unable to mlock"); 4.55 + goto out; 4.56 + } 4.57 4.58 4.59 /* Track the mfn_to_pfn table down from the domains PT */ 4.60 @@ -238,26 +249,22 @@ int xc_linux_save(int xc_handle, 4.61 unsigned long *pgd; 4.62 unsigned long mfn_to_pfn_table_start_mfn; 4.63 4.64 - pgd = mfn_mapper_map_single(xc_handle, domid, 4.65 + pgd = mfn_mapper_map_single(xc_handle, domid, 4.66 PAGE_SIZE, PROT_READ, 4.67 ctxt.pt_base>>PAGE_SHIFT); 4.68 -/* 4.69 - printf("pt mfn=%d pfn=%d type=%08x pte=%08x\n",ctxt.pt_base>>PAGE_SHIFT, 4.70 - mfn_to_pfn_table[ctxt.pt_base>>PAGE_SHIFT], 4.71 - pfn_type[mfn_to_pfn_table[ctxt.pt_base>>PAGE_SHIFT]], 4.72 - pgd[HYPERVISOR_VIRT_START>>L2_PAGETABLE_SHIFT] ); 4.73 -*/ 4.74 - mfn_to_pfn_table_start_mfn = pgd[HYPERVISOR_VIRT_START>>L2_PAGETABLE_SHIFT]>>PAGE_SHIFT; 4.75 + 4.76 + mfn_to_pfn_table_start_mfn = 4.77 + pgd[HYPERVISOR_VIRT_START>>L2_PAGETABLE_SHIFT]>>PAGE_SHIFT; 4.78 4.79 - live_mfn_to_pfn_table = 4.80 - mfn_mapper_map_single(xc_handle, ~0ULL, 4.81 - PAGE_SIZE*1024, PROT_READ, 4.82 - mfn_to_pfn_table_start_mfn ); 4.83 + live_mfn_to_pfn_table = 4.84 + mfn_mapper_map_single(xc_handle, ~0ULL, 4.85 + PAGE_SIZE*1024, PROT_READ, 4.86 + mfn_to_pfn_table_start_mfn ); 4.87 } 4.88 4.89 4.90 /* 4.91 - * Quick sanity check. 4.92 + * Quick belt and braces sanity check. 4.93 */ 4.94 4.95 for ( i = 0; i < srec.nr_pfns; i++ ) 4.96 @@ -270,50 +277,6 @@ int xc_linux_save(int xc_handle, 4.97 } 4.98 4.99 4.100 -/* test new pfn_type stuff */ 4.101 - { 4.102 - int n, i, j; 4.103 - 4.104 - if ( mlock( pfn_type, srec.nr_pfns * sizeof(unsigned long) ) ) 4.105 - { 4.106 - ERROR("Unable to mlock"); 4.107 - goto out; 4.108 - } 4.109 - for ( n = 0; n < srec.nr_pfns; ) 4.110 - { 4.111 - 4.112 - for( j = 0, i = n; j < BATCH_SIZE && i < srec.nr_pfns ; j++, i++ ) 4.113 - { 4.114 - pfn_type[i] = live_pfn_to_mfn_table[i]; 4.115 - } 4.116 - 4.117 - if ( get_pfn_type_batch(xc_handle, domid, j, &pfn_type[n]) ) 4.118 - { 4.119 - ERROR("get_pfn_type_batch failed"); 4.120 - goto out; 4.121 - } 4.122 - 4.123 - for( j = 0, i = n; j < BATCH_SIZE && i < srec.nr_pfns ; j++, i++ ) 4.124 - { 4.125 - 4.126 - pfn_type[i] >>= 29; 4.127 - 4.128 - if(pfn_type[i] == 7) 4.129 - { 4.130 - ERROR("bogus page"); 4.131 - goto out; 4.132 - } 4.133 - 4.134 -/* if(pfn_type[i]) 4.135 - printf("i=%d type=%d\n",i,pfn_type[i]); */ 4.136 - } 4.137 - 4.138 - n+=j; 4.139 - } 4.140 - } 4.141 - 4.142 - 4.143 - 4.144 /* Canonicalise the suspend-record frame number. */ 4.145 if ( !translate_mfn_to_pfn(&ctxt.cpu_ctxt.esi) ) 4.146 { 4.147 @@ -366,8 +329,7 @@ int xc_linux_save(int xc_handle, 4.148 !checked_write(gfd, &srec.nr_pfns, sizeof(unsigned long)) || 4.149 !checked_write(gfd, &ctxt, sizeof(ctxt)) || 4.150 !checked_write(gfd, live_shinfo, PAGE_SIZE) || 4.151 - !checked_write(gfd, pfn_to_mfn_frame_list, PAGE_SIZE) || 4.152 - !checked_write(gfd, pfn_type, 4 * srec.nr_pfns) ) 4.153 + !checked_write(gfd, pfn_to_mfn_frame_list, PAGE_SIZE) ) 4.154 { 4.155 ERROR("Error when writing to state file"); 4.156 goto out; 4.157 @@ -394,6 +356,11 @@ int xc_linux_save(int xc_handle, 4.158 prev_pc = this_pc; 4.159 } 4.160 4.161 + for( j = 0, i = n; j < BATCH_SIZE && i < srec.nr_pfns ; j++, i++ ) 4.162 + { 4.163 + pfn_type[j] = live_pfn_to_mfn_table[i]; 4.164 + } 4.165 + 4.166 4.167 for( j = 0, i = n; j < BATCH_SIZE && i < srec.nr_pfns ; j++, i++ ) 4.168 { 4.169 @@ -411,32 +378,54 @@ int xc_linux_save(int xc_handle, 4.170 goto out; 4.171 } 4.172 4.173 -#if 0 4.174 - typer_handle = get_type_init( xc_handle, BATCH_SIZE ) 4.175 - 4.176 + if ( get_pfn_type_batch(xc_handle, domid, j, pfn_type) ) 4.177 + { 4.178 + ERROR("get_pfn_type_batch failed"); 4.179 + goto out; 4.180 + } 4.181 + 4.182 for( j = 0, i = n; j < BATCH_SIZE && i < srec.nr_pfns ; j++, i++ ) 4.183 { 4.184 - /* queue up ownership and type checks for all pages in batch */ 4.185 + if((pfn_type[j]>>29) == 7) 4.186 + { 4.187 + ERROR("bogus page"); 4.188 + goto out; 4.189 + } 4.190 4.191 - get_type_queue_entry( typer_handle, domain, 4.192 - pfn_to_mfn_frame_list[i] ); 4.193 + /* canonicalise mfn->pfn */ 4.194 + pfn_type[j] = (pfn_type[j] & PGT_type_mask) | 4.195 + live_mfn_to_pfn_table[pfn_type[j]&~PGT_type_mask]; 4.196 + 4.197 +/* if(pfn_type[j]>>29) 4.198 + printf("i=%d type=%d\n",i,pfn_type[i]); */ 4.199 } 4.200 4.201 - region_type = get_type; 4.202 + 4.203 + if ( !checked_write(gfd, &j, sizeof(int) ) ) 4.204 + { 4.205 + ERROR("Error when writing to state file"); 4.206 + goto out; 4.207 + } 4.208 4.209 -#endif 4.210 + if ( !checked_write(gfd, pfn_type, sizeof(unsigned long)*j ) ) 4.211 + { 4.212 + ERROR("Error when writing to state file"); 4.213 + goto out; 4.214 + } 4.215 + 4.216 4.217 for( j = 0, i = n; j < BATCH_SIZE && i < srec.nr_pfns ; j++, i++ ) 4.218 { 4.219 /* write out pages in batch */ 4.220 4.221 - if ( (pfn_type[i] == L1TAB) || (pfn_type[i] == L2TAB) ) 4.222 + if ( ((pfn_type[j] & PGT_type_mask) == L1TAB) || 4.223 + ((pfn_type[j] & PGT_type_mask) == L2TAB) ) 4.224 { 4.225 4.226 memcpy(page, region_base + (PAGE_SIZE*j), PAGE_SIZE); 4.227 4.228 for ( k = 0; 4.229 - k < ((pfn_type[i] == L2TAB) ? 4.230 + k < (((pfn_type[j] & PGT_type_mask) == L2TAB) ? 4.231 (HYPERVISOR_VIRT_START >> L2_PAGETABLE_SHIFT) : 1024); 4.232 k++ ) 4.233 { 4.234 @@ -478,6 +467,14 @@ int xc_linux_save(int xc_handle, 4.235 /* Success! */ 4.236 rc = 0; 4.237 4.238 + /* Zero terminate */ 4.239 + if ( !checked_write(gfd, &rc, sizeof(int)) ) 4.240 + { 4.241 + ERROR("Error when writing to state file"); 4.242 + goto out; 4.243 + } 4.244 + 4.245 + 4.246 out: 4.247 /* Restart the domain if we had to stop it to save its state. */ 4.248 if ( we_stopped_it )
5.1 --- a/tools/xc/lib/xc_netbsd_build.c Sat May 01 00:30:43 2004 +0000 5.2 +++ b/tools/xc/lib/xc_netbsd_build.c Sat May 01 22:14:05 2004 +0000 5.3 @@ -27,6 +27,7 @@ static long get_tot_pages(int xc_handle, 5.4 dom0_op_t op; 5.5 op.cmd = DOM0_GETDOMAININFO; 5.6 op.u.getdomaininfo.domain = (domid_t)domid; 5.7 + op.u.getdomaininfo.ctxt = NULL; 5.8 return (do_dom0_op(xc_handle, &op) < 0) ? 5.9 -1 : op.u.getdomaininfo.tot_pages; 5.10 } 5.11 @@ -59,7 +60,7 @@ static int setup_guestos(int xc_handle, 5.12 unsigned long tot_pages, 5.13 unsigned long *virt_startinfo_addr, 5.14 unsigned long *virt_load_addr, 5.15 - dom0_builddomain_t *builddomain, 5.16 + full_execution_context_t *ctxt, 5.17 const char *cmdline, 5.18 unsigned long shared_info_frame, 5.19 unsigned int control_evtchn) 5.20 @@ -78,8 +79,6 @@ static int setup_guestos(int xc_handle, 5.21 mmu_t *mmu = NULL; 5.22 int pm_handle, i; 5.23 5.24 - memset(builddomain, 0, sizeof(*builddomain)); 5.25 - 5.26 if ( (pm_handle = init_pfn_mapper((domid_t)dom)) < 0 ) 5.27 goto error_out; 5.28 5.29 @@ -119,7 +118,7 @@ static int setup_guestos(int xc_handle, 5.30 */ 5.31 l2tab = page_array[alloc_index] << PAGE_SHIFT; 5.32 alloc_index--; 5.33 - builddomain->ctxt.pt_base = l2tab; 5.34 + ctxt->pt_base = l2tab; 5.35 5.36 if ( (mmu = init_mmu_updates(xc_handle, dom)) == NULL ) 5.37 goto error_out; 5.38 @@ -221,7 +220,7 @@ int xc_netbsd_build(int xc_handle, 5.39 int kernel_fd = -1; 5.40 gzFile kernel_gfd = NULL; 5.41 int rc, i; 5.42 - full_execution_context_t *ctxt; 5.43 + full_execution_context_t st_ctxt, *ctxt = &st_ctxt; 5.44 unsigned long virt_startinfo_addr; 5.45 5.46 if ( (tot_pages = get_tot_pages(xc_handle, domid)) < 0 ) 5.47 @@ -244,8 +243,15 @@ int xc_netbsd_build(int xc_handle, 5.48 return 1; 5.49 } 5.50 5.51 + if ( mlock(&st_ctxt, sizeof(st_ctxt) ) ) 5.52 + { 5.53 + PERROR("Unable to mlock ctxt"); 5.54 + return 1; 5.55 + } 5.56 + 5.57 op.cmd = DOM0_GETDOMAININFO; 5.58 op.u.getdomaininfo.domain = (domid_t)domid; 5.59 + op.u.getdomaininfo.ctxt = ctxt; 5.60 if ( (do_dom0_op(xc_handle, &op) < 0) || 5.61 ((u64)op.u.getdomaininfo.domain != domid) ) 5.62 { 5.63 @@ -253,7 +259,7 @@ int xc_netbsd_build(int xc_handle, 5.64 goto error_out; 5.65 } 5.66 if ( (op.u.getdomaininfo.state != DOMSTATE_STOPPED) || 5.67 - (op.u.getdomaininfo.ctxt.pt_base != 0) ) 5.68 + (op.u.getdomaininfo.ctxt->pt_base != 0) ) 5.69 { 5.70 ERROR("Domain is already constructed"); 5.71 goto error_out; 5.72 @@ -261,7 +267,7 @@ int xc_netbsd_build(int xc_handle, 5.73 5.74 if ( setup_guestos(xc_handle, domid, kernel_gfd, tot_pages, 5.75 &virt_startinfo_addr, 5.76 - &load_addr, &launch_op.u.builddomain, cmdline, 5.77 + &load_addr, &st_ctxt, cmdline, 5.78 op.u.getdomaininfo.shared_info_frame, 5.79 control_evtchn) < 0 ) 5.80 { 5.81 @@ -274,8 +280,6 @@ int xc_netbsd_build(int xc_handle, 5.82 if( kernel_gfd ) 5.83 gzclose(kernel_gfd); 5.84 5.85 - ctxt = &launch_op.u.builddomain.ctxt; 5.86 - 5.87 ctxt->flags = 0; 5.88 5.89 /* 5.90 @@ -328,9 +332,11 @@ int xc_netbsd_build(int xc_handle, 5.91 ctxt->failsafe_callback_cs = FLAT_GUESTOS_CS; 5.92 ctxt->failsafe_callback_eip = 0; 5.93 5.94 + memset( &launch_op, 0, sizeof(launch_op) ); 5.95 + 5.96 launch_op.u.builddomain.domain = (domid_t)domid; 5.97 launch_op.u.builddomain.num_vifs = 1; 5.98 - 5.99 + launch_op.u.builddomain.ctxt = ctxt; 5.100 launch_op.cmd = DOM0_BUILDDOMAIN; 5.101 rc = do_dom0_op(xc_handle, &launch_op); 5.102
6.1 --- a/xen/common/dom0_ops.c Sat May 01 00:30:43 2004 +0000 6.2 +++ b/xen/common/dom0_ops.c Sat May 01 22:14:05 2004 +0000 6.3 @@ -53,24 +53,19 @@ static void read_msr_for(void *unused) 6.4 long do_dom0_op(dom0_op_t *u_dom0_op) 6.5 { 6.6 long ret = 0; 6.7 - dom0_op_t *op; 6.8 + dom0_op_t curop,*op=&curop; 6.9 6.10 if ( !IS_PRIV(current) ) 6.11 return -EPERM; 6.12 6.13 - if ( (op = kmalloc(sizeof(*op), GFP_KERNEL)) == NULL ) 6.14 - return -ENOMEM; 6.15 - 6.16 if ( copy_from_user(op, u_dom0_op, sizeof(*op)) ) 6.17 { 6.18 - ret = -EFAULT; 6.19 - goto out; 6.20 + return -EFAULT; 6.21 } 6.22 6.23 if ( op->interface_version != DOM0_INTERFACE_VERSION ) 6.24 { 6.25 - ret = -EACCES; 6.26 - goto out; 6.27 + return -EACCES; 6.28 } 6.29 6.30 switch ( op->cmd ) 6.31 @@ -269,15 +264,17 @@ long do_dom0_op(dom0_op_t *u_dom0_op) 6.32 for_each_domain ( p ) 6.33 { 6.34 if ( p->domain >= op->u.getdomaininfo.domain ) 6.35 - break; 6.36 + break; 6.37 } 6.38 6.39 if ( p == NULL ) 6.40 { 6.41 ret = -ESRCH; 6.42 + goto gdi_out; 6.43 } 6.44 else 6.45 { 6.46 +printk("AAAA %p\n",op->u.getdomaininfo.ctxt); 6.47 op->u.getdomaininfo.domain = p->domain; 6.48 strcpy (op->u.getdomaininfo.name, p->name); 6.49 op->u.getdomaininfo.processor = p->processor; 6.50 @@ -291,59 +288,81 @@ long do_dom0_op(dom0_op_t *u_dom0_op) 6.51 op->u.getdomaininfo.cpu_time = p->cpu_time; 6.52 op->u.getdomaininfo.shared_info_frame = 6.53 __pa(p->shared_info) >> PAGE_SHIFT; 6.54 - if ( p->state == TASK_STOPPED ) 6.55 + 6.56 + if ( p->state == TASK_STOPPED && op->u.getdomaininfo.ctxt ) 6.57 { 6.58 + full_execution_context_t *c=NULL; 6.59 + 6.60 + if ( (c = kmalloc(sizeof(*c), GFP_KERNEL)) == NULL ) 6.61 + { 6.62 + ret= -ENOMEM; 6.63 + goto gdi_out; 6.64 + } 6.65 + 6.66 rmb(); /* Ensure that we see saved register state. */ 6.67 - op->u.getdomaininfo.ctxt.flags = 0; 6.68 - memcpy(&op->u.getdomaininfo.ctxt.cpu_ctxt, 6.69 + c->flags = 0; 6.70 + memcpy(&c->cpu_ctxt, 6.71 &p->shared_info->execution_context, 6.72 sizeof(p->shared_info->execution_context)); 6.73 if ( test_bit(PF_DONEFPUINIT, &p->flags) ) 6.74 - op->u.getdomaininfo.ctxt.flags |= ECF_I387_VALID; 6.75 - memcpy(&op->u.getdomaininfo.ctxt.fpu_ctxt, 6.76 + c->flags |= ECF_I387_VALID; 6.77 + memcpy(&c->fpu_ctxt, 6.78 &p->thread.i387, 6.79 sizeof(p->thread.i387)); 6.80 - memcpy(&op->u.getdomaininfo.ctxt.trap_ctxt, 6.81 + memcpy(&c->trap_ctxt, 6.82 p->thread.traps, 6.83 sizeof(p->thread.traps)); 6.84 #ifdef ARCH_HAS_FAST_TRAP 6.85 if ( (p->thread.fast_trap_desc.a == 0) && 6.86 (p->thread.fast_trap_desc.b == 0) ) 6.87 - op->u.getdomaininfo.ctxt.fast_trap_idx = 0; 6.88 + c->fast_trap_idx = 0; 6.89 else 6.90 - op->u.getdomaininfo.ctxt.fast_trap_idx = 6.91 + c->fast_trap_idx = 6.92 p->thread.fast_trap_idx; 6.93 #endif 6.94 - op->u.getdomaininfo.ctxt.ldt_base = p->mm.ldt_base; 6.95 - op->u.getdomaininfo.ctxt.ldt_ents = p->mm.ldt_ents; 6.96 - op->u.getdomaininfo.ctxt.gdt_ents = 0; 6.97 + c->ldt_base = p->mm.ldt_base; 6.98 + c->ldt_ents = p->mm.ldt_ents; 6.99 + c->gdt_ents = 0; 6.100 if ( GET_GDT_ADDRESS(p) == GDT_VIRT_START ) 6.101 { 6.102 for ( i = 0; i < 16; i++ ) 6.103 - op->u.getdomaininfo.ctxt.gdt_frames[i] = 6.104 + c->gdt_frames[i] = 6.105 l1_pgentry_to_pagenr(p->mm.perdomain_pt[i]); 6.106 - op->u.getdomaininfo.ctxt.gdt_ents = 6.107 + c->gdt_ents = 6.108 (GET_GDT_ENTRIES(p) + 1) >> 3; 6.109 } 6.110 - op->u.getdomaininfo.ctxt.guestos_ss = p->thread.guestos_ss; 6.111 - op->u.getdomaininfo.ctxt.guestos_esp = p->thread.guestos_sp; 6.112 - op->u.getdomaininfo.ctxt.pt_base = 6.113 + c->guestos_ss = p->thread.guestos_ss; 6.114 + c->guestos_esp = p->thread.guestos_sp; 6.115 + c->pt_base = 6.116 pagetable_val(p->mm.pagetable); 6.117 - memcpy(op->u.getdomaininfo.ctxt.debugreg, 6.118 + memcpy(c->debugreg, 6.119 p->thread.debugreg, 6.120 sizeof(p->thread.debugreg)); 6.121 - op->u.getdomaininfo.ctxt.event_callback_cs = 6.122 + c->event_callback_cs = 6.123 p->event_selector; 6.124 - op->u.getdomaininfo.ctxt.event_callback_eip = 6.125 + c->event_callback_eip = 6.126 p->event_address; 6.127 - op->u.getdomaininfo.ctxt.failsafe_callback_cs = 6.128 + c->failsafe_callback_cs = 6.129 p->failsafe_selector; 6.130 - op->u.getdomaininfo.ctxt.failsafe_callback_eip = 6.131 + c->failsafe_callback_eip = 6.132 p->failsafe_address; 6.133 + 6.134 + if( copy_to_user(op->u.getdomaininfo.ctxt, c, sizeof(*c)) ) 6.135 + { 6.136 +printk("URGHT %p\n",op->u.getdomaininfo.ctxt); 6.137 + ret = -EINVAL; 6.138 + } 6.139 + 6.140 + if (c) kfree(c); 6.141 } 6.142 } 6.143 + 6.144 + if ( copy_to_user(u_dom0_op, op, sizeof(*op)) ) 6.145 + ret = -EINVAL; 6.146 + 6.147 + gdi_out: 6.148 read_unlock_irqrestore(&tasklist_lock, flags); 6.149 - copy_to_user(u_dom0_op, op, sizeof(*op)); 6.150 + 6.151 } 6.152 break; 6.153 6.154 @@ -647,7 +666,5 @@ long do_dom0_op(dom0_op_t *u_dom0_op) 6.155 6.156 } 6.157 6.158 - out: 6.159 - kfree(op); 6.160 return ret; 6.161 }
7.1 --- a/xen/common/domain.c Sat May 01 00:30:43 2004 +0000 7.2 +++ b/xen/common/domain.c Sat May 01 22:14:05 2004 +0000 7.3 @@ -535,45 +535,58 @@ void release_task(struct task_struct *p) 7.4 int final_setup_guestos(struct task_struct *p, dom0_builddomain_t *builddomain) 7.5 { 7.6 unsigned long phys_basetab; 7.7 - int i; 7.8 + int i, rc = 0; 7.9 + full_execution_context_t *c; 7.10 + 7.11 + if ( (c = kmalloc(sizeof(*c), GFP_KERNEL)) == NULL ) 7.12 + return -ENOMEM; 7.13 7.14 if ( test_bit(PF_CONSTRUCTED, &p->flags) ) 7.15 - return -EINVAL; 7.16 + { 7.17 + rc = -EINVAL; 7.18 + goto out; 7.19 + } 7.20 + 7.21 + if ( copy_from_user(c, builddomain->ctxt, sizeof(*c)) ) 7.22 + { 7.23 + rc = -EFAULT; 7.24 + goto out; 7.25 + } 7.26 7.27 clear_bit(PF_DONEFPUINIT, &p->flags); 7.28 - if ( builddomain->ctxt.flags & ECF_I387_VALID ) 7.29 + if ( c->flags & ECF_I387_VALID ) 7.30 set_bit(PF_DONEFPUINIT, &p->flags); 7.31 memcpy(&p->shared_info->execution_context, 7.32 - &builddomain->ctxt.cpu_ctxt, 7.33 + &c->cpu_ctxt, 7.34 sizeof(p->shared_info->execution_context)); 7.35 memcpy(&p->thread.i387, 7.36 - &builddomain->ctxt.fpu_ctxt, 7.37 + &c->fpu_ctxt, 7.38 sizeof(p->thread.i387)); 7.39 memcpy(p->thread.traps, 7.40 - &builddomain->ctxt.trap_ctxt, 7.41 + &c->trap_ctxt, 7.42 sizeof(p->thread.traps)); 7.43 #ifdef ARCH_HAS_FAST_TRAP 7.44 SET_DEFAULT_FAST_TRAP(&p->thread); 7.45 - (void)set_fast_trap(p, builddomain->ctxt.fast_trap_idx); 7.46 + (void)set_fast_trap(p, c->fast_trap_idx); 7.47 #endif 7.48 - p->mm.ldt_base = builddomain->ctxt.ldt_base; 7.49 - p->mm.ldt_ents = builddomain->ctxt.ldt_ents; 7.50 + p->mm.ldt_base = c->ldt_base; 7.51 + p->mm.ldt_ents = c->ldt_ents; 7.52 SET_GDT_ENTRIES(p, DEFAULT_GDT_ENTRIES); 7.53 SET_GDT_ADDRESS(p, DEFAULT_GDT_ADDRESS); 7.54 - if ( builddomain->ctxt.gdt_ents != 0 ) 7.55 + if ( c->gdt_ents != 0 ) 7.56 (void)set_gdt(p, 7.57 - builddomain->ctxt.gdt_frames, 7.58 - builddomain->ctxt.gdt_ents); 7.59 - p->thread.guestos_ss = builddomain->ctxt.guestos_ss; 7.60 - p->thread.guestos_sp = builddomain->ctxt.guestos_esp; 7.61 + c->gdt_frames, 7.62 + c->gdt_ents); 7.63 + p->thread.guestos_ss = c->guestos_ss; 7.64 + p->thread.guestos_sp = c->guestos_esp; 7.65 for ( i = 0; i < 8; i++ ) 7.66 - (void)set_debugreg(p, i, builddomain->ctxt.debugreg[i]); 7.67 - p->event_selector = builddomain->ctxt.event_callback_cs; 7.68 - p->event_address = builddomain->ctxt.event_callback_eip; 7.69 - p->failsafe_selector = builddomain->ctxt.failsafe_callback_cs; 7.70 - p->failsafe_address = builddomain->ctxt.failsafe_callback_eip; 7.71 + (void)set_debugreg(p, i, c->debugreg[i]); 7.72 + p->event_selector = c->event_callback_cs; 7.73 + p->event_address = c->event_callback_eip; 7.74 + p->failsafe_selector = c->failsafe_callback_cs; 7.75 + p->failsafe_address = c->failsafe_callback_eip; 7.76 7.77 - phys_basetab = builddomain->ctxt.pt_base; 7.78 + phys_basetab = c->pt_base; 7.79 p->mm.pagetable = mk_pagetable(phys_basetab); 7.80 get_page_and_type(&frame_table[phys_basetab>>PAGE_SHIFT], p, 7.81 PGT_base_page_table); 7.82 @@ -586,8 +599,11 @@ int final_setup_guestos(struct task_stru 7.83 (void)create_net_vif(p->domain); 7.84 7.85 set_bit(PF_CONSTRUCTED, &p->flags); 7.86 + 7.87 +out: 7.88 + if (c) kfree(c); 7.89 7.90 - return 0; 7.91 + return rc; 7.92 } 7.93 7.94 static inline int is_loadable_phdr(Elf_Phdr *phdr)
8.1 --- a/xen/include/hypervisor-ifs/dom0_ops.h Sat May 01 00:30:43 2004 +0000 8.2 +++ b/xen/include/hypervisor-ifs/dom0_ops.h Sat May 01 22:14:05 2004 +0000 8.3 @@ -80,6 +80,7 @@ typedef struct dom0_getdomaininfo_st 8.4 { 8.5 /* IN variables. */ 8.6 domid_t domain; 8.7 + full_execution_context_t *ctxt; 8.8 /* OUT variables. */ 8.9 char name[MAX_DOMAIN_NAME]; 8.10 int processor; 8.11 @@ -91,7 +92,6 @@ typedef struct dom0_getdomaininfo_st 8.12 unsigned int tot_pages, max_pages; 8.13 long long cpu_time; 8.14 unsigned long shared_info_frame; /* MFN of shared_info struct */ 8.15 - full_execution_context_t ctxt; 8.16 } dom0_getdomaininfo_t; 8.17 8.18 #define DOM0_BUILDDOMAIN 13 8.19 @@ -100,7 +100,8 @@ typedef struct dom0_builddomain_st 8.20 /* IN variables. */ 8.21 domid_t domain; 8.22 unsigned int num_vifs; 8.23 - full_execution_context_t ctxt; 8.24 + /* IN/OUT parameters */ 8.25 + full_execution_context_t *ctxt; 8.26 } dom0_builddomain_t; 8.27 8.28 #define DOM0_IOPL 14 8.29 @@ -152,7 +153,8 @@ typedef struct dom0_getpageframeinfo_st 8.30 domid_t domain; /* To which domain does the frame belong? */ 8.31 /* OUT variables. */ 8.32 /* Is the page PINNED to a type? */ 8.33 - enum { NONE, L1TAB, L2TAB, L3TAB, L4TAB } type; 8.34 + enum { NONE, L1TAB=(1<<29), L2TAB=(2<<29), L3TAB=(3<<29), L4TAB=(4<<29) } type; 8.35 +#define PGT_type_mask (7<<29) 8.36 } dom0_getpageframeinfo_t; 8.37 8.38