SimpleOS

LXR

Navigation



Site hébergé par : enix

The LXR Cross Referencer for SOS

source navigation ]
diff markup ]
identifier search ]
general search ]
 
 
Article:1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 6.5 ] [ 7 ] [ 7.5 ] [ 8 ] [ 9 ] [ 9.5 ]

001 /* Copyright (C) 2004  David Decotigny
002    Copyright (C) 2000  The KOS Team
003 
004    This program is free software; you can redistribute it and/or
005    modify it under the terms of the GNU General Public License
006    as published by the Free Software Foundation; either version 2
007    of the License, or (at your option) any later version.
008    
009    This program is distributed in the hope that it will be useful,
010    but WITHOUT ANY WARRANTY; without even the implied warranty of
011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012    GNU General Public License for more details.
013    
014    You should have received a copy of the GNU General Public License
015    along with this program; if not, write to the Free Software
016    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
017    USA. 
018 */
019 #include <sos/list.h>
020 #include <sos/macros.h>
021 #include <sos/assert.h>
022 #include <sos/klibc.h>
023 
024 #include "physmem.h"
025 
026 /** A descriptor for a physical page in SOS */
027 struct physical_page_descr
028 {
029   /** The physical base address for the page */
030   sos_paddr_t paddr;
031 
032   /** The reference count for this physical page. > 0 means that the
033      page is in the used list. */
034   sos_count_t ref_cnt;
035 
036   /** Some data associated with the page when it is mapped in kernel space */
037   struct sos_kmem_range *kernel_range;
038 
039   /** The other pages on the list (used, free) */
040   struct physical_page_descr *prev, *next;
041 };
042 
043 /** These are some markers present in the executable file (see sos.lds) */
044 extern char __b_kernel, __e_kernel;
045 
046 /** The array of ppage descriptors will be located at this address */
047 #define PAGE_DESCR_ARRAY_ADDR \
048   SOS_PAGE_ALIGN_SUP((sos_paddr_t) (& __e_kernel))
049 static struct physical_page_descr * physical_page_descr_array;
050 
051 /** The list of physical pages currently available */
052 static struct physical_page_descr *free_ppage;
053 
054 /** The list of physical pages currently in use */
055 static struct physical_page_descr *used_ppage;
056 
057 /** We will store here the interval of valid physical addresses */
058 static sos_paddr_t physmem_base, physmem_top;
059 
060 /** We store the number of pages used/free */
061 static sos_count_t physmem_total_pages, physmem_used_pages;
062 
063 sos_ret_t sos_physmem_subsystem_setup(sos_size_t ram_size,
064                                       /* out */sos_paddr_t *kernel_core_base,
065                                       /* out */sos_paddr_t *kernel_core_top)
066 {
067   /* The iterator over the page descriptors */
068   struct physical_page_descr *ppage_descr;
069 
070   /* The iterator over the physical addresses */
071   sos_paddr_t ppage_addr;
072 
073   /* Make sure ram size is aligned on a page boundary */
074   ram_size = SOS_PAGE_ALIGN_INF(ram_size);/* Yes, we may lose at most a page */
075 
076   /* Reset the used/free page lists before building them */
077   free_ppage = used_ppage = NULL;
078   physmem_total_pages = physmem_used_pages = 0;
079 
080   /* Make sure that there is enough memory to store the array of page
081      descriptors */
082   *kernel_core_base = SOS_PAGE_ALIGN_INF((sos_paddr_t)(& __b_kernel));
083   *kernel_core_top
084     = PAGE_DESCR_ARRAY_ADDR
085       + SOS_PAGE_ALIGN_SUP( (ram_size >> SOS_PAGE_SHIFT)
086                             * sizeof(struct physical_page_descr));
087   if (*kernel_core_top > ram_size)
088     return -SOS_ENOMEM;
089 
090   /* Page 0-4kB is not available in order to return address 0 as a
091      means to signal "no page available" */
092   physmem_base = SOS_PAGE_SIZE;
093   physmem_top  = ram_size;
094 
095   /* Setup the page descriptor arrray */
096   physical_page_descr_array
097     = (struct physical_page_descr*)PAGE_DESCR_ARRAY_ADDR;
098 
099   /* Scan the list of physical pages */
100   for (ppage_addr = 0,
101          ppage_descr = physical_page_descr_array ;
102        ppage_addr < physmem_top ;
103        ppage_addr += SOS_PAGE_SIZE,
104          ppage_descr ++)
105     {
106       enum { PPAGE_MARK_RESERVED, PPAGE_MARK_FREE,
107              PPAGE_MARK_KERNEL, PPAGE_MARK_HWMAP } todo;
108 
109       memset(ppage_descr, 0x0, sizeof(struct physical_page_descr));
110 
111       /* Init the page descriptor for this page */
112       ppage_descr->paddr = ppage_addr;
113 
114       /* Reserved : 0 ... base */
115       if (ppage_addr < physmem_base)
116         todo = PPAGE_MARK_RESERVED;
117 
118       /* Free : base ... BIOS */
119       else if ((ppage_addr >= physmem_base)
120                && (ppage_addr < BIOS_N_VIDEO_START))
121         todo = PPAGE_MARK_FREE;
122 
123       /* Used : BIOS */
124       else if ((ppage_addr >= BIOS_N_VIDEO_START)
125                && (ppage_addr < BIOS_N_VIDEO_END))
126         todo = PPAGE_MARK_HWMAP;
127 
128       /* Free : BIOS ... kernel */
129       else if ((ppage_addr >= BIOS_N_VIDEO_END)
130                && (ppage_addr < (sos_paddr_t) (& __b_kernel)))
131         todo = PPAGE_MARK_FREE;
132 
133       /* Used : Kernel code/data/bss + physcal page descr array */
134       else if ((ppage_addr >= *kernel_core_base)
135                 && (ppage_addr < *kernel_core_top))
136         todo = PPAGE_MARK_KERNEL;
137 
138       /* Free : first page of descr ... end of RAM */
139       else
140         todo = PPAGE_MARK_FREE;
141 
142       /* Actually does the insertion in the used/free page lists */
143       physmem_total_pages ++;
144       switch (todo)
145         {
146         case PPAGE_MARK_FREE:
147           ppage_descr->ref_cnt = 0;
148           list_add_head(free_ppage, ppage_descr);
149           break;
150 
151         case PPAGE_MARK_KERNEL:
152         case PPAGE_MARK_HWMAP:
153           ppage_descr->ref_cnt = 1;
154           list_add_head(used_ppage, ppage_descr);
155           physmem_used_pages ++;
156           break;
157 
158         default:
159           /* Reserved page: nop */
160           break;
161         }
162     }
163 
164   return SOS_OK;
165 }
166 
167 
168 sos_paddr_t sos_physmem_ref_physpage_new(sos_bool_t can_block)
169 {
170   struct physical_page_descr *ppage_descr;
171 
172   if (! free_ppage)
173     return (sos_paddr_t)NULL;
174 
175   /* Retrieve a page in the free list */
176   ppage_descr = list_pop_head(free_ppage);
177 
178   /* The page is assumed not to be already used */
179   SOS_ASSERT_FATAL(ppage_descr->ref_cnt == 0);
180 
181   /* Mark the page as used (this of course sets the ref count to 1) */
182   ppage_descr->ref_cnt ++;
183 
184   /* No associated kernel range by default */
185   ppage_descr->kernel_range = NULL;
186   
187   /* Put the page in the used list */
188   list_add_tail(used_ppage, ppage_descr);
189   physmem_used_pages ++;
190 
191   return ppage_descr->paddr;
192 }
193 
194 
195 /**
196  * Helper function to get the physical page descriptor for the given
197  * physical page address.
198  *
199  * @return NULL when out-of-bounds or non-page-aligned
200  */
201 inline static struct physical_page_descr *
202 get_page_descr_at_paddr(sos_paddr_t ppage_paddr)
203 {
204   /* Don't handle non-page-aligned addresses */
205   if (ppage_paddr & SOS_PAGE_MASK)
206     return NULL;
207   
208   /* Don't support out-of-bounds requests */
209   if ((ppage_paddr < physmem_base) || (ppage_paddr >= physmem_top))
210     return NULL;
211 
212   return physical_page_descr_array + (ppage_paddr >> SOS_PAGE_SHIFT);
213 }
214 
215 
216 sos_ret_t sos_physmem_ref_physpage_at(sos_paddr_t ppage_paddr)
217 {
218   struct physical_page_descr *ppage_descr
219     = get_page_descr_at_paddr(ppage_paddr);
220 
221   if (! ppage_descr)
222     return -SOS_EINVAL;
223 
224   /* Increment the reference count for the page */
225   ppage_descr->ref_cnt ++;
226 
227   /* If the page is newly referenced (ie we are the only owners of the
228      page => ref cnt == 1), transfer it in the used pages list */
229   if (ppage_descr->ref_cnt == 1)
230     {
231       list_delete(free_ppage, ppage_descr);
232 
233       /* No associated kernel range by default */
234       ppage_descr->kernel_range = NULL;
235   
236       list_add_tail(used_ppage, ppage_descr);
237       physmem_used_pages ++;
238 
239       /* The page is newly referenced */
240       return FALSE;
241     }
242 
243   /* The page was already referenced by someone */
244   return TRUE;
245 }
246 
247 
248 sos_ret_t
249 sos_physmem_unref_physpage(sos_paddr_t ppage_paddr)
250 {
251   /* By default the return value indicates that the page is still
252      used */
253   sos_ret_t retval = FALSE;
254 
255   struct physical_page_descr *ppage_descr
256     = get_page_descr_at_paddr(ppage_paddr);
257 
258   if (! ppage_descr)
259     return -SOS_EINVAL;
260 
261   /* Don't do anything if the page is not in the used list */
262   if (ppage_descr->ref_cnt <= 0)
263     return -SOS_EINVAL;
264 
265   /* Unreference the page, and, when no mapping is active anymore, put
266      the page in the free list */
267   ppage_descr->ref_cnt--;
268   if (ppage_descr->ref_cnt <= 0)
269     {
270       /* Reset associated kernel range */
271       ppage_descr->kernel_range = NULL;
272   
273       /* Transfer the page, considered USED, to the free list */
274       list_delete(used_ppage, ppage_descr);
275       physmem_used_pages --;
276       list_add_head(free_ppage, ppage_descr);
277 
278       /* Indicate that the page is now unreferenced */
279       retval = TRUE;
280     }
281 
282   return retval;
283 }
284 
285 
286 struct sos_kmem_range* sos_physmem_get_kmem_range(sos_paddr_t ppage_paddr)
287 {
288   struct physical_page_descr *ppage_descr
289     = get_page_descr_at_paddr(ppage_paddr);
290 
291   if (! ppage_descr)
292     return NULL;
293 
294   return ppage_descr->kernel_range;
295 }
296 
297 
298 sos_ret_t sos_physmem_set_kmem_range(sos_paddr_t ppage_paddr,
299                                      struct sos_kmem_range *range)
300 {
301   struct physical_page_descr *ppage_descr
302     = get_page_descr_at_paddr(ppage_paddr);
303 
304   if (! ppage_descr)
305     return -SOS_EINVAL;
306 
307   ppage_descr->kernel_range = range;
308   return SOS_OK;
309 }
310 
311 sos_ret_t sos_physmem_get_state(/* out */sos_count_t *total_ppages,
312                                 /* out */sos_count_t *used_ppages)
313 {
314   if (total_ppages)
315     *total_ppages = physmem_total_pages;
316   if (used_ppages)
317     *used_ppages = physmem_used_pages;
318   return SOS_OK;
319 }

source navigation ] diff markup ] identifier search ] general search ]