From 6c6d77cfa68c9fd06504a17a545ce4bc832c2069 Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Tue, 5 Apr 2011 14:44:42 +0200 Subject: [PATCH] Committed the rest. There is only few stuff missing. Embedded nice pictures. --- .gitignore | 4 +- Doxyfile | 11 +- documentation/doxy_mainpage.h | 47 ++ documentation/img/lfbs_logo.gif | Bin 0 -> 3472 bytes documentation/img/metalsvm_stack.jpg | Bin 0 -> 76026 bytes documentation/img/rwth_logo.gif | Bin 0 -> 4214 bytes documentation/tmpl/footer.html | 4 + documentation/tmpl/header.html | 27 + documentation/tmpl/stylesheet.css | 814 +++++++++++++++++++++++++++ include/metalsvm/fs.h | 115 +++- include/metalsvm/mailbox_types.h | 11 + include/metalsvm/tasks.h | 6 +- include/metalsvm/time.h | 23 +- include/metalsvm/vma.h | 36 +- kernel/tasks.c | 86 ++- 15 files changed, 1162 insertions(+), 22 deletions(-) create mode 100644 documentation/doxy_mainpage.h create mode 100644 documentation/img/lfbs_logo.gif create mode 100644 documentation/img/metalsvm_stack.jpg create mode 100644 documentation/img/rwth_logo.gif create mode 100644 documentation/tmpl/footer.html create mode 100644 documentation/tmpl/header.html create mode 100644 documentation/tmpl/stylesheet.css diff --git a/.gitignore b/.gitignore index 244ebdef..d237657d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ *.pcap *.img tags +include/metalsvm/config.h +Makefile tools/make_initrd newlib/examples/hello newlib/examples/echo @@ -13,4 +15,4 @@ newlib/examples/tests newlib/tmp/* newlib/x86/* metalsvm.elf -documentation/html +documentation/html/ diff --git a/Doxyfile b/Doxyfile index 8a16db2e..352ee44e 100644 --- a/Doxyfile +++ b/Doxyfile @@ -605,7 +605,8 @@ INPUT = ./fs \ ./arch \ ./libkern \ ./mm \ - ./tools + ./tools \ + ./documentation/doxy_mainpage.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -683,7 +684,7 @@ EXAMPLE_RECURSIVE = NO # directories that contain image that are included in the documentation (see # the \image command). -IMAGE_PATH = +IMAGE_PATH = ./documentation/img/ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -825,13 +826,13 @@ HTML_FILE_EXTENSION = .html # each generated HTML page. If it is left blank doxygen will generate a # standard header. -HTML_HEADER = +HTML_HEADER = ./documentation/tmpl/header.html # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. -HTML_FOOTER = +HTML_FOOTER = ./documentation/tmpl/footer.html # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to @@ -840,7 +841,7 @@ HTML_FOOTER = # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! -HTML_STYLESHEET = +HTML_STYLESHEET = ./documentation/tmpl/stylesheet.css # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images diff --git a/documentation/doxy_mainpage.h b/documentation/doxy_mainpage.h new file mode 100644 index 00000000..1420ef75 --- /dev/null +++ b/documentation/doxy_mainpage.h @@ -0,0 +1,47 @@ +/** + * @file doxy_mainpage.h + * @mainpage + * + * @section Introduction + * + * On a traditional multicore system, a single operating system + * manages all cores and schedules threads and processes among + * them, inherently supported by hardware-implemented cache + * coherence protocols. However, a further growth of the number + * of cores per system implies an increasing chip complexity, + * especially with respect to the cache coherence protocols. + * Therefore, a very attractive alternative for future many-core + * systems is to waive the hardware-based cache coherency and to + * introduce a software-oriented message-passing based architecture + * instead: a so-called Cluster-on-Chip architecture. + * Intel's Single-chip Cloud Computer (SCC), a many-core research + * processor with 48 non-coherent memory-coupled cores, is a very + * recent example for such a Cluster-on-Chip architecture. The SCC + * can be configured to run one operating system per core by + * partitioning the shared main memory in a strict manner. However, + * it is also possible to access the shared main memory in an unsplit + * and concurrent manner, provided that the cache coherency is then + * ensured by software. + * + * @section Research Objective + * + * In this project, we develop a new approach for a SCC-related shared + * virtual memory management system, called MetalSVM, that will be + * implemented in terms of a bare-metal hypervisor, located within a + * virtualization layer between the SCC's hardware and the actual + * operating system. This new hypervisor will undertake the crucial + * task of coherency management by utilizing special SCC-related features + * as, for example, its on-die Message-Passing Buffers (MPB). That way, + * common Linux kernels will be able to run almost transparently across + * the entire SCC system. However, in order to offer a maximum of flexibility + * with respect to resource allocation as well as to an efficiency-adjusted + * degree of parallelism, also a dynamic partitioning of the SCC's computing + * resources into several coherency domains will be made possible. + * + * @image html metalsvm_stack.jpg + * + * @section Acknowledgment + * + * This research project is funded by Intel Corporation. + * + */ diff --git a/documentation/img/lfbs_logo.gif b/documentation/img/lfbs_logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..2760f9bc9c74681525959f327eb8efd89eb2b555 GIT binary patch literal 3472 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%0A&q11*sgyLo$@R=J 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 R0E#n#mMaDhEN1+F69AM1E$;vT literal 0 HcmV?d00001 diff --git a/documentation/img/rwth_logo.gif b/documentation/img/rwth_logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..d5db607be0f67158511adf3fa1f6dc726db580cd GIT binary patch literal 4214 zcmV-+5Q*shMIho61tI(y$ z=c32i$k*ORpUn@4#iq#GmciIprq9dR-zk#GWv$Twf5A4H%LIeO$k*mJm&v}<=fBe9 zxX|3I%Gy1f%N2{pQKQvQqScMP)`Gd!AdkmyvC`Y$;ZCB?ugu#omC2F6*J-ZN$k*iG z;o_^x=hEBbztiR5;o_>w=eW@3iM-aS$>+k<w9e+w+T`Kl z;=9r1$=BpFm&&Qi=hNKdt;^@t+~cpz<|dKI&f4VL-{V-P&$Z9y)7#_T;NlyM$GOnv zzSHHp(dEk6AKVF+~DHI*XFg+>!``vz|`G{yVlm-;F!YL0D!_=sL(*1 z%-!JOFqXh+)YRPM#n$CtsMkcH(;SS)N37Jx*51a}-VTMsT&LC$h{dM=ET+Bd$!a}qRp+$=~brBER@L@i^jLn=)Kb9%Gl+}*W*y5 z&cfB@l)=}x(A-|B(AeJOi@nxuu+qNL-OSnNN~h7n)8a^=)7RbPKb_5MuhP@p;NIZl z+TP;0((Ad==%B^fztrfO!`K9a!@AJpS*Op#)!y9ToNNU-3*LLI35`-kF~!B7Jh{%b_Y zAPsQpBr>qbv17+yqgqI8L8y<(liVaOjPasnOF1fI7IE1!M$CmR!gyh$9dQ(idoADwT7gpVg~auL3$m=+vjIRxL=3bpUKXOA_CiUCS0M2^21HEV-~sMq5EYn{D(KQ6QX)xMqEhvCC4ZvHz%nUBgQb^;Tz zBO`DXA3!u#oj;O67g!jV1vZCGi*bgXdHs|S1XvFEaaw9-Q9>Ssub~&0ZCT+a(H$<- zcawXRRiV-tfjx1>8ifTWM}m1dCQ)()<|V`#F!bmn3~xw5nHo1b2-X2NB-ve7)o5du z2p6uUhlQ?P^@tN#I-$psSJ9zImW-5@m4~!}h?R&7g`w9DCHiAW8&b7d2M@*RAdwA$ z4IxnvflYyjfJH_&85V2Eha6>31Zv%bSTQ026;yO%09b(R;{aIbNr0hQ%KTHMRUTL& zsuk)S0v0Nwj4FX=W2U!Wh`FWzp;s1K=y=?UdhtPqUO7lKgI+}B7Q=ABT?|SddU+)+dW>is?TOXs1#OT->IT6^vfRMQ-S_x6m5z z)u4kHyv8JJUTM6bR}}N^pqCaZ#bAV9PXvhUfI0fptfA2QGlsH@g5`u2Dun~q2mqM% z1p}hDtONIN8JUP5s^v6kD6s#VSz%77hQ5mem1;MA8-l3jlJJ$sf}E&&o$8 zDYuv@FND|5V$_h=9t{5fBL=WB46E0~O|{WB(!IhLda~F z={DRPinxy9*!y5et`ZHR7Z|$f#lt^3D4suplemZxhk3;5!at}dEjsx_Mby*YRh-ZS z0C4Fl4iJDuB48G}!699ga$VbCB@y^RNJOg$jAG{FKOg+#3Wkf%KaS2tC}!CcDYY5&{>40G4rAb>d`x^1^}`7|shZiGk&u z1~nc|%~{oX*bnj&g$M=`V}2N2Rva)1l{|nBYU%>!w0v} z3l?@j%^md7AE;!5UTV;VY8uWEy30!zVgdu_8G#3Q+MVJWMzx!~GG}|yU$t(zkP(;# z5-|~*S?DnT2>{RyEXKTIKe9+WGFcHUsrbh?u4$kuw4e(+^hag{mKXb|q*i;&0-)CU z&Z;p>Bv-%!WsKMluKlAPi?oU)7NHVou!Rod_=l<15>Q8C%2fhQ=r|(kFewO7d1{Cw z4_qh_2l|5t_)=%`*a=HTQB4pdrRPG@VHOprWD+Uu5&-_;QkpiBgbUH4RSbGeEUp0_ zfpLLa*SbxSGHb1B4Gb7E=?@uj&WvvLhYe`xk7O;iqwf6ZLZUWLi(zab0{Ev@P7sqR z?sKb)V1!pkShuhi6sKUZ06TCYyvkAT4tpRD5t889CScSbbx3DkrXZ0V1jY@$s}aMd zMq1DRQZ5ESv`Y)ja2S%B^dC(~0a$7Q3Y7>z9*w=>e#4!aOyn8g?|aS~YQu+@Js z0E$_uDOjJ|s_0rJ8OtDmUNjREFc62eG2!bJs0W?kB&P?njp$wlyO1F;bOvhMCFmq# z0!wBE7#5PqSIoiziA)MC9ypg1u0o0mR3HN}nMDOift6RpgakGYas^W03kBD8S}KX{ zHgBbpAq0jxTQJ;Vo?sISV^|`f%u5_744o|8&0&3c=6kA@0412&%n?{YdKRLB2%Opf z%+u(=$yVUzH&a3a66wI3&0GptYybl}aE363;pS2lAOxzQ#V+hI1q{ps1R*GZH_S|r z5|jhb{I~}>K%fdtn8OvAXoUg@P=_agfv7Pc!_wH`0^V+O3yoWv7h)u|GgGe1ei+5 zm=_!Pnm9EGgd!pu1)@A94L_x}|-TAY5Do%;SL4Z%dI??BT6QeVD zA*x+E$NjN{r$;?6Q*TZbOfE)=%yMsSpsNeE;PS@1{0d2RSR8bqgFoPU2I2IF5dyIg zBm@R)%gzfAUf}rP<)sE^aCyEBArUC3knXUr`!?_XkGwNIFveL3)TN%BIPmql3(5TN z=xT(-t=$EE6N3PEz=Uhs0OUp;YUus3g9w{E4~AFzabnQ3yx5_sv7fvD-Bn`yz58Rk z&-}YT0xuXe$WK88Toe3*mmk zBScCSey}%wNvCn9d~hNk z_Xl$7R|X827gzvc@)3gvXmSd;5Z2=lH2?&z<_~AUgFQHV=zyX z;B|F{eJm6?ML}0YFa$;Lhku9$*td3AfOTQ$4_ja`hYP*{}6`=(?8o(1FEQs{y-fL=K?+O0%5=cW#|H$!DX7{ z21xdZYtn^vLQcdn1DP0pn^=B>Lp*u$G@!}kmxFpy2mc_E4pVq^B?CV& z2tP2ABZ)l@$4%5Uj%84I3n2$tpnmt}0|n@dbtQ=l_)|Im#E?W+jGZW3P9}EKH-{_4 zkuGor@?;$nL41WGh0JmVfpZ56=|?9L20svXJ&=Dg_>DHX5T_*roTroG=a8HTk+RhS z-Nyq)C6pbuO+%o5IYfVkGKC9K3Dq|k$*10l7cC5Tjvk7cY<|Q z1V&{e_C^_Mxe#%*2SHO;EGdF%EY@OcWEl?S<%{~(&AS(?<=gjco@ z)8_(jFlII)ldvg@3*iU})HlRud?nZhH&_N7DFjUaR*!)J1$zmaH-VhWIdO7uO*24L zNsw1v&;p~>R}$fQhY_3q@RN(E5_~uYOz{GD5P9vyi<(4XYygl^IR!^aoSoB`*%vv< zxt`I6BLAWWc))RB&;~b`EJ+|`7I}}BS%$OXpAq#3xup`*R|RoUn|Z+oZlDHHIhiww zo-v`G6dG|a;Gzk11wPQB7nPsZDVv8;12B=45rqdS+Iql3f;)sg2Kk`IiK976Z9Nwl zJ76b_=?_GrD0$%q3HNjVpqhKpY`$qAQ(%Ywz;GP7elc)blu-j-;6gWO1kILk<0%o- z=MNKFrO5LFU|_@V z@CTOqBR&EJMUbgIl87oH2B8`sF`x!~zyn4=s9i7vj(~Iv#Hdh^19DfXJpu-Sc@t6~ MsbIix{}2!WJ3Y1@TmS$7 literal 0 HcmV?d00001 diff --git a/documentation/tmpl/footer.html b/documentation/tmpl/footer.html new file mode 100644 index 00000000..6725e7cd --- /dev/null +++ b/documentation/tmpl/footer.html @@ -0,0 +1,4 @@ + + + diff --git a/documentation/tmpl/header.html b/documentation/tmpl/header.html new file mode 100644 index 00000000..b1a6a471 --- /dev/null +++ b/documentation/tmpl/header.html @@ -0,0 +1,27 @@ + + + + + $title + + + + + + + +
+
+ + + + + + +
+
MetalSVM
+ + +
A Bare-Metal Hypervisor for Non-Coherent Memory-Coupled Cores
+
+
diff --git a/documentation/tmpl/stylesheet.css b/documentation/tmpl/stylesheet.css new file mode 100644 index 00000000..125800f1 --- /dev/null +++ b/documentation/tmpl/stylesheet.css @@ -0,0 +1,814 @@ +/* The standard CSS for doxygen */ + +body, table, div, p, dl { + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; + font-size: 12px; +} + +/* @group Heading Levels */ + +h1 { + font-size: 150%; +} + +h2 { + font-size: 120%; +} + +h3 { + font-size: 100%; +} + +dt { + font-weight: bold; +} + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + padding: 2px; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #3D578C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #4665A2; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #ffffff; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code { + color: #4665A2; +} + +a.codeRef { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +.fragment { + font-family: monospace, fixed; + font-size: 105%; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; +} + +div.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 10px; + margin-right: 10px; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #A3B4D7; +} + +th.dirtab { + background: #EBEFF6; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #4A6AAA; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F9FAFC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memItemLeft, .memItemRight, .memTemplParams { + border-top: 1px solid #C4CFE5; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.memitem { + padding: 0; + margin-bottom: 10px; +} + +.memname { + white-space: nowrap; + font-weight: bold; + margin-left: 6px; +} + +.memproto { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 0px 6px 0px; + color: #253555; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 8px; + border-top-left-radius: 8px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 8px; + -moz-border-radius-topleft: 8px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 8px; + -webkit-border-top-left-radius: 8px; + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + +} + +.memdoc { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 2px 5px; + background-color: #FBFCFD; + border-top-width: 0; + /* opera specific markup */ + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 8px; + -moz-border-radius-bottomright: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7); + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 8px; + -webkit-border-bottom-right-radius: 8px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7)); +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} + +.params, .retval, .exception, .tparams { + border-spacing: 6px 2px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + + + + +/* @end */ + +/* @group Directory (tree) */ + +/* for the tree view */ + +.ftvtree { + font-family: sans-serif; + margin: 0px; +} + +/* these are for tree view when used as main index */ + +.directory { + font-size: 9pt; + font-weight: bold; + margin: 5px; +} + +.directory h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +/* +The following two styles can be used to replace the root node title +with an image of your choice. Simply uncomment the next two styles, +specify the name of your image and be sure to set 'height' to the +proper pixel height of your image. +*/ + +/* +.directory h3.swap { + height: 61px; + background-repeat: no-repeat; + background-image: url("yourimage.gif"); +} +.directory h3.swap span { + display: none; +} +*/ + +.directory > h3 { + margin-top: 0; +} + +.directory p { + margin: 0px; + white-space: nowrap; +} + +.directory div { + display: none; + margin: 0px; +} + +.directory img { + vertical-align: -30%; +} + +/* these are for tree view when not used as main index */ + +.directory-alt { + font-size: 100%; + font-weight: bold; +} + +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +.directory-alt > h3 { + margin-top: 0; +} + +.directory-alt p { + margin: 0px; + white-space: nowrap; +} + +.directory-alt div { + display: none; + margin: 0px; +} + +.directory-alt img { + vertical-align: -30%; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; +} + +address { + font-style: normal; + color: #2A3D61; +} + +table.doxtable { + border-collapse:collapse; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; +} + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image:url('tab_b.png'); + background-repeat:repeat-x; + height:30px; + line-height:30px; + color:#8AA0CC; + border:solid 1px #C2CDE4; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; +} + +.navpath li.navelem a:hover +{ + color:#6884BD; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + font-size: 8pt; + padding-left: 5px; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC; + margin: 0px; + border-bottom: 1px solid #C4CFE5; +} + +div.headertitle +{ + padding: 5px 5px 5px 10px; +} + +dl +{ + padding: 0 0 0 10px; +} + +dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug +{ + border-left:4px solid; + padding: 0 0 0 6px; +} + +dl.note +{ + border-color: #D0D000; +} + +dl.warning, dl.attention +{ + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + border-color: #00D000; +} + +dl.deprecated +{ + border-color: #505050; +} + +dl.todo +{ + border-color: #00C0E0; +} + +dl.test +{ + border-color: #3030E0; +} + +dl.bug +{ + border-color: #C08050; +} + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + font: 300% arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectbrief +{ + font: 120% arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#lfbslogo +{ + position: absolute; + top: 5px; + right: 200px; +} + +#rwthlogo +{ + position: absolute; + top: 5px; + right: 5px; +} + +#projectnumber +{ + font: 50% arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; +} + diff --git a/include/metalsvm/fs.h b/include/metalsvm/fs.h index 60a4541e..56eba752 100644 --- a/include/metalsvm/fs.h +++ b/include/metalsvm/fs.h @@ -17,6 +17,12 @@ * This file is part of MetalSVM. */ +/** + * @author Stefan Lankes + * @file include/metalsvm/fs.h + * @brief Filesystem related functions and structures + */ + #ifndef __FS_H__ #define __FS_H__ @@ -33,65 +39,162 @@ struct vfs_node; -/* These typedefs define the type of callbacks - called when read/write/open/close are called. */ +/** @defgroup fsprototypes FS access function prototypes + * + * These typedefs define the type of callbacks - called when read/write/open/close are called.\n + * They aren't as well documented as the read_fs and so on functions. Just look there for further information. + * + * @{ + */ + +/** @brief Read function pointer */ typedef ssize_t (*read_type_t) (struct vfs_node *, uint8_t*, size_t, off_t); +/** @brief Write function pointer */ typedef ssize_t (*write_type_t) (struct vfs_node *, uint8_t*, size_t, off_t); +/** @brief Open function pointer */ typedef int (*open_type_t) (struct vfs_node *); +/** @brief Close function pointer */ typedef int (*close_type_t) (struct vfs_node *); +/** @brief Read directory function pointer */ typedef struct dirent *(*readdir_type_t) (struct vfs_node *, uint32_t); +/** @brief Find directory function pointer */ typedef struct vfs_node *(*finddir_type_t) (struct vfs_node *, const char *name); +/** @brief Make directory function pointer */ typedef struct vfs_node *(*mkdir_type_t) (struct vfs_node *, const char *name); +/** @} */ + #define MAX_DATABLOCKS 12 #define MAX_DIRENTRIES 32 +/** @brief Block list structure. VFS nodes keep those in a list */ typedef struct block_list { + /// Array with pointers to data blocks void* data[MAX_DATABLOCKS]; + /// Pointer to the next block_list in the list struct block_list* next; } block_list_t; typedef struct vfs_node { - uint32_t mask; // The permissions mask. - uint32_t uid; // The owning user. - uint32_t gid; // The owning group. - uint32_t type; // Includes the node type. See #defines above. + /// The permissions mask. + uint32_t mask; + /// The owning user. + uint32_t uid; + /// The owning group. + uint32_t gid; + /// Includes the node type. See #defines above. + uint32_t type; + /// Open handler function pointer open_type_t open; + /// Close handler function pointer close_type_t close; + /// Read handler function pointer read_type_t read; + /// Write handler function pointer write_type_t write; + /// Read dir handler function pointer readdir_type_t readdir; + /// Find dir handler function pointer finddir_type_t finddir; + /// Make dir handler function pointer mkdir_type_t mkdir; + /// Lock variable to thread-protect this structure spinlock_t lock; + /// Block size size_t block_size; + /// List of blocks block_list_t block_list; } vfs_node_t; +/** @brief Directory entry structure */ typedef struct dirent{ + /// Directory name char name[MAX_FNAME]; + /// Corresponding VFS node pointer vfs_node_t* vfs_node; } dirent_t; +/** @brief Dir block structure which will keep directory entries */ typedef struct { + /// Array of directory entries dirent_t entries[MAX_DIRENTRIES]; } dir_block_t; extern vfs_node_t* fs_root; // The root of the filesystem. -/* +/** @defgroup fsfunc FS related functions + * * Standard read/write/open/close/mkdir functions. Note that these are all suffixed with * _fs to distinguish them from the read/write/open/close which deal with file descriptors, * not file nodes. + * + * @{ + */ + +/** @brief Read from file system into the buffer + * @param node Pointer to the node to read from + * @param buffer Pointer to buffer to write into + * @param size Number of bytes to read + * @param offset Offset position of the source range + * @return + * - number of bytes copied (size) + * - 0 on error */ ssize_t read_fs(vfs_node_t * node, uint8_t* buffer, size_t size, off_t offset); + +/** @brief Write into the file system from the buffer + * @param node Pointer to the node to write to + * @param buffer Pointer to buffer to read from + * @param size Number of bytes to read + * @param offset Offset position of the destination range + * @return + * - number of bytes copied (size) + * - 0 on error + */ ssize_t write_fs(vfs_node_t * node, uint8_t* buffer, size_t size, off_t offset); + +/** @brief Yet to be documented */ int open_fs(vfs_node_t * node, uint8_t read, uint8_t write); +/** @brief Yet to be documented */ int close_fs(vfs_node_t * node); + +/** @brief Get dir entry at index + * @param node VFS node to get dir entry from + * @param index Index position of desired dir entry + * @return + * - The desired dir entry + * - NULL on failure + */ struct dirent *readdir_fs(vfs_node_t * node, uint32_t index); + +/** @brief Find a directory by looking for the dir name + * @param node The node where to start the search from + * @param name The dir name string + * @return + * - a VFS node pointer + * - NULL on failure + */ vfs_node_t* finddir_fs(vfs_node_t * node, const char *name); + +/** @brief Make a new directory in a VFS node + * @param node Pointer to the node where the dir is to create in + * @param name Name of the new directory + * @return + * - new VFS node pointer + * - NULL on failure + */ vfs_node_t* mkdir_fs(vfs_node_t* node, const char* name); + +/** @brief Find a node within root file system + * @param name The node name + * @return + * - VFS node pointer + * - NULL on failure + */ vfs_node_t* findnode_fs(const char* name); +/* @} */ + int null_init(vfs_node_t* node, const char* name); int initrd_init(void); diff --git a/include/metalsvm/mailbox_types.h b/include/metalsvm/mailbox_types.h index f4d09c64..2e400562 100644 --- a/include/metalsvm/mailbox_types.h +++ b/include/metalsvm/mailbox_types.h @@ -17,6 +17,12 @@ * This file is part of MetalSVM. */ +/** + * @author Stefan Lankes + * @file include/metalsvm/mailbox_types.h + * @brief Message type structure definitions for various task return types + */ + #ifndef __MAILBOX_TYPES_H__ #define __MAILBOX_TYPES_H__ @@ -26,8 +32,13 @@ extern "C" { #endif +/** @brief Wait message structure + * + * This message struct keeps a recipient task id and the message itself */ typedef struct { + /// The task id of the task which is waiting for this message tid_t id; + /// The message payload int32_t result; } wait_msg_t; diff --git a/include/metalsvm/tasks.h b/include/metalsvm/tasks.h index 43bc7892..904f737a 100644 --- a/include/metalsvm/tasks.h +++ b/include/metalsvm/tasks.h @@ -54,7 +54,7 @@ int multitasking_init(void); /** @brief create a kernel task. * * @param id The value behind this pointer will be set to the new task's id - * @param ep Entry point for the new task + * @param ep Pointer to the entry function for the new task * @param arg Arguments the task shall start with * * @return @@ -66,14 +66,14 @@ int create_kernel_task(tid_t* id, entry_point_t ep, void* arg); /** @brief Create a user level task. * * @param id The value behind this pointer will be set to the new task's id - * @param filename Filename of the executable to start the task with + * @param fname Filename of the executable to start the task with * @param argv Pointer to arguments array * * @return * - 0 on success * - -EINVAL (-22) or -ENOMEM (-12)on failure */ -int create_user_task(tid_t* id, const char* filename, char** argv); +int create_user_task(tid_t* id, const char* fame, char** argv); /** @brief Block current task until the child task is terminated * @param result The terminated child's return value diff --git a/include/metalsvm/time.h b/include/metalsvm/time.h index b793169d..4c2cdf51 100644 --- a/include/metalsvm/time.h +++ b/include/metalsvm/time.h @@ -17,6 +17,12 @@ * This file is part of MetalSVM. */ +/** + * @author Stefan Lankes + * @file include/metalsvm/time.h + * @brief Time related functions + */ + #ifndef __TIME_H__ #define __TIME_H__ @@ -24,8 +30,23 @@ extern "C" { #endif +/** @brief Initialize Timer interrupts + * + * This procedure installs IRQ handlers for timer interrupts + */ int timer_init(void); -void timer_wait(unsigned int); + +/** @brief Blocking wait function + * + * This function does no busy-wait. + * + * @param ticks Amount of ticks to wait + */ +void timer_wait(unsigned int ticks); + +/** @brief Returns the current number of ticks. + * @return Current number of ticks + */ uint64_t get_clock_tick(void); static inline void sleep(unsigned int i) { timer_wait(i*TIMER_FREQ); } diff --git a/include/metalsvm/vma.h b/include/metalsvm/vma.h index 7f3b3953..449e81da 100644 --- a/include/metalsvm/vma.h +++ b/include/metalsvm/vma.h @@ -17,6 +17,12 @@ * This file is part of MetalSVM. */ +/** + * @author Stefan Lankes + * @file include/metalsvm/vma.h + * @brief VMA related sructure and functions + */ + #ifndef __VMA_H__ #define __VMA_H__ @@ -33,14 +39,42 @@ extern "C" { struct vma; +/** @brief VMA structure definition */ typedef struct vma { - size_t start, end; + /// Start address of the memory area + size_t start; + /// End address of the memory area + size_t end; + /// Type flags field uint32_t type; + /// Pointer of next VMA element in the list struct vma* next; + /// Pointer to previous VMA element in the list struct vma* prev; } vma_t; +/** @brief Add a new virtual memory region to the list of VMAs + * + * @param task Pointer to the task_t structure of the task + * @param start Start address of the new region + * @param end End address of the new region + * @param type Type flags the new region shall have + * + * @return + * - 0 on success + * - -EINVAL (-22) or -EINVAL (-12) on failure + */ int vma_add(struct task* task, size_t start, size_t end, uint32_t type); + +/** @brief Dump information about this task's VMAs into the terminal. + * + * This will print out Start, end and flags for each VMA in the task's list + * + * @param task The task's task_t structure + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ int vma_dump(struct task* task); #ifdef __cplusplus diff --git a/kernel/tasks.c b/kernel/tasks.c index 144026fc..3582a9f9 100644 --- a/kernel/tasks.c +++ b/kernel/tasks.c @@ -17,6 +17,15 @@ * This file is part of MetalSVM. */ +/** + * @author Stefan Lankes + * @file kernel/tasks.c + * @brief Implementations of task loading, killing, scheduling. + * + * This files contains all the implementations of different functions + * to start tasks with, wake them up, schedule them, etc. + */ + #include #include #include @@ -32,12 +41,18 @@ #include DEFINE_PER_CORE(task_t*, current_task, NULL); -static task_t task_table[MAX_TASKS] = {[0 ... MAX_TASKS-1] = {0, TASK_INVALID, ATOMIC_INIT(0), \ - SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL}}; + +/** @brief Array of task structures + * + * A task's id will be its position in this array. + */ +static task_t task_table[MAX_TASKS] = {[0 ... MAX_TASKS-1] = \ + {0, TASK_INVALID, ATOMIC_INIT(0), \ + SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL}}; static spinlock_t table_lock = SPINLOCK_INIT; -/* - * helper function for the assembly code to determine the current task +/** @brief helper function for the assembly code to determine the current task + * @return Pointer to the task_t structure of current task */ task_t* get_current_task(void) { return per_core(current_task); @@ -58,6 +73,10 @@ int multitasking_init(void) { return -ENOMEM; } +/** @brief Wakeup tasks which are waiting for a message from the current one + * + * @param result Current task's resulting return value + */ static void wakeup_blocked_tasks(int result) { wait_msg_t tmp = { per_core(current_task)->id, result }; @@ -76,6 +95,8 @@ static void wakeup_blocked_tasks(int result) spinlock_unlock_irqsave(&table_lock); } +/** @brief A procedure to be called by + * procedures which are called by exiting tasks. */ static void NORETURN do_exit(int arg) { vma_t* tmp; @@ -98,7 +119,8 @@ static void NORETURN do_exit(int arg) { drop_pgd(); // delete page directory and its page tables if (atomic_int32_read(&per_core(current_task)->user_usage)) - kprintf("Memory leak! Task %d did not release %d pages\n", per_core(current_task)->id, atomic_int32_read(&per_core(current_task)->user_usage)); + kprintf("Memory leak! Task %d did not release %d pages\n", + per_core(current_task)->id, atomic_int32_read(&per_core(current_task)->user_usage)); per_core(current_task)->status = TASK_FINISHED; reschedule(); @@ -108,6 +130,7 @@ static void NORETURN do_exit(int arg) { } } +/** @brief A procedure to be called by kernel tasks */ void NORETURN leave_kernel_task(void) { int result; @@ -115,14 +138,25 @@ void NORETURN leave_kernel_task(void) { do_exit(result); } +/** @brief To be called by the systemcall to exit tasks */ void NORETURN sys_exit(int arg) { do_exit(arg); } +/** @brief Aborting a task is like exiting it with result -1 */ void NORETURN abort(void) { do_exit(-1); } +/** @brief Create a task with a specific entry point + * + * @param id Pointer to a tid_t struct were the id shall be set + * @param ep Pointer to the function the task shall start with + * @param arg Arguments list + * @return + * - 0 on success + * - -ENOMEM (-12) or -EINVAL (-22) on failure + */ static int create_task(tid_t* id, entry_point_t ep, void* arg) { int ret = -ENOMEM; @@ -166,6 +200,7 @@ create_task_out: return ret; } + int sys_fork(void) { int ret = -ENOMEM; @@ -243,13 +278,24 @@ int create_kernel_task(tid_t* id, entry_point_t ep, void* arg) #define MAX_ARGS (PAGE_SIZE - 2*sizeof(int) - sizeof(vfs_node_t*)) +/** @brief Structure which keeps all + * relevant data for a new task to start */ typedef struct { + /// Points to the node with the executable in the file system vfs_node_t* node; + /// Argument count int argc; + /// Environment var count int envc; + /// Buffer for env and argv values char buffer[MAX_ARGS]; } load_args_t; +/** @brief Internally used function to load tasks with a load_args_t structure + * keeping all the information needed to launch. + * + * This is where the serious loading action is done. + */ static int load_task(load_args_t* largs) { uint32_t i, offset, idx; @@ -431,11 +477,22 @@ invalid: return -EINVAL; } +/** @brief This call is used to adapt create_task calls + * which want to have a start function and argument list */ static int STDCALL user_entry(void* arg) { return load_task((load_args_t*) arg); } +/** @brief Luxus-edition of create_user_task functions. Just call with an exe name + * + * @param id Pointer to the tid_t structure which shall be filles + * @param fname Executable's path and filename + * @param argv Arguments list + * @return + * - 0 on success + * - -ENOMEM (-12) or -EINVAL (-22) on failure + */ int create_user_task(tid_t* id, const char* fname, char** argv) { vfs_node_t* node; @@ -476,6 +533,7 @@ int create_user_task(tid_t* id, const char* fname, char** argv) return create_task(id, user_entry, load_args); } +/** @brief Used by the execve-Systemcall */ int sys_execve(const char* fname, char** argv, char** env) { vfs_node_t* node; @@ -552,6 +610,8 @@ int sys_execve(const char* fname, char** argv, char** env) return ret; } +/** @brief Called by tasks which are waiting for another task's + * return value. */ tid_t wait(int32_t* result) { wait_msg_t tmp = { -1, -1}; @@ -571,6 +631,12 @@ tid_t wait(int32_t* result) return tmp.id; } +/** @brief Wakeup a blocked task + * @param id The task's tid_t structure + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ int wakeup_task(tid_t id) { int ret = -EINVAL; @@ -590,6 +656,12 @@ int wakeup_task(tid_t id) return ret; } +/** @brief Block a running or ready task. + * @param id The task's tid_t structure + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ int block_task(tid_t id) { int ret = -EINVAL; @@ -606,6 +678,10 @@ int block_task(tid_t id) return ret; } +/** @brief _The_ scheduler procedure + * + * Manages scheduling - right now this is just a round robin scheduler. + */ void scheduler(void) { unsigned int i;