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

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